2) Fabriquer un sextant électronique avec arduino (compas de relèvement, gitomètre, et horloge en option).

Très intéressant pour les hackers spécialistes en bidouillette bricoling, arduino propose à la vente des microcontrôleurs programmables en langage proche C, C++ , agrémenté d’une communauté internet riche fonctionnant en open source, bref c’est un peu la caverne d’Ali Baba on y trouve de tout, n’importe quoi, tout avec n’importe quoi et n’importe quoi dans un peu de tout… Ma copine m’a offert le kit débutant pour Noel, et elle s’en mord déjà les doigts (arduino=aspirateur de vie, de temps et d’interactions sociales, voire même d’hygiène corporelle dans les cas les plus poussés mais passons…) ! N’essayez jamais c’est un piège cette connerie en fait. La période de confinement m’a permit d’avoir le temps de m’y consacrer un peu tout en gardant en même temps une certaine dignité on va dire….

  1. Arduinono

La bébète , ici le modèle « basique » Uno (quand on a tout branché il faut crier UNO !!! Sinon le programme marche pas…Ha ha pardon j’ai honte):

Arduino permet de mettre en place des circuits électroniques dans des domaines comme la domotique, la robotique, l’informatique…On peut par exemple créer un système d’arrosage automatique de plantes de jardin, ou encore des détecteurs de mouvements, ou même des drones ! Un gars a même fabriqué une raquette de tennis qui crie à la place du joueur…(il y a plein d’exemples sur youtube) ou encore un trieur de M&M’s. Le cerveau de ces cartes est un microcontrôleur atmega; les autres composants (quartz, prise jack, interface USB…) sont annexes. Bref c’est très utile pour de la collecte d’infos, du traitement et aussi réaliser une action automatisée en retour selon les données enregistrées ou captées.

Une carte Arduino traite des données analogiques et numériques (convertisseur CAN et CNA intégré), pour ensuite les traiter puis fournir en sortie des données analogiques, ou numériques exploitables. Le programme suit toujours grosso-modo la même trame « grammaticale »:

Etape 1: on déclare les bibliothèques importées (si besoin) pour le préprocesseur, les constantes et les types de variables globales (exploitées tout le long du programme) en jeu.

Etape 2: On rédige la boucle « setup ». Ces instructions ne seront exécutées qu’une seule fois en début de programme. En général dans cette boucle on déclare les installations de base comme l’utilisation des bornes (en entrée ou en sortie) ou l’initialisation de certains composants (moniteur série, écran lcd…).

Etape 3: On s’occupe de la boucle « loop »: ses instructions seront répétées « en boucle » jusqu’à l’arrêt ou le reset de la carte. Les boucles « setup » et « loop » sont un peu l’équivalent de la boucle « main » pour le C et le C++; sans elles pas de programme qui fonctionne.

Etape 4: après la boucle « loop »on rédige au besoin les fonctions que l’on a crée et qui seront indispensables au bon fonctionnement du programme. Il suffira alors de les « appeler » dans la boucle loop ou le setup si besoin.

Les variables écrites en dehors des boucles setup et loop sont considérées comme globales (on peut les appeler à tout moment à n’importe quel endroit du programme), les autres locales (ne marchent que dans leur boucle, pas ailleurs).

Ces microcontôleurs de type AVR (donc architecture Harvard : contrairement à une ti par exemple les variables et le code des instructions à suivre ne sont pas stockées au même endroit) sont aussi caractérisés par 3 mémoires de travail:

  • Une mémoire flash, celle qui va intégrer le programme écrit et compilé dans la carte utilisée. Cette mémoire est modifiée à chaque compilation d’un nouveau code dans la carte. En gros elle contient les instructions.
  • Une mémoire dynamique SRAM qui prend en charge les variables. Elle s’efface et repart à zéro à chaque reset ou arrêt de la carte.
  • Une mémoire EEPROM appelée également mémoire morte, où les données sont conservées même après arrêt et mise en route de la carte. Certains modèles n’ont pas ce type de mémoire mais nous en reparlerons plus tard.

Selon le microcontrôleur utilisé, on aura donc une capacité de travail différente (on y reviendra plus tard…) et le microcontrôleur sera plus ou moins puissant mais nous en reparlerons :

  • Arduino nano : mémoire flash de 32Ko, 2Ko de mémoire SRAM et 1Ko de mémoire eeprom.
  • Arduino uno R3 : pareil que nano mais en petit, plus intéressant pour les systèmes embarqués du coup.
  • Arduino mega2560 : on tape un peu plus haut avec 256 Ko de mémoire flash pour 8 Ko de mémoire SRAM et 4 Ko de mémoire EEPROM. Plus gros et imposant par contre.

De plus selon les différentes cartes des options apparaissent ou disparaissent comme le nombres de bornes analogiques, la fréquence de calcul (8 ou 16 MhZ si il y a un oscillateur à quartz intégré ou pas ce qui augmente la vitesse de calcul tout comme la consommation d’énergie) par exemple… Donc selon le type de projet que l’on veut faire il faut choisir le bon microcontrôleur, on fait un petit cahier des charges quoi ! Un peu comme prévoir le jambon dans la purée si on aime le jambon dans la purée, ou le PQ, la râpe à fromage et les kinders au camping….Si on aime les kinders au camping évidemment.

2. Notre centrale de navigation, projet d’origine.

La centrale de nav que je voulais faire au départ était sensée indiquer l’angle de gite et de tangage du bateau, le cap compas, la température extérieure et la pression atmosphérique. Pour tout ça on va avoir besoin de différents composants…

A. Gite et tangage : Utiliser un accéléromètre ADXL335

Débutant dans le petit monde de l’arduino, je remarque en écumant les tutos youtube et autre qu’il est possible de fabriquer un inclinomètre grâce à ce petit capteur capable de détecter les accélérations dans les 3 directions de l’espace; du coup je me dis inclinomètre=gitomètre ! Mais peu doué pour exploiter les résultats obtenus par les différents tutos, je décide à partir du datasheet et du pdf de l’IUT de Toulon( Arduino/m2206_dossiertechnique_inclinometre.pdf) de programmer moi même, si possible, ce petit truc.

En furetant sur le net, on apprend que l’adxl335 fonctionne un peu comme un condensateur variable. A l’intérieur on trouve une partie fixe et une partie mobile, et en bougeant la partie mobile fait varier la capacité de l’accéléromètre de façon subtile, permettant de calculer un angle ou une force appliquée au composant. Un truc de fou.

Matos en jeu en plus: 1 carte arduino uno, 1 potentiomètre, 1 ecran lcd.

Branchements

Pour l’écran lcd, la borne RS est branchée sur la borne 8 de l’arduino, la borne E sur la 9, et les bornes D4, D5, D6 et D7 respectivement sur les bornes digitales 4, 5, 6 et 7 de l’arduino. La borne V0 est branchée sur le potentiomètre (c’est la résolution de l’écran lcd), la borne led- est reliée à la masse et la borne led+ est reliée à l’alimentation 5V via une résistance de 220 ohms (led- et led+ gèrent le rétroéclairage de l’écran). La borne Vdd est reliée à l’alimentation 5V et la borne Vss à la masse. Le reste des bornes de l’écran lcd sont reliées à la masse.

Pour l’accéléromètre, Vcc est à relier à l’alimentation 3,3 V de l’arduino, GND est à relier à la masse, et les bornes X, Y et Z sont respectivement à relier aux bornes analogiques A2, A1 et A0 de l’arduino.

Le code de l’inclinomètre simple:

#include<LiquidCrystal.h>
LiquidCrystal lcd(8,9,4,5,6,7);

const int axe_z=A0;
const int axe_y=A1;
const int axe_x=A2;

int x=0;
int y=0;
int z=0;

const float tension_zero=1.66;
const float sensibilite=0.3;



void setup() {
  lcd.begin(16,2);
  
   

}

void loop() {
  x=analogRead(axe_x);
  y=analogRead(axe_y);
  z=analogRead(axe_z);

  float tension_x=(x/1024.0)*5.0;
  float tension_y=(y/1024.0)*5.0;
  float tension_z=(z/1024.0)*5.0;

  float Gx=(tension_x-tension_zero)/sensibilite;
  float Gy=(tension_y-tension_zero)/sensibilite;
  float Gz=(tension_z-tension_zero)/sensibilite;

  float carre_Gx=sq(Gx);
  float carre_Gy=sq(Gy);
  float carre_Gz=sq(Gz);

  float norme_G=sqrt(carre_Gx+carre_Gy+carre_Gz);

  float roulis=degrees(asin(Gx/norme_G));
  float tangage=degrees(asin(Gy/norme_G));
  float verticalite=degrees(acos(Gz/norme_G));

  lcd.print("r:");
  lcd.setCursor(2,0);
  lcd.print(roulis);
  lcd.setCursor(0,1);
  lcd.print("t:");
  lcd.setCursor(2,1);
  lcd.print(tangage);
  lcd.setCursor(9,0);
  lcd.print("v:");
  lcd.setCursor(11,0);
  lcd.print(verticalite);
  
  
  

  delay(500);



}

Calibrage

Afin d’avoir une précision optimale, il est nécessaire de positionner l’accéléromètre bien parallèle à un plan bien droit, puis utiliser des règles à niveau. Il va falloir alors via la fonction Serial du moniteur série capter la tension de sortie quand un axe de l’accéléromètre est bien parallèle à celui de la force de gravité. On note ensuite la tension correspondante. Cette tension est la constante « tension_zero » dans le code tapé au-dessus (1,66 Volts dans mon cas). La sensibilité (300 mV) est fournie dans le datasheet du composant.

Avantages/limites

L’avantage numéro 1 de l’accéléromètre pour mesurer un angle, c’est sa simplicité de mise en place autant en branchements qu’en programmation; un brancos comme moi a pu le faire en (relativement…) peu de temps. L’inconvénient majeur, c’est qu’il est soumis aux forces qu’il rencontre et pas seulement celle de gravité, donc si il a un choc, ou si le bateau accélère ou ralentit, les données fournies sont faussées…Il reste intéressant pour faire un inclinomètre pour le bricolage à la maison par exemple, mais quand il est embarqué sur une personne ou un objet qui se déplace, c’est pas top (à moins de se déplacer en vitesse constante et tout droit tout le temps, mais bon…).

B. Gite et tangage: Utiliser un accéléromètre/gyrocompas mpu6050

Il combine l’accéléromètre et le gyrocompas (et la température aussi je crois mais ça j’ai pas creusé plus). Ce composé est très utilisé pour les drones car il permet de calculer l’assiette de l’engin en vol (qui subit des accélérations/ralentissements…), donc contrôler sa trajectoire. Du coup très intéressant pour notre projet…On en trouve aussi dans les portables pour gérer les changements d’écran (quand on tourne le portable, l’écran tourne également).

Le gyrocompas indique la vitesse angulaire de rotation autour des axes X, Y et Z. Qui dit vitesse dit dérivée de la position par rapport au temps, donc en intégrant ces vitesses nous pourront déterminer la position angulaire (à une constante près, c’est à dire la position initiale ou précédente à l’intégration pour être plus précis). Le petit problème c’est qu’à chaque intégration des données il y a une petite perte de précision qui se cumule à plus ou moins long terme selon la puissance de calcul disponible. Et à la fin le gyrocompas part en vrille totale, on appelle ça la dérive: c’est un peu quand on laisse tonton Roger parler à table complètement bourré après l’apéro, on part d’un sujet quelconque mais noble comme la philosophie existentialiste et cinq minutes après on dérive sur la levrette chez les esturgeons du Danemark. Pour corriger ça on mixe les données du gyrocompas avec celles de l’accéléromètre. En tout cas pour nous ça marche, pour tonton Roger un peu moins.

Branchements

Pour faire fonctionner et programmer ce composant, il y a cette page web qui est très bien :

http://gilles.thebault.free.fr/spip.php?article32

Le code

Afin d’avoir l’angle de gite et l’angle de tangage du bateau j’ai donc utilisé deux gitomètres (un seul pourrait suffire mais pour le moment je ne suis pas assez bon pour programmer ça malgré quelques tentatives, mais on verra ça ensuite); pour cela j’ai utilisé le dernier code publié de la page web précédente. Attention il y a une petite coquille dans ce code aux lignes 11 et 12: pour les noms des variables il faut rajouter 1 à la fin (ça donne ax1,ay1,az1 et gx1,gy1,gz1) sinon le programme ne pourra pas calculer l’angle1 à la ligne 42 car il ne reconnaîtra pas les paramètres utilisés.

Après branchement et code légèrement modifié l’ensemble fonctionne très bien pour notre projet.

C.Pression/température: Le BME280

Le BME28O est un capteur capable de nous fournir la pression atmosphérique (c’est pourquoi on peut s’en servir pour bricoler un altimètre), la température et l’hygrométrie. Ce petit composant peut aussi permettre de fabriquer une petite station météo du coup (et merde encore une autre idée de projet, on s’en sort pas…).

Pour le code, les branchements et le reste c’est pareil j’ai utilisé tout ce que j’ai trouvé sur cette page web très bien expliquée:

http://gilles.thebault.free.fr/spip.php?article47

D. Le cap: le GY-271

Ce composant va nous indiquer le cap. Et pi voilà. Pareil cette page :

http://gilles.thebault.free.fr/spip.php?article58

explique très bien comment coder notre compas. En fait c’est toujours sur le même site, très bien fait et que j’ai outrageusement pillé comme un gros crevard.

E. Le bus I2C.

Comme on va utiliser plusieurs composants en même temps, on va utiliser le système de communication I2C entre eux et le microcontrôleur. L’avantage majeur de ce procédé c’est l’économie de branchements et de fils qui partent dans tout les sens; les composants seront tous branchés sur 4 fils communs: SDA où circule l’info, SCL qui indique la fréquence d’horloge pour que « l’orchestre » joue au même rythme, la masse et l’alimentation.

Premier bilan

Alors premiers trucs qui cassent un peu la boîte à bigorneaux je dois dire :

  • Le module BME280 fonctionne sur du 3,3 volts alors que le reste peut marcher sur du 5 volts au regard des datasheet, du coup il semblerait (je crois, mais pas sûr que ce soit ça) qu’en I2C ça marche pas bien (on dirait que les modules doivent avoir la même tension d’alimentation). Par contre en usage seul il marche très bien.
  • Le compas GY271 fonctionne pas bien…Il indique des caps comme si j’étais en forêt de Blair Witch c’est dire…Mon module est à priori un QMCL5883L, sauf que sur le composant je devrais voir DA 5883 alors que là j’ai DB 5883 marqué dessus …Je n’utilise peut-être pas la bonne bibliothèque. A moins que la raison de ce plantage soie ailleurs. Cependant il a l’air d’indiquer correctement le nord, c’est quand je m’en éloigne que ça se gâte mais bon y a pas que le Nord dans la vie.
  • J’avais prévu de faire tourner une arduino nano pour tout ça, sauf que mon modèle est une copie (mêmes capacités toutefois, c’est un modèle elegoo). Pour la faire reconnaître par l’IDE arduino, il faut télécharger le pilote CH340 et pour téléverser le code il faut sélectionner arduino nano et processeur atmega (old bootloader). En tout cas dans mon cas après ça a marché. C’est une carte elegoo. Le « bootloader » est une sorte de petit programme pré-installé dans la mémoire flash de la carte et qui facilite le compilage de notre code dans le microcontrôleur.

Du coup avec le labo, l’équipe, mon chat et le paquet de chips à côté de moi, brainstorming et nouveau projet :

Je me suis dit bon, le BME280 je me le garde pour plus tard histoire de faire un baromètre station météo, et je garde le compas (en espérer le faire marcher), les gyrocompas et faire un compas de relèvement/gitomètre. Et en potassant un peu j’ai vu qu’il existait des horloges externes arduino…En plus le gyro à l’air de me donne un angle assez précis avec peu de « bruit »….Donc si j’ai un angle assez précis et une trace du temps réel même quand l’arduino est éteint, je peut donc peut-être fabriquer un compas de relèvement/gitomètre/sextant « numérique » pour pas cher et en se creusant un peu l’éponge à idées.

3) Le sextant gitomètre compas chipolata

Pour ce projet, on va utiliser une carte arduino mega 2560, un seul gitomètre, un compas GY271, un ecran lcd avec module I2C, une horloge externe temps réel DS3231 (pratique car la pile est amovible!). Pour fabriquer le viseur je l’ai dégoté sur une vieille lunette astronomique, et les interrupteurs, les résistances et les diodes proviennent d’un vieux poste radio du fin fond de mon garage. On va ajouter aussi un clavier souple arduino à 16 touches.

a) un gitomètre pour obtenir la gite et le tangage

Pour notre projet nous allons devoir utiliser un seul gitomètre; pourquoi ? He bien lors de l’utilisation du système de communication en I2C, chaque composant relié a une adresse qui lui est propre. l’écran lcd utilisé peut en avoir plusieurs (ça dépend du type de circuit I2C employé avec l’écran et aussi son réglage avec les bornes cavalier), dans notre cas il est configuré avec l’adresse 0x27, le gitomètre ne peut en avoir que 2: 0x68 ou 0x69. Pour l’horloge externe, c’est aussi 0x68 et il n’y en a pas d’autre…

Du coup il va falloir configurer le gitomètre avec l’adresse 0x69 pour ne pas perturber les données de l’horloge fournies. Il existe un programme très utile appelé I2CSCAN facilement trouvable sur le net, capable de donner directement via le moniteur série les adresses des composants branchés ensembles en I2C.

Ne pas oublier: quand on change l’adresse du gitomètre de 0x68 à 0x69, on doit également brancher la borne ADO du composant sur l’alimentation. On change le branchement et le code également. Pour notre gitomètre voici le code de la page web http://gilles.thebault.free.fr/spip.php?article32 modifié pour également faire gite et tangage en même temps ; il y a 2 modifs à faire toutes simples au final. Quand on regarde le code pour un seul gyrocompas :

  • Ligne 8, ça donne: « MPU6050 accelgyro(0x69) »
  • la ligne 35, on change avec: »

Gite = 0.98 * (Gite + float(gy) * 0.01 / 131) + 0.02 * atan2((double)ax, (double)az) * 180 / PI;
Tangage = 0.98 * (Tangage + float(gx) * 0.01 / 131) + 0.02 * atan2((double)ay, (double)az) * 180 / PI; »

On change la variable angle par 2 variables gite et tangage, qu’il faudra déclarer en début de programme comme variables globales.

b) L’horloge externe

Pour la partie sextant il nous faut ce composant. Les formules de calcul de Jean Meeus sont essentiellement basées sur des fonctions travaillant avec la variable temps. Or lorsque la carte est hors tension, la trace de l’écoulement du temps est perdue. Il y a bien les fonctions millis() et micros() qui activent une horloge interne de la carte (timer), mais seulement quand elle est alimentée; de plus le stockage numérique de ce décompte est limité. L’horloge DS3231 garde une trace du temps grâce à une pile amovible, même quand notre circuit est ouvert.

L’horloge doit tout d’abord être configurée pour régler le temps, ensuite elle pourra être insérée dans notre programme et notre montage. Pour ce faire j’ai honteusement pillé, sans vergogne, le code présent dans l’ouvrage « arduino: apprivoisez l’electronique et le codage ». Sur le site de gilles thebault (utilisé précédemment pour le mpu6050, et le compas) il y a aussi un programme fournit pour la faire fonctionner et la régler à la bonne heure, mais bon je vais pas tout le temps le dépouiller non plus!

c) Le clavier.

Pour l’exploiter on va utiliser la bibliothèque keypad. Il y a pas mal de tutos sur youtube ou le net qui expliquent son fonctionnement, un peu comme une matrice de contacts.

d) Le compas.

On veillera dans le boitier à ne pas le mettre trop près d’un élément perturbant au niveau électromagnétique pour ne pas faire trop dévier les données. J’ai utilisé d’autres compas cette fois avec le bon numéro sur le composant et là, ça marche !

e) Le big problème

A cette étape du projet tout les éléments fonctionnent ensemble via le bus I2C, la nomencalture du programme marche pas mal, et là c’est le drame : la carte arduino mega2560 comme la uno et la nano d’ailleurs, carburent avec des microcontrôleur avec 8Kb de SRAM (mémoire dynamique, celle des variables) ce qui limite la taille des variables manipulables par notre programme et la puissance de traitement.

Du coup voilà le pitch: on peut soit manipuler des décimaux (variable de type « float », tient sur 32 bits) avec 7 chiffres significatifs maximum (entier inclus, formant la mantisse en tout), ou alors des entiers positifs (variable de type « unsigned long ») où notre variable ne doit pas dépasser le nombre 4294967295. Pour notre plus grand malheur, sur ces cartes la variable « double » est équivalente à la « float » (« double » c’est aussi pour stocker un décimal mais avec 14 chiffres sur la mantisse. Pour la mantisse, voir wikipépé, la page sur la virgule flottante. La variable double tient sur 64 bits). Avec les équations de Jean Meeus ça va être un peu chiant vu qu’elles mobilisent des nombres avec pas mal de chiffres…Va falloir ruser pour garder un minimum de précision requise.

J’ai bien essayé de faire des variables « long long » ou « unsigned long long » ni vu ni connu je te fume pour stocker mes variables sur 64 bits, mais à chaque fois le compilateur tousse, me fait les gros yeux et me dit d’aller me faire vigoureusement entreprendre par une tribu de gorilles. Un peu comme un douanier impitoyable, quand je déclare pas bien mes variables il me recale…Il y a peut-être un moyen pour le faire mais je l’ignore, grand couillon que je suis. Va falloir trouver autre chose.

Partie 2: projet final

Bref, après toutes ces déconvenues et billevesées désapointantes passons aux choses sérieuses:

1) Matos

1 buzzer, un écran lcd, un module I2C, un mpu6050 gyrocompas, un GY271 compas magnétique, une horloge externe ds3231, une carte nano elegoo et une carte arduino due, une pile 9V, 1 diode, 1 interrupteur,un convertisseur logique 3,3V/5V, une breadboard, un viseur de lunette astro de récup, le filtre soleil qui va avec, du bois pour le boitier, nouilles chinoises et tablettes de chocolat lindt 85% à foison (pour l’équilibre mental de l’équipe).

Le buzzer va nous servir comme aide à la visée de l’astre (en-dessous de 2 degrés de gite, il couine), et ici on va utiliser une carte arduino due car elle est capable de manipuler des vraies variables double (comme la arduino zero) grâce à son processeur plus pointu (SAM3X8E ARM CortexM3, processeur 32 bits).

Pourquoi utiliser 2 cartes alors que la arduino due aurait suffit ? He bien en fait sur le net il y a peu de bibliothèques dispo pour cette carte car elle a un usage moins « grand public ». Du coup elle est plutôt réservée à des programmeurs expérimentés capables de rédiger du bas niveau proche du bit et des registres, voire capable de rédiger leurs propres bibliothèques…Je décide donc de contourner mes compétences limitées en programmation en utilisant la carte elegoo comme les « yeux » du projet (elle va capter les infos) et la arduino due comme le cerveau. Elle vont communiquer ensemble pour bosser, un peu comme Minus et Cortex, Arnold et Willy, Stone et Charden ou Jackie et Michel …Je dégage toute responsabilité dans le choix de votre duo préféré.

A. Problèmes principaux rencontrés

a) Serial.write ou Serial.print ?

Je croyais que les 2 c’était la même chose…Et bien non. Serial.print renvoie la valeur directement tandis que Serial.write envoie la valeur sous sa forme binaire.

b) Stocker les nombres saisis avec le clavier…J’en rêve encore la nuit

C’est bien expliqué sur le forum arduino, il faut rédiger un compteur et stocker l’ensemble dans un tableau de char via l’usage d’une boucle for, puis convertir la chaîne de caractères en variable (dans notre cas des entiers).

c) Convertir des boutons switch en boutons « permanents »…J’en pisse encore au lit la nuit.

il faut créer une variable intermédiaire puis l’évaluer avec des conditions, si elle remplit la ou les conditions on la mémorise dans une autre variable qui déclenchera le menu (dans notre cas prédatastate, datastate).

d) Faire communiquer 2 cartes différentes par la voie série…Pour envoyer et recevoir des données entre elles…J’en hurle d’effroi encore la nuit

A priori d’après les trucs vus sur le net on peut faire alternativement envoyer et recevoir des données entre cartes via une voie série…J’ai longtemps essayé avant de changer de technique; au début la nano envoyait bien les bonnes données à la due via une variable structure, la due faisait son taf et le renvoyait à la nano mais par contre la nano n’arrivait pas à recevoir correctement l’info. Comme j’utilisai juste une seule voie pour ces deux chemins de données, je me suis dit que ça devait être un problème de buffer pas vidé qui perturbe le signal (elle est pas mal cette excuse, hein ? ça sonne bien ça fait le gars qui gère) ou alors des variables envoyées qui devaient être réinitialisées mais même en vidant avec attention les buffers et en réinitialisant, ça marchait toujours pas.

Du coup j’ai configuré une seconde voie série sur la nano grâce à la bibliothèque softwareserial, j’ai maintenant les broches 11 et 12 qui me permettent de communiquer avec la due. Cette seconde voie enverra les résultats de la due à la nano. ça marche, mais ceux qui lorgneront mon code concernant la nano remarqueront mon « coup de triche inélégant » à la fin du programme…En effet au lieu de recevoir la variable azimut et intercept, mon bricolage m’envoie 3 valeurs: l’intercept, « -1 », et l’azimut. en supprimant le « delay » quand -1 s’affiche, ça passe!

e) Conversion des variables pour les fonctions trigonométriques

Il faut bien faire attention avec la bibliothèque maths, certaines variables doivent être converties en radians, d’autres en degrés sinon c’est le bordel.

B.Schéma du montage (fritzing).

Fritzing est une application et aussi un site qui permet de visualiser et faire visualiser ses montages électroniques, ses circuits… On peut également faire virtuellement sur leur plateforme un circuit puis leur passer en commande pour faire le circuit imprimé ce qui n’est pas inintéressant. Voilà le montage en espérant ne pas avoir fait de fautes d’étourderies:

Comme le schéma de montage comporte pas mal de branchements, et n’étant pas à l’abris d’avoir semé des coquilles, voici les points importants:

Number 1: cette ligne + et – est alimentée par la nano en 5 volts, bref la nano alimente tout les composants fonctionnant entre eux en I2C (écran, gyrocompas, compas magnétique, horloge).

Number 2: cette seconde ligne + et – est alimentée par la pile 9 volts; elle fournit le jus pour nos 2 cartes arduino.

Number 3: c’est le convertisseur logique 3,3 volts/5 volts. toutes les connexions de la arduino due doivent passer par lui sinon vous risquez de la cramer (elle n’accepte qu’une tension de 3,3 volts, pas plus) sauf l’alimentation via Vin. Pour chaque liaison série, il ne faut pas oublier d’inverser les Tx et Rx: la borne d’envoi d’une carte est la borne de réception de l’autre et inversement. Sinon la communication marche pas. Je précise car sur mon schéma il y a peut-être des coquilles à ce niveau là.

Number 4: pour le circuit d’alimentation des deux cartes j’ai ajouté une diode pour les protéger; comme ça si jamais la pile 9 volts est branchée à l’envers sur son support, elle ne grille pas la nano et la due, ce qui serait somme toute un peu balo…

C.La bête, et son fonctionnement.

Vous excuserez le design très « URSS vintage » mais le budget était limité hein…

Mode d’emploi:

  • Le menu est géré par les boutons A,B,C et D du clavier. A l’allumage, l’engin (on l’appellera Nono) bref Nono affichera toujours l’heure UTC. A active le gitomètre qui indique la gite et le tangage, B le compas de relèvement, C la droite de hauteur avec le Soleil et enfin D la droite de hauteur avec la Lune.
  • Pour les droites de hauteur, si le nombre à rentrer pour le point estimé est négatif (latitude sud par exemple), on écrit au clavier le nombre puis on appuie sur la touche « * ». Si le nombre est positif, pour enregistrer le nombre on appuie sur la touche « # ». Si un nombre est négatif, attention d’enregistrer également comme négatif les minutes.
  • Quand on veut fixer le soleil, dans mon viseur il y a un filtre astro pour ne pas se brûler la rétine. On vise l’astre. Si le buzzer sonne ça veut dire que l’assiette de Nono est pas mal (moins de 2 degrés). Ensuite, attendre 1 minute au moins avant d’appuyer le temps que le gyro se stabilise, et paf on appuie sur n’importe quel bouton du clavier (excepté ceux du menu) pour saisir l’astre et mesurer notre position.
  • Nono donne ensuite d’abord l’intercept en miles nautiques et puis l’azimut en degrés.

Attention! : avant usage ne pas oublier de calibrer le gyrocompas. Quand on fixe ou colle le gyro au fond de Nono (hum) il y a une perte de précision d’angle du coup comme pour l’inclinomètre simple on va se servir d’une règle à niveau et du bouton A (gite et tangage) pour identifier la correction de la variable « Tangage », très importante quand à la précision de la visée de l’astre (dans notre programme il s’agit de TangageInit). On appuie sur le bouton A, puis on met la règle à niveau posée sur le dessus de l’appareil parallèle au viseur; quand la bulle du niveau est au milieu on note l’angle de tangage affiché par Nono, puis on le rentre dans le programme « nono » comme valeur initiale de la variable tangageInit (en inversant le signe).

Attention2 ! : le boitier que vous voyez est provisoire; les 2 câbles que j’ai laissé dépasser permettent de continuer à modifier ou régler le programme sans avoir à tout démonter à chaque fois. Bref il n’est pas étanche….

D. Les codes (nano et due).

La carte « maitre » (arduino nano):

/*bibliothèques nécessaires au programme*/


#include <SoftwareSerial.h>//permet d'émuler une seconde voie série.

SoftwareSerial retourSerial(11,12);// Rx=11 et Tx=12

#include <MPU6050.h>// gyrocompas.

#include <LiquidCrystal_I2C.h>//ecran lcd en i2C.

#include <Keypad.h>//le clavier.

#include <Wire.h>//le bus I2C.

#include <MechaQMC5883.h>//le compas GY271(modèle QMC).

#include <math.h>//fonctions maths en supplément (trigo notamment).

/*variables globales compas */

MechaQMC5883 capteur;

     int mx, my, mz; // déclaration des variables sur les axes x, y, z
     float angle;



/*variables globales clavier (keypad)*/


const byte LIGNES = 4;
const byte COLONNES = 4;

char keys [LIGNES][COLONNES] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'#', '0', '*', 'D'}
};
byte ligPins[LIGNES] = {5, 4, 3, 2};
byte colPins[COLONNES] = {9 , 8, 7, 6};

Keypad keypad = Keypad( makeKeymap(keys), ligPins, colPins, LIGNES, COLONNES );


      char Clavier[4];// variables globales utilisées pour stocker les données du clavier (point estimé, hauteur oeil).
      int compteur = 0;


/*variables pour le menu des options A,B,C et D (change également les switchs du clavier en interrupteurs "classiques")*/

      char preDataState=0;// permet de gérer le menu gitomètre, compas de relèvement, heure utc et droites de hauteur.

      char DataState=0;// variable de transfert pour grossomerdo changer les switchs du keypad en interrupteurs "permanents".



/*variables globales ecran (liquidcrystal lcd)*/

LiquidCrystal_I2C lcd(0x27,16,2); //ecran lcd (adresse I2C,lignes, colonnes)

/*variables globales gyrocompas (mpu6050)*/


MPU6050 accelgyro(0x69); // changement d'adresse du gyrocompas de 0x68 (par défaut) à 0x69.

int16_t ax, ay, az;
int16_t gx, gy, gz;
uint8_t Accel_range;
uint8_t Gyro_range;
      float Gite = 0;
      float Tangage = 0;
      float GiteInit=-0.93;//permet d'étalonner le gyrocompas en gite.
      float TangageInit=-2.12;//permet d'étalonner le gyrocompas en Tangage (particulièrement important pour la précision du sextant).

/*variables horloge externe (ds3231)*/

#define ADRESSE_I2C_RTC 0x68 //adresse de l'horloge.
byte seconde,minute,heure,numJourSemaine,numJourMois,mois,annee;
String jourDeLaSemaine[8]={"","Dim","Lun","Mar","Mer","Jeu","Ven","Sam"};

/*variable buzzer*/

       const int BUZZER=10;

/*variables de calcul*/

       int LatDeg=333;//  variables du point estimé, initialisées à 333 (arbitraire) pour pouvoir rentrer le 0 comme valeur dans les calculs.
       int LatMin=333;
       int LonDeg=333;
       int LonMin=333;
       byte hauteurOeil=250;
       float estimeLat=0;
       float estimeLon=0;
       int Intercept=0;
       int Azimut=0;

       struct retour {
        int Intercept;
        int Azimut;
       };

       retour point;
       
  
    

void setup() {
  keypad.setDebounceTime(10);// cette instruction évite les problèmes de rebonds avec le clavier.
  Serial.begin(57600);//ouvre la première liaison série pour communiquer entre les 2 cartes.
  retourSerial.begin(57600);//ouvre la seconde liaison série.
  
  capteur.init();
  //capteur.setMode(Mode_Continuous,ODR_200Hz,RNG_2G,OSR_256);pour le compas magnétique.

  Wire.begin();// initialise la liaison I2C.

  
  

  /* setup ecran*/

  lcd.init();
  lcd.backlight(); 
  

  lcd.home();
  lcd.setCursor(3,0);
  lcd.print("navigogol");
  lcd.setCursor(0,1);
  lcd.print("on est ou ?");
  delay(3000);
  lcd.clear();
  
  

  /* setup gyrocompas */

  
  accelgyro.initialize();

  lcd.home();
  lcd.print("Test gyro ...");
  lcd.setCursor(0,1);
  lcd.print(accelgyro.testConnection() ? "gyro ok" : "gyro pas ok");
  delay(1000);
  lcd.clear();
  

  
  
  /*configuration du Buzzer */

  pinMode(BUZZER,OUTPUT);
  
  
  
}

void loop() {
  
   /*variables globales utilisées dans le loop*/

  
   char key = keypad.getKey();//on stocke la touche pressée dans la variable key.
   
   

  /*mise en route des composants*/

    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    Gite = 0.98 * (Gite + float(gy) * 0.01 / 131) + 0.02 * atan2((double)ax, (double)az) * 180 / PI;
    Tangage = 0.98 * (Tangage + float(gx) * 0.01 / 131) + 0.02 * atan2((double)ay, (double)az) * 180 / PI;

    

    Wire.beginTransmission(ADRESSE_I2C_RTC);//reception heure et date
    Wire.write(0);//positionne le pointeur de registre sur 00h
    Wire.endTransmission();
    Wire.requestFrom(ADRESSE_I2C_RTC,7);//accède aux données de l'horloge à partir du registre 00h
    seconde=bcdToDec(Wire.read()& 0x7f);
    minute=bcdToDec(Wire.read());
    heure=bcdToDec(Wire.read()&0x3f);
    numJourSemaine=bcdToDec(Wire.read());
    numJourMois=bcdToDec(Wire.read());
    mois=bcdToDec(Wire.read());
    annee=bcdToDec(Wire.read());

    capteur.read(&mx, &my, &mz);
    angle = degrees(atan2((double)mx, (double)my));

     if (angle>=0) {
       angle=360-angle;
  }
     else {
       angle=abs(angle);
  }

  /*codage du menu*/

  DataState=key;//on stocke la touche dans DataState.Permet de garder ensuite avec predatastate en mémoire la touche pressée après la loop.
    
    if (DataState=='A'||DataState=='B'||DataState=='C'||DataState=='D') {

      preDataState=DataState;//change les switch du keypad en interrupteurs "classiques". PreDataState sert à faire fonctionner le menu principal.
      key=0;
      
      lcd.clear();//dès qu'on presse une touche, efface rapidement l'écran. ça évite les bugs avec l'écran lcd.
      
    }

    

   
    

  switch (preDataState) {
    case 'A':// la touche A active le gitomètre.
    
       noTone(BUZZER);
       lcd.home();
       lcd.print("gite:");
       lcd.setCursor(7,0);
       lcd.print(Gite);
       lcd.setCursor(0,1);
       lcd.print("tangage:");
       lcd.setCursor(10,1);
       lcd.print(Tangage);
       delay(10);

       break;

       case 'B':// la touche B active le compas de relèvement.

       noTone(BUZZER);
       lcd.home();
       lcd.print("relevement:");
       lcd.setCursor(3,1);
       lcd.print(angle);
       delay(100);

       break;

       case 'C':// la touche C active le mode sextant(soleil).

       
       
       if (LatDeg==333 && LatMin==333 && LonDeg==333 && LonMin==333 && hauteurOeil==250) {
       
       lcd.home();
       lcd.print("point estime");
       lcd.setCursor(0,1);
       lcd.print("LatDeg:");
       

          if (key!=NO_KEY && key!='*' && key!='#') {
           Clavier[compteur++]=key;
           Clavier[compteur]='\0';// indique l'arrêt de la chaîne de caractères.
           lcd.setCursor(9,1);
           lcd.print(Clavier);
       }
          else if (key=='*') {
           LatDeg=atoi(Clavier);// convertit char en int.
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }
          else if (key=='#') {
           LatDeg=atoi(Clavier)*-1;
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }
       }
      else if (LatDeg!=333 && LatMin==333 && LonDeg==333 && LonMin==333 && hauteurOeil==250) {
       
       
       lcd.home();
       lcd.print("point estime");
       lcd.setCursor(0,1);
       lcd.print("LatMin:");
       lcd.setCursor(9,1);
       
       
          if (key!=NO_KEY && key!='*' && key !='#') {
            Clavier[compteur++]=key;
            Clavier[compteur]='\0';
            lcd.setCursor(9,1);
            lcd.print(Clavier);
       }

          else if (key=='*') {
           LatMin=atoi(Clavier);
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }

          else if (key=='#') {
           LatMin=atoi(Clavier)*-1;
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }
  
      }
      else if (LatDeg!=333 && LatMin!=333 && LonDeg==333 && LonMin==333 && hauteurOeil==250) {

       
       lcd.home();
       lcd.print("point estime");
       lcd.setCursor(0,1);
       lcd.print("LonDeg:");
       

          if (key!=NO_KEY && key!='*' && key!='#') {
           Clavier[compteur++]=key;
           Clavier[compteur]='\0';
           lcd.setCursor(9,1);
           lcd.print(Clavier);
       }

          else if (key=='*') {
           LonDeg=atoi(Clavier);
           Clavier[4]=0;
           compteur=0;
           lcd.clear();
       }

          else if (key=='#') {
           LonDeg=atoi(Clavier)*-1;
           Clavier[4]=0;
           compteur=0;
           lcd.clear();
       }
      }

      else if (LatDeg!=333 && LatMin!=333 && LonDeg!=333 && LonMin==333 && hauteurOeil==250) {

       
       lcd.home();
       lcd.print("point estime");
       lcd.setCursor(0,1);
       lcd.print("LonMin:");
       

          if (key!=NO_KEY && key!='*' && key!='#') {
           Clavier[compteur++]=key;
           Clavier[compteur]='\0';
           lcd.setCursor(9,1);
           lcd.print(Clavier);
       }

          else if (key=='*') {
           LonMin=atoi(Clavier);
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }

          else if (key=='#') {
           LonMin=atoi(Clavier)*-1;
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }
      }

       else if (LatDeg!=333 && LatMin!=333 && LonDeg!=333 && LonMin!=333 && hauteurOeil==250) {
       
       lcd.home();
       lcd.print("hauteur oeil:");
       

           if (key!=NO_KEY && key!='*' && key!='#') {
             Clavier[compteur++]=key;
             Clavier[compteur]='\0';
             lcd.setCursor(0,1);
             lcd.print(Clavier);
             }

           else if (key=='*' || key=='#') {
              hauteurOeil=atoi(Clavier);
              Clavier[4]={0};
              compteur=0;
              lcd.clear();
             }
             
      
             }

             
       


      else if (hauteurOeil!=250 && LatDeg!=333 && LatMin!=333 && LonDeg!=333 && LonMin!=333) {

        
        
       
         lcd.home();
         lcd.print("angle soleil:");
         lcd.setCursor(0,1);
         lcd.print(Tangage);
       
       
      if (key==NO_KEY) {
       
          if (Gite==constrain(Gite,0,2) ) {// facilite la précision de la visée. Si ça buzze c'est bon, on peut appuyer pour lancer la machine.
           tone(BUZZER,500);
       }
          else {
           noTone(BUZZER);
       }
          
          delay(100); 
      }
  
         
       else if (key!=NO_KEY) {
            noTone(BUZZER);
            lcd.clear();

            estimeLat=LatDeg+LatMin/60.00;
            estimeLon=LonDeg+LonMin/60.00;
            char astre='C';

           struct chiffres {// données à envoyer sous forme de variable structure.

             float Tangage;
             float estimeLat;
             float estimeLon;
             byte hauteurOeil;
             byte annee;
             byte numJourMois;
             byte mois;
             byte heure;
             byte minute;
             byte seconde;
             char astre;
             
        };

           chiffres data;

           data.Tangage=Tangage+TangageInit;
           data.estimeLat=estimeLat;
           data.estimeLon=estimeLon;
           data.hauteurOeil=hauteurOeil;
           data.annee=annee;
           data.numJourMois=numJourMois;
           data.mois=mois;
           data.heure=heure;
           data.minute=minute;
           data.seconde=seconde;
           data.astre=astre;
           
           
          
        
           Serial.write((byte *)&data, sizeof data);

           Serial.flush();
        
           

          delay(3000);
          
          
          
          while (retourSerial.available()>0) {
  
          retourSerial.readBytes((byte *)&point, sizeof point);

          
         
          Intercept=point.Intercept;
          Azimut=point.Azimut;

          
          
          
          
          lcd.home();
          lcd.print("inter et azim:");
          lcd.setCursor(0,3);
          lcd.print(Intercept);
          delay(4000);
          lcd.clear();
          lcd.home();
          lcd.print("caca chiant");
          lcd.setCursor(0,3);
          lcd.print(Azimut);
          lcd.clear();
          
          estimeLat=0,estimeLon=0,LatDeg=333,LatMin=333,LonDeg=333,LonMin=333,hauteurOeil=250;
          }
  
      }   
      }
       
         
          

       

      

       break;

       case 'D'://active le mode sextant(lune).

       if (LatDeg==333 && LatMin==333 && LonDeg==333 && LonMin==333 && hauteurOeil==250) {
       
       lcd.home();
       lcd.print("point estime");
       lcd.setCursor(0,1);
       lcd.print("LatDeg:");
       

          if (key!=NO_KEY && key!='*' && key!='#') {
           Clavier[compteur++]=key;
           Clavier[compteur]='\0';// indique l'arrêt de la chaîne de caractères.
           lcd.setCursor(9,1);
           lcd.print(Clavier);
       }
          else if (key=='*') {
           LatDeg=atoi(Clavier);// convertit char en int.
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }
          else if (key=='#') {
           LatDeg=atoi(Clavier)*-1;
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }
       }
      else if (LatDeg!=333 && LatMin==333 && LonDeg==333 && LonMin==333 && hauteurOeil==250) {
       
       
       lcd.home();
       lcd.print("point estime");
       lcd.setCursor(0,1);
       lcd.print("LatMin:");
       lcd.setCursor(9,1);
       
       
          if (key!=NO_KEY && key!='*' && key !='#') {
            Clavier[compteur++]=key;
            Clavier[compteur]='\0';
            lcd.setCursor(9,1);
            lcd.print(Clavier);
       }

          else if (key=='*') {
           LatMin=atoi(Clavier);
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }

          else if (key=='#') {
           LatMin=atoi(Clavier)*-1;
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }
  
      }
      else if (LatDeg!=333 && LatMin!=333 && LonDeg==333 && LonMin==333 && hauteurOeil==250) {

       
       lcd.home();
       lcd.print("point estime");
       lcd.setCursor(0,1);
       lcd.print("LonDeg:");
       

          if (key!=NO_KEY && key!='*' && key!='#') {
           Clavier[compteur++]=key;
           Clavier[compteur]='\0';
           lcd.setCursor(9,1);
           lcd.print(Clavier);
       }

          else if (key=='*') {
           LonDeg=atoi(Clavier);
           Clavier[4]=0;
           compteur=0;
           lcd.clear();
       }

          else if (key=='#') {
           LonDeg=atoi(Clavier)*-1;
           Clavier[4]=0;
           compteur=0;
           lcd.clear();
       }
      }

      else if (LatDeg!=333 && LatMin!=333 && LonDeg!=333 && LonMin==333 && hauteurOeil==250) {

       
       lcd.home();
       lcd.print("point estime");
       lcd.setCursor(0,1);
       lcd.print("LonMin:");
       

          if (key!=NO_KEY && key!='*' && key!='#') {
           Clavier[compteur++]=key;
           Clavier[compteur]='\0';
           lcd.setCursor(9,1);
           lcd.print(Clavier);
       }

          else if (key=='*') {
           LonMin=atoi(Clavier);
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }

          else if (key=='#') {
           LonMin=atoi(Clavier)*-1;
           Clavier[4]={0};
           compteur=0;
           lcd.clear();
       }
      }

       else if (LatDeg!=333 && LatMin!=333 && LonDeg!=333 && LonMin!=333 && hauteurOeil==250) {
       
       lcd.home();
       lcd.print("hauteur oeil:");
       

           if (key!=NO_KEY && key!='*' && key!='#') {
             Clavier[compteur++]=key;
             Clavier[compteur]='\0';
             lcd.setCursor(0,1);
             lcd.print(Clavier);
             }

           else if (key=='*' || key=='#') {
              hauteurOeil=atoi(Clavier);
              Clavier[4]={0};
              compteur=0;
              lcd.clear();
             }
             
      
             }

             
       


      else if (hauteurOeil!=250 && LatDeg!=333 && LatMin!=333 && LonDeg!=333 && LonMin!=333) {

        
        
       
         lcd.home();
         lcd.print("angle lune:");
         lcd.setCursor(0,1);
         lcd.print(Tangage);
       
       
      if (key==NO_KEY) {
       
          if (Gite==constrain(Gite,0,2) ) {// facilite la précision de la visée. Si ça buzze c'est bon, on peut appuyer pour lancer la machine.
           tone(BUZZER,500);
       }
          else {
           noTone(BUZZER);
       }
          
          delay(100); 
      }
  
         
       else if (key!=NO_KEY) {
            noTone(BUZZER);
            lcd.clear();

            estimeLat=LatDeg+LatMin/60.00;
            estimeLon=LonDeg+LonMin/60.00;
            char astre='D';

           struct chiffres {// données à envoyer sous forme de variable structure.

             float Tangage;
             float estimeLat;
             float estimeLon;
             byte hauteurOeil;
             byte annee;
             byte numJourMois;
             byte mois;
             byte heure;
             byte minute;
             byte seconde;
             char astre;
             
          
        };

           chiffres data;

           data.Tangage=Tangage+TangageInit;
           data.estimeLat=estimeLat;
           data.estimeLon=estimeLon;
           data.hauteurOeil=hauteurOeil;
           data.annee=annee;
           data.numJourMois=numJourMois;
           data.mois=mois;
           data.heure=heure;
           data.minute=minute;
           data.seconde=seconde;
           data.astre=astre;
           

           while(Serial.available()>0) Serial.read();

        
           Serial.write((byte *)&data, sizeof data);

           Serial.flush();
        
       

          delay(3000);

           
          
          
          while (retourSerial.available()>0) {
  
          retourSerial.readBytes((byte *)&point, sizeof point);

          

          
          Intercept=point.Intercept;
          Azimut=point.Azimut;

          
          
          lcd.home();
          lcd.print("inter et azim:");
          lcd.setCursor(0,1);
          lcd.print(Intercept);
          delay(4000);
          lcd.home();
          lcd.print("caca chiant:");
          lcd.setCursor(0,1);
          lcd.print(Azimut);
          lcd.clear();
          estimeLat=0,estimeLon=0,LatDeg=333,LatMin=333,LonDeg=333,LonMin=333,hauteurOeil=250;
  }
      }

         
      }
       

       
          
      

   
      
         

       break;

       default:// affiche l'horloge UTC quand aucune touche du menu n'est pressée.

       noTone(BUZZER);
      lcd.home();
      lcd.print("date:");
      lcd.setCursor(7,0);
      lcd.print(numJourMois);
      lcd.setCursor(9,0);
      lcd.print("/");
      lcd.setCursor(11,0);
      lcd.print(mois);
      lcd.setCursor(13,0);
      lcd.print("/");
      lcd.setCursor(14,0);
      lcd.print(annee);
      lcd.setCursor(0,1);
      lcd.print("UTC:");
      lcd.setCursor(5,1);
      lcd.print(heure);
      lcd.setCursor(7,1);
      lcd.print("/");
      lcd.setCursor(8,1);
      lcd.print(minute);
      lcd.setCursor(10,1);
      lcd.print("/");
      lcd.setCursor(11,1);
      lcd.print(seconde);

    
      delay(100);
     

      break;
  }

      
      
  
}



byte bcdToDec(byte val){return((val/16*10)+(val%16));} //fonction qui change décimaux codés binaires en décimaux normaux.


La carte « esclave » (arduino due):

#include <math.h>//importe des fonctions trigonométriques indispensables pour nos calculs astro.

struct chiffres {// données reçues par la nano mises en variables globales.

  float Tangage;
  float estimeLat;
  float estimeLon;
  byte hauteurOeil;
  byte annee;
  byte numJourMois;
  byte mois;
  byte heure;
  byte minute;
  byte seconde;
  char astre;

};
chiffres data;

struct retour {
      int Intercept;
      int Azimut;
    };
retour point;    

float Tangage,estimeLat,estimeLon;
byte annee,numJourMois,mois,heure,minute,seconde,hauteurOeil;
char astre;
int Intercept,Azimut;


void setup() {
  Serial.begin(57600);//ouvre la voie série entre la nano et la due.
  Serial1.begin(57600);//ouvre la voie série entre la due et la nano.

  
}
void loop() {
  
   if (Serial.available()) {// lecture du buffer de réception si il est plein.
    
    Serial.readBytes((byte *)&data, sizeof data);// lecture des données qui vont être stockées dans les variables globales.

   Tangage=data.Tangage;
   estimeLat=data.estimeLat;
   estimeLon=data.estimeLon;
   hauteurOeil=data.hauteurOeil;
   annee=data.annee;
   numJourMois=data.numJourMois;
   mois=data.mois;
   heure=data.heure;
   minute=data.minute;
   seconde=data.seconde;
   astre=data.astre;
   
  
   }
   while(Serial.available()>0) Serial.read();//vidage du buffer.
  
   
   /*Serial.print(Tangage);
   Serial.print("  ");
   Serial.print(estimeLat);
   Serial.print("  ");
   Serial.print(estimeLon);
   Serial.print("  ");
   Serial.print(hauteurOeil);
   Serial.print("  ");
   Serial.print(annee);
   Serial.print("  ");
   Serial.print(numJourMois);
   Serial.print("  ");
   Serial.print(mois);
   Serial.print("  ");
   Serial.print(heure);
   Serial.print("  ");
   Serial.print(minute);
   Serial.print("  ");
   Serial.print(seconde);
   Serial.print("  ");
   Serial.print(astre);
   Serial.print("  ");
   Serial.print(Intercept);
   Serial.print("  ");
   Serial.print(Azimut);
   Serial.println();*/
   
  int an=2000+annee;
  int m=mois;

  if (mois<=2) {
  an-=1;
  m+=12;
  }
  else {
    an=an;
    m=m;
  }
  
  int a=int(an/100);
  int b=2-a+int(a/4);

  double jJulien=long(365.25*an)+long(30.6001*(m+1))+(numJourMois+heure/24.0+minute/(24.0*60.0)+seconde/(24.0*60.0*60.0))+1720994.5+b;// calcul jour julien.
  double jJulien00h=long(365.25*an)+long(30.6001*(m+1))+numJourMois+1720994.5+b;//calcul jour julien à 00hUTC(permet de calculer TSMG).

  double T=(jJulien-2415020.0)/36525.0;// temps en siècles juliens(variable principales des équations de Jean Meeus).
  double T00h=(jJulien00h-2415020)/36525;//temps en siecles juliens à 00hUTC(permet de calculer TSMG).
  

  

    
    
  
  
  if (astre=='C') {
    
   
  double A=radians(153.23+22518.7541*T);//correction venus.
  double B=radians(216.57+45037.5082*T);//correction venus.
  double CD=radians(312.69+32964.3577*T);//correction jupiter.
  double D=radians(350.74+445267.1142*T-0.00144*sq(T));//correction lune.
  double E=radians(231.19+20.20*T);//inegalite de longue periode.
  double H=radians(353.40+65928.7155*T);//taille du slip de Mike Brant.

  double L=279.69668+36000.76892*T+0.0003025*sq(T)+0.00134*cos(A)+0.00154*cos(B)+0.00200*cos(CD)+0.00178*sin(E)+0.00179*sin(D);//longitude moyenne du soleil.

  double L1=L-long(L)+long(L)%360;

  
  double M=358.47583+35999.04975*T-0.000150*sq(T)-0.0000033*pow(T,3);//anomalie moyenne du soleil.

  double M1=M-long(M)+long(M)%360;

  
  double e=0.01675104-0.0000418*T-0.000000126*sq(T);//excentricité de l'orbite terrestre.
  
  double C1=(1.919460-0.004789*T-0.000014*sq(T))*sin(radians(M1));//équation du centre du soleil.

  double C2=(0.020094-0.0001*T)*sin(radians(M1));

  double C3=0.000293*sin(3*radians(M1));

  double C=C1+C2+C3;

  double lonVraie=L1+C;//longitude vraie du soleil.

  double v=M1+C;//anomalie vraie du soleil.

  double R=((1.0000002*(1-sq(e)))/(1+e*cos(radians(v)))+0.00000543*sin(A)+0.00001575*sin(B)+0.00001627*sin(CD)+0.00003076*cos(D)+0.00000927*sin(H));//rayon vecteur du soleil en UA.

  double obliquite=23.452294-0.0130125*T-0.00000164*sq(T)+0.000000503*pow(T,3);//obliquité de l'écliptique.

  double declinaison=degrees(asin(sin(radians(obliquite))*sin(radians(lonVraie))));

  double ascensionDroite=degrees(atan2(cos(radians(obliquite))*sin(radians(lonVraie)),cos(radians(lonVraie))));

  if (ascensionDroite<0) {
    ascensionDroite+=360;
  }
  else {
    ascensionDroite+=0;
  }

  double refraction=0;//calcul de correction de la réfraction atmosphérique selon Jean Meeus.

  if (Tangage>15.0) {
       refraction+=(58.294*tan(radians(90-Tangage))-0.0668*pow(tan(radians(90-Tangage)),3))/3600;
    }

    else if (Tangage>=0.0 && Tangage<4.0) {
       refraction+=(sq(6.6176-1.9037*log(Tangage+1.4)))/60;
    }

    else if (Tangage>=4.0 && Tangage<=15.0) {
       refraction+=(sq(4.3481-0.9466*log(Tangage-1.4)))/60;
    }

    double Oeil=(1.76/sqrt(hauteurOeil))/60;//correction de la hauteur d'oeil.

    
    double tempsSidGreen=0.276919398+100.0021359*T00h+0.000001075*sq(T00h);//temps sidéral à greenwich en révolutions.
    double tempsSidGreenHeures=(tempsSidGreen-long(tempsSidGreen))*24;//temps sidéral à greenwich en heures.

    double instantUT=(heure+minute/60.0+seconde/3600.0)*1.002737908;
    double TSMG1=instantUT+tempsSidGreenHeures;//temps sidéral à greenwich à n'importe quelle heure UT.
    double TSMG=TSMG1-long(TSMG1)+long(TSMG1)%24;

    double Diametre=degrees(atan(695510.0/(R*150000000.0)));// calcul de l'erreur de demi-diamètre(inutile pour notre système de visée au final).

    double Parallaxe=(6371/(R*150000000.0))*cos(radians(Tangage-Oeil));

    double AHL1=(TSMG*15+360)-ascensionDroite+estimeLon;
    double AHL=AHL1-long(AHL1)+long(AHL1)%360;

    
    double hauteurCalculee=degrees(asin(sin(radians(estimeLat))*sin(radians(declinaison))+cos(radians(estimeLat))*cos(radians(declinaison))*cos(radians(AHL))));

    double Azimut=0;

    if (AHL>=180 && AHL<360) {
       Azimut+=degrees(acos((sin(radians(declinaison))-sin(radians(estimeLat))*sin(radians(hauteurCalculee)))/(cos(radians(estimeLat))*cos(radians(hauteurCalculee)))));
    }
    else {
       Azimut+=360-degrees(acos((sin(radians(declinaison))-sin(radians(estimeLat))*sin(radians(hauteurCalculee)))/(cos(radians(estimeLat))*cos(radians(hauteurCalculee)))));
    }

    double hauteurVraie=Tangage-refraction-Oeil+Parallaxe;// la correction de demi-diamètre est enlevée car là on vise le milieu de l'astre.
    
    double Intercept=(hauteurVraie-hauteurCalculee)*60;

    
    
   

    

    point.Intercept=int(round(Intercept));
    point.Azimut=int(round(Azimut));

    

   
    

    
   Serial.print(point.Intercept);
   Serial.print("  ");
   Serial.print(point.Azimut);
   Serial.println();

    
    
    Serial1.write((byte *)&point,sizeof point);
    
    
    Serial1.flush();//cette instruction permet d'envoyer toutes les infos avant de passer à la suitre du programme.

    
  
    delay(500);
   
    



  }

  else if (astre=='D') {

    
  
  double omega1=259.183275-1934.1420*T+0.002078*sq(T)+0.0000022*pow(T,3);//longitude du noeud ascendant de la lune.

  double omega=omega1-long(omega1)+long(omega1)%360;

  double Lprim1=270.434164+481267.8831*T-0.001133*sq(T)+0.0000019*pow(T,3)+0.000233*sin(radians(51.2+20.2*T))+0.003964*sin(radians(346.56+132.87*T-0.0091731*sq(T)))+0.001964*sin(radians(omega));//longitude moyenne de la lune.

  double Lprim=Lprim1-long(Lprim1)+long(Lprim1)%360;
  
  double M1=358.475833+35999.0498*T-0.000150*sq(T)-0.0000033*pow(T,3)-0.001778*sin(radians(51.2+20.2*T));//anomalie moyenne du soleil.

  double M=M1-long(M1)+long(M1)%360;

  double Mprim1=296.104608+477198.8491*T+0.009192*sq(T)+0.0000144*pow(T,3)+0.000817*sin(radians(51.2+20.2*T))+0.003964*sin(radians(346.56+132.87*T-0.0091731*sq(T)))+0.002541*sin(radians(omega));//anomalie moyenne de la lune.

  double Mprim=Mprim1-long(Mprim1)+long(Mprim1)%360;

  double D1=350.737486+445267.1142*T-0.001436*sq(T)+0.0000019*pow(T,3)+0.002011*sin(radians(51.2+20.2*T))+0.001964*sin(radians(omega))+0.003964*sin(radians(346.56+132.87*T-0.0091731*sq(T)));//élongation moyenne de la lune.

  double D=D1-long(D1)+long(D1)%360;

  double F1=11.250889+483202.0251*T-0.003211*sq(T)-0.0000003*pow(T,3)+0.003964*sin(radians(346.56+132.87*T-0.0091731*sq(T)))-0.024691*sin(radians(omega))-0.004328*sin(radians(omega+275.05-2.3*T));//distance moyenne de la lune à son noeud ascendant.

  double F=F1-long(F1)+long(F1)%360;
  
  double e=1-0.002495*T-0.00000752*sq(T);

  double teta=Lprim+6.288750*sin(radians(Mprim))+1.274018*sin(radians(2*D-Mprim))+0.658309*sin(radians(2*D))+0.213616*sin(radians(2*Mprim))-0.185596*sin(radians(M))*e-0.114336*sin(radians(2*F))+0.058793*sin(radians(2*D-2*Mprim))+0.057212*sin(radians(2*D-M-Mprim))*e+0.053320*sin(radians(2*D+Mprim))+0.045874*sin(radians(2*D-M))*e+
  0.041024*sin(radians(Mprim-M))*e-0.034718*sin(radians(D))-0.030465*sin(radians(M+Mprim))*e+0.015326*sin(radians(2*D-2*F))-0.012528*sin(radians(2*F+Mprim))-0.010980*sin(radians(2*F-Mprim))+0.010674*sin(radians(4*D-Mprim))+0.010034*sin(radians(3*Mprim))+0.008548*sin(radians(4*D-2*Mprim))-0.007910*sin(radians(M-Mprim+2*D))*e-
  0.006783*sin(radians(2*D+M))*e+0.005162*sin(radians(Mprim-D))+0.005*sin(radians(M+D))*e+0.004049*sin(radians(Mprim-M+2*D))*e+0.003996*sin(radians(2*Mprim+2*D))+0.003862*sin(radians(4*D))+0.003665*sin(radians(2*D-3*Mprim))+0.002695*sin(radians(2*Mprim-M))*e+0.002602*sin(radians(Mprim-2*F-2*D))+0.002396*sin(radians(2*D-M-2*Mprim))*e
  -0.002349*sin(radians(Mprim+D))+0.002249*sin(radians(2*D-2*M))*sq(e)-0.002125*sin(radians(2*Mprim+M))*e-0.002079*sin(radians(2*M))*sq(e)+0.002059*sin(radians(2*D-Mprim-2*M))*sq(e)-0.001773*sin(radians(Mprim+2*D-2*F))-0.001595*sin(radians(2*F+2*D))+0.001220*sin(radians(4*D-M-Mprim))*e-0.001110*sin(radians(2*Mprim+2*F))
  +0.000892*sin(radians(Mprim-3*D))-0.000811*sin(radians(M+Mprim+2*D))*e+0.000761*sin(radians(4*D-M-2*Mprim))*e+0.000717*sin(radians(Mprim-2*M))*sq(e)+0.000704*sin(radians(Mprim-2*M-2*D))*sq(e)+0.000693*sin(radians(M-2*Mprim+2*D))*e+0.000598*sin(radians(2*D-M-2*F))*e+0.000550*sin(radians(Mprim+4*D))
  +0.000538*sin(radians(4*Mprim))+0.000521*sin(radians(4*D-M))*e+0.000486*sin(radians(2*Mprim-D));//longitude écliptique de la lune.

  double B=5.128189*sin(radians(F))+0.280606*sin(radians(Mprim+F))+0.277693*sin(radians(Mprim-F))+0.173238*sin(radians(2*D-F))+0.055413*sin(radians(2*D+F-Mprim))+0.046272*sin(radians(2*D-F-Mprim))+0.032573*sin(radians(2*D+F))+0.017198*sin(radians(2*Mprim+F))+0.009267*sin(radians(2*D+Mprim-F))+0.008823*sin(radians(2*Mprim-F))
  +0.008247*sin(radians(2*D-M-F))*e+0.004323*sin(radians(2*D-F-2*Mprim))+0.004200*sin(radians(2*D+F+Mprim))+0.003372*sin(radians(F-M-2*D))*e+0.002472*sin(radians(2*D+F-M-Mprim))*e+0.002222*sin(radians(2*D+F-M))*e+0.002072*sin(radians(2*D-F-M-Mprim))*e+0.001877*sin(radians(F-M+Mprim))*e+0.001828*sin(radians(4*D-F-Mprim))
  -0.001803*sin(radians(F+M))*e-0.001750*sin(radians(3*F))+0.001570*sin(radians(Mprim-M-F))*e-0.001487*sin(radians(F+D))-0.001481*sin(radians(F+M+Mprim))*e+0.001417*sin(radians(F-M-Mprim))*e+0.001350*sin(radians(F-M))*e+0.001330*sin(radians(F-D))+0.001106*sin(radians(F+3*Mprim))+0.001020*sin(radians(4*D-F))+0.000833*sin(radians(F+4*D-Mprim))
  +0.000781*sin(radians(Mprim-3*F))+0.000670*sin(radians(F+4*D-2*Mprim))+0.000606*sin(radians(2*D-3*F))+0.000597*sin(radians(2*D+2*Mprim-F))+0.000492*sin(radians(2*D+Mprim-M-F))*e+0.000450*sin(radians(2*Mprim-F-2*D))+0.000439*sin(radians(3*Mprim-F))+0.000423*sin(radians(F+2*D+2*Mprim))+0.000422*sin(radians(2*D-F-3*Mprim))
  -0.000367*sin(radians(M+F+2*D-Mprim))*e-0.000353*sin(radians(M+F+2*D))*e+0.000331*sin(radians(F+4*D))+0.000317*sin(radians(2*D+F-M+Mprim))*e+0.000306*sin(radians(2*D-2*M-F))*sq(e)-0.000283*sin(radians(Mprim+3*F));

  double W1=0.0004664*cos(radians(omega));
  double W2=0.0000754*cos(radians(omega+275.05-2.30*T));

  double beta=B*(1-W1-W2);//latitude écliptique de la lune.

  double Pi=0.950724+0.051818*cos(radians(Mprim))+0.009531*cos(radians(2*D-Mprim))+0.007843*cos(radians(2*D))+0.002824*cos(radians(2*Mprim))+0.000857*cos(radians(2*D+Mprim))+0.000533*cos(radians(2*D-M))*e+0.000401*cos(radians(2*D-M-Mprim))*e+0.000320*cos(radians(Mprim-M))*e-0.000271*cos(radians(D))-0.000264*cos(radians(M+Mprim))*e
  -0.000198*cos(radians(2*F-Mprim))+0.000173*cos(radians(3*Mprim))+0.000167*cos(radians(4*D-Mprim))-0.000111*cos(radians(M))*e+0.000103*cos(radians(4*D-2*Mprim))-0.000084*cos(radians(2*Mprim-2*D))-0.000083*cos(radians(2*D+M))*e+0.000079*cos(radians(2*D+2*Mprim))+0.000072*cos(radians(4*D))+0.000064*cos(radians(2*D-M+Mprim))*e
  -0.000063*cos(radians(2*D+M-Mprim))*e+0.000041*cos(radians(M+D))*e+0.000035*cos(radians(2*Mprim-M))*e-0.000033*cos(radians(3*Mprim-2*D))-0.000030*cos(radians(Mprim+D))-0.000029*cos(radians(2*F-2*D))-0.000029*cos(radians(2*Mprim+M))*e+0.000026*cos(radians(2*D-2*M))*sq(e)-0.000023*cos(radians(2*F-2*D+Mprim))
  +0.000019*cos(radians(4*D-M-Mprim))*e;//parallaxe de la lune.

  double R=(6378.14/sin(radians(Pi)));//distance entre le centre de la terre et de la lune en km.

  double obliquite=23.452294-0.0130125*T-0.00000164*sq(T)+0.000000503*pow(T,3);//obliquité de l'écliptique.


  
  
  double ascensionDroite=degrees(atan2((sin(radians(teta))*cos(radians(obliquite))-tan(radians(beta))*sin(radians(obliquite))),cos(radians(teta))));//ascension droite de la lune.

  if (ascensionDroite<0) {
    ascensionDroite+=360;
  }
  else {
    ascensionDroite+=0;
  }
  
  double declinaison=degrees(asin(sin(radians(beta))*cos(radians(obliquite))+cos(radians(beta))*sin(radians(obliquite))*sin(radians(teta))));//declinaison de la lune.

   

  double refraction=0;

  if (Tangage>15.0) {
       refraction+=(58.294*tan(radians(90-Tangage))-0.0668*pow(tan(radians(90-Tangage)),3))/3600;
    }

    else if (Tangage>=0.0 && Tangage<4.0) {
       refraction+=(sq(6.6176-1.9037*log(Tangage+1.4)))/60;
    }

    else if (Tangage>=4.0 && Tangage<=15.0) {
       refraction+=(sq(4.3481-0.9466*log(Tangage-1.4)))/60;
    }

    double Oeil=(1.76/sqrt(hauteurOeil))/60;

    
    double tempsSidGreen=0.276919398+100.0021359*T00h+0.000001075*sq(T00h);//temps sidéral à greenwich en révolutions.
    double tempsSidGreenHeures=(tempsSidGreen-long(tempsSidGreen))*24;//temps sidéral à greenwich en heures.

    double instantUT=(heure+minute/60.0+seconde/3600.0)*1.002737908;
    double TSMG1=instantUT+tempsSidGreenHeures;//temps sidéral à greenwich à n'importe quelle heure UT.
    double TSMG=TSMG1-long(TSMG1)+long(TSMG1)%24;

    double Diametre=degrees(atan(1750/R));//inutile pour notre système de visée au final.

    double Parallaxe=(6371/R)*cos(radians(Tangage-Oeil));

    double AHL1=(TSMG*15+360)-ascensionDroite+estimeLon;
    double AHL=AHL1-long(AHL1)+long(AHL1)%360;

    
    double hauteurCalculee=degrees(asin(sin(radians(estimeLat))*sin(radians(declinaison))+cos(radians(estimeLat))*cos(radians(declinaison))*cos(radians(AHL))));

    double Azimut=0;

    if (AHL>=180 && AHL<360) {
       Azimut+=degrees(acos((sin(radians(declinaison))-sin(radians(estimeLat))*sin(radians(hauteurCalculee)))/(cos(radians(estimeLat))*cos(radians(hauteurCalculee)))));
    }
    else {
       Azimut+=360-degrees(acos((sin(radians(declinaison))-sin(radians(estimeLat))*sin(radians(hauteurCalculee)))/(cos(radians(estimeLat))*cos(radians(hauteurCalculee)))));
    }

    double hauteurVraie=Tangage-refraction-Oeil+Parallaxe;
    
    double Intercept=(hauteurVraie-hauteurCalculee)*60;
  

    point.Intercept=int(round(Intercept));
    point.Azimut=int(round(Azimut));

     
   
    Serial1.write((byte *)&point,sizeof point);

    Serial1.flush();

    while(Serial1.available()) Serial1.read();
    
    
  
  


  

  }


  
  retour point={0,0};

  chiffres data={0,0,0,0,0,0,0,0,0,0,0};

  Tangage=0,estimeLat=0,estimeLon=0,hauteurOeil=0,numJourMois=0,mois=0,heure=0,minute=0,seconde=0,astre=0,Azimut=0,Intercept=0,annee=0;
  


  }

   

E. Résultats, verdict de Nono

  1. Sur la Terre ferme

En testant Nono dans mon jardin (avec ce fonctionnement on a pas besoin de voir la ligne d’horizon donc c’est possible, par contre il faut connaitre son altitude), les fonctions gitomètre et compas fonctionnent bien. Pour la fonction C (droite de hauteur avec le Soleil) également; en prenant bien le temps de faire sa visée, on obtient une bonne précision pour un truc « home made ». En revanche la fonction D avec la Lune est beaucoup moins précise sans être catastrophique pour autant; va falloir l’améliorer. Cependant la visée reste délicate à faire par rapport à un sextant classique. Le moindre petit mouvement parasite nous fait perdre de la précision (surtout quand on appuie sur le bouton de saisie), et là c’est dans le jardin…En mer ça risque d’être plus délicat, à voir.

2. Sur mon bateau

Sans surprise, la visée reste faisable sur le bateau par temps calme. Quand ça bouge bien on perd aussitôt beaucoup de précision. Un essai par mer calme et à la cape m’a donné un point d’une précision de 13 Mn environ. Sur terre avec un bon étalonnage du gyrocompas on obtient environ la même précision sur les 2 points faits. Par contre la visée avec la Lune n’est pas encore assez précise, bien que les résultats ne soient pas complètement abberrants.

3. Conclusion

Par rapport à un sextant classique qui coûte cher et demande de faire des calculs à la table à carte après, ce système est nettement avantageux car on gagne du temps et du pognon; en plus on peut l’utiliser sans voir la ligne d’horizon en cas de brouillard où à la tombée de la nuit par exemple, donc aussi sur la terre ferme (à condition de savoir son altitude). Par contre quand la mer est agitée il est très difficile de viser l’astre et d’avoir les données du gyrocompas suffisamment stables pour obtenir une mesure précise.

4.prolongements possibles pour améliorer ce système

a. Faire mieux et moins cher

Ce système utilise deux cartes arduino dont la due qui coûte cher, or je pense qu’il est possible de faire bien mieux avec moins…Au moment où je termine d’écrire l’article, je viens de recevoir une carte raspberry pi zero. En bref, par rapport à une carte arduino, les raspberry pi se rapprochent plus du PC que du microcontrôleur. En plus cette carte ne coûte que 5 euros (voir kubii), 10 avec les frais de port…

Cette carte est en fait un vrai petit ordinateur, avec des capacités de calculs accrues (mais pas de traitement de données analogiques contrairement à l’arduino par contre) et surtout…Une mémoire ! En effet dans mon projet il n’y a pas de calcul de la méridienne avec le soleil car je n’avais pas les moyens de stocker de l’info. Avec une mémoire interne, on peut je pense mémoriser les points mesurés afin d’interpoler une fonction déclinant le déplacement du soleil (une parabole) via une interpolation de Lagrange ou alors une autre technique, à voir (on a pas besoin d’avoir une courbe extrêmement précise, c’est le max de la courbe qui importe si en abcisses on a mis le temps et en ordonnées la hauteur angulaire du soleil). Cette mémoire interne permettra également de stocker les positions pour induire un menu, un historique de navigation par exemple, ou alors calculer une vitesse fond…

Enfin, cette carte peut intégrer un petite caméra…Vous voyez où je veux en venir ? (non, pas filmer les vestiaires des filles en catimini) toujours pas ? (pas faire la suite du film « the room » non plus pauvres fous) améliorer la visée de l’astre! Bon pour tout ça va falloir que je me mette au langage python…

b. La suite

Prochainement je prolongerai l’article en essayant d’avoir une meilleure précision avec la droite de hauteur de la lune, et aussi un meilleur usage du buzzer (avec par exemple un son plus grave quand le gyrocompas est stable pour faciliter la visée par exemple à voir…). Et évidemment, refaire une autre version améliorée avec python et raspberry pi ! Bon si vous avez noté des coquilles ou des améliorations possibles du projet surtout n’hésitez pas à partager en laissant un petit message ! En attendant, petite video explicative:

petite vidéo test pénardo dans le jardin

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.