Je vient vers vous pour exposer mon projet et peut être recevoir votre aide.
Ma passion a moi , ce n'est pas l'arduino.
Je suis aquariophile depuis tout petit et tournée depuis 6 ans vers le récifal.
Cette passion, tout comme vous, je la partage sur un forum (récifal France) et a chaque étape/projet que j'ai réalisé, j'en est profité pour partager la réalisation avec les autres membres.
Mon bac actuel :
http://www.recifal-france.fr/400l-de-djbouns-t22449.html
Un des premier projet arduin :
http://www.recifal-france.fr/boitier-balling-maison-de-80-t13110.html
Et mon dernier avec le montage d'un ackduino :
Peut etre avez vous déjà entendu parler de l'ackduino ? si non, pour faire cours, c'est un programme sur arduino afin de gérer tout les éléments essentiel d'un bac récifal
http://www.recifal-france.fr/montage-d-un-ackduino-de-a-a-z-t23888.html?hilit=ackduino#p413808
Comme vous, je prend du temps pour partager et aider les autre dans le domaine que je "maîtrise"
Je me suis lancé dans l'arduino a cause du prix de ma passion, les appareil du commerce son hors de prix (exemple, l'aquatronica de base a 750€ http://www.aqua-store.fr/automatisation-aquarium/4717-aquatronica-touch-controller-kit-basic.html)
jetait donc content d'avoir réalisé pour ~80€ des pompe de dosage, car a cette époque le premier prix était dans les 250€ et la distribution de produit en récifal était vraiment la base.
J'etait encore plus content de le partagé et de voir d'autre passionné enthousiasmé (qui devait être dans la situation budgétaire)
Mon projet en cours est en remplacement de cette ackduino (ackduino a été crée par plusieurs personne qui on chacune ajouté leur touche au fur et a mesure puis "abandonné" par les createur au depent d'un projet sur raspbery.
A moyen terme, j'ai personnellement eu le droit a des bug régulier puis a un débordement du bac + une pompe bloqué en marche a vide = cramé + disjoncté ...) bref ...
Dans le but de d'avoir un outil plus fiable pour nos bac, je me suis donc lancé dans ce projet.
(depuis juin).
Je pense avoir bien avancé seul dans un premier temps coté programme :
Levée couchée soleil
Puissance brassage
Oscillateur
Distribution nourriture
Affichage sur écran 3.2 (volonté du non tactil)
Divers alarme et securité
Mais aussi coté hardware avec le dessin et création d'un circuit imprimé (première fois pour moi), le but étant de n'avoir qu'a brancher sonde et autres ... la simplicité pour tous !
C'est sur les fonctionne plus complexe du programme que j'arrive sur le forum arduino.com ...
Je vient tout juste d'intégré dans le programme la gestion des SMS en cas de dépassement température ou de coupure edf.
Oufff :)
Mais voila ...
Je coince sur la partie WEB qui doit avoir pour but de voir les paramètres actuel du bac mais également de pouvoir faire les réglages des paramètres.
j'en apprend tout les jours avec l'arduino mais cette fonction nécessite un niveau de compétence qui me dépasse malgré mon implication. https://forum.arduino.cc/index.php?topic=502206.0
Je cherche a être épauler afin de finir ce dernier chapitre et finaliser tout ça, je pourrait ensuite, a mon tour, partager et apporté mon aide dans mon domaine, sur le forum recifal france (je ne manquerait pas de préciser les personne aillant participé)
Je comprendrait tout a fait que cela n'intéresse pas puisque il n'y a pas d'intérêt pour vous dans tout ça (a par si vous êtes aquariophile également :)).
Ci joint les fichier du projet :
https://www.petit-fichier.fr/2017/10/07/aquabouns/
Toute petite pierre a l'édifice est bien venu :)
Quoi qu'il en soit , je vous remercie d'avoir pris le temps de me lire jusque la.
Les premiers question me viennent de JML :
pourquoi vous obstiner à travailler au niveau de la commande AT et ne pas utiliser des libraries et mon petit hack PROGMEM pour balancer les réponses?
Tout simplement parce que malgré des heures de recherche sur le net, toute bibliothèque trouvé utilise des pin digital via SoftwareSerial Library au lieu des pin RX/TX
pourquoi un esp-01 en mode série? plutôt que des puces plus puissantes genre un wemos D1 mini pro par exemple qui va embarquer la couche wifi directement?
J'essaie de prendre des composant pour les quelle je pense (ou pansait pour celui ci) maîtrisé leur utilisation seul, le plus simplement possible (pour moi simplicité = fiabilité) faire embarqué la couche wifi me semble être compliqué a faire vu mon niveau
est-ce un arduino dédié à l'interface web parlant à d'autres arduino pour collecter les données à représenter où est-ce un seul arduino pilotant tout été devant aussi fournir une interface web? Quels so les éléments connectés et que font-ils
Oui, un arduino seul, toujours pour des raison de codage. ces donc ce seul arduino qui collecte et envoie les info sur la page web.
Je détaillerais tout les éléments connecté dans le post suivant.
Aussi Quelles sont les raisons qu'on président aux choix des composants ou de l'usage direct des commandes AT etc - y-a-t-il une flexibilité sur ce point là? Il doit y avoir à peine 1 ou 2 euros d'ecart Entre un ESP-01et un ESP-12 ou ESP-32 (ESP-8266EX) du Wemos
Il n'y a aucun blocage pour changer de composant si celui ci utilise un port RX/TX. Le circuit imprimé étant deja fait, il serait préférable que les composant changer, quel qu'il soit, puisse se brancher a dans l'emplacement prévu. J'étudierais dans le cas contraire si il y a vraiment un grand bénéfice a changer le CI pour ce composant.
En détails, voici les composants relié au CI :
1 sonde lm35 pour prise de température de la rampe d'éclairage
1 buzzer pour l'alarme
1 sonde ds18b20 pour température du bac
4 sortie pour le PWM de l'éclairage, driver meanwell
(http://i65.tinypic.com/14eaxkg.jpg)
(http://i63.tinypic.com/33y430y.jpg)
(http://i67.tinypic.com/axffom.jpg)
(http://i68.tinypic.com/14t1snl.jpg)
3 sortie pour le PWM des oscillateur, futaba s3003
(http://neo3plus.com/279-large_default/oscillateurs-de-pompes-osci.jpg)
3 sortie Pour le PWM des pompe de brassage ( jebao rw15) il faut ajouter une petite carte a l'interieur du boitier afin de le relier a l'arduino :
(http://www.aquastyleonline.com/product_images/uploaded_images/rw-2.jpg)
(https://farm3.staticflickr.com/2943/15137331898_b8bb076902_z.jpg)
(https://farm4.staticflickr.com/3904/15137202409_377e6d12c9_z.jpg)
sda/scl pour ds1307
rx/tx1 pour gsm sim900
rx/tx2 pour esp-01
7 sorties A pour boutons via C.I additionnel (5 utilisés pour le moment donc 1 pour la coupure EDF)
8 sorties A pour relais (commutable car certain relais seront utilisé sans l'arduino si besoin) Les relais sont dans le tableau électrique afin qu'il n'y est JAMAIS de 220v hors du tableau (et des prise :) :) :) )
(http://www.conrad.fr/medias/global/ce/3000_3999/3800/3800/3804/1217711_BB_00_FB.EPS_1000.jpg)
1 sortie A pour sonde PH
sorties de 22 a 54 pour hx8357c (écran + sd) (avec se que j'ai trouvé sur le net les sortie 42+43+44+45+46+47+49 ne serait pas utilisé)
(https://img.banggood.com/thumb/water/oaupload/banggood/images/64/6C/6f365674-fd09-4db6-988e-90d1c1dc1721.png)
(https://img.banggood.com/thumb/water/upload/2014/01/SKU193219-4.jpg)
Il seras déporté de 10/15 cm via un nappe.
Le détails des fonctions déjà codé:
l'éclairage, levée et couché progressif en fonctionne d'un horaire de début et de fin, chaque canal et décalé d'un temps donné. niveau maximum réglable
Brassage, associé au levée couché du soleil avec un minimum et maximum réglable.
Oscillateur, angle mini et maxi pour chaque oscillateur avec un temps de mouvement réglable (mais identique pour les 3).
température rampe, déclenchement des ventilateur en cas de dépassement de la température maximum réglée
température bac, déclenchement des ventilateur en cas de dépassement de la température maximum réglée puis alarme, envoie sms, coupure chauffage en cas de dépassement d'une température critique.
GSM, pour envoie de toute anomalie + coupure edf (relais situé dans le tableau électrique et alimenté en continu (relais fermé = bouton non appuyé) qui fait un retour sur un pin bouton de l'arduino, si bouton appuyé (relais ouvert = plus alimenté = bouton appuyé) envoyé d'un SMS (l'arduino est alimenté par une batterie de secours en 5v)
Wifi, pour visualisé les paramètre du bac a tout moment et aussi une partie "réglage" des paramètre
PH, mesure le PH (simplement :) ) mais possibilité par la suite de l'associer a un relais pour gestion d'un RAC par exemple (Réacteur A Calcaire, c'est un tube remplis d'aragonite dans le quelle une faible partie de l'eau de l'aquarium passe, on y injecte du CO2 afin de descendre le PH se qui dissous l'aragonite, l'eau qui en sort est donc chargé en calcium en KH et compense la consommation des coraux, le CO2 est régulé par un PH-mètre réglé a un PH précis qui active un électrovanne)
(http://www.neo3plus.com/img/cms/info%20recifale/PERIPHERIQUESRAC.jpg)
écran pour afficher les paramètre ( a voir si possibilité de mise en veille )
Fonction tempête, met le brassage au max durant un laps de temps paramétrable.
Nourrissage, 1 fonction automatique a heure fixe qui actionne un relais qui active un distributeur et baisse/coupure brassage / pompe remonté ... 2eme fonction manuel pour couper les pompe via un bouton durant un laps de temps paramétrable pour nourrire manuellement (cube de congelé par exemple)
pourquoi vous obstiner à travailler au niveau de la commande AT et ne pas utiliser des libraries et mon petit hack PROGMEM pour balancer les réponses?
Tout simplement parce que malgré des heures de recherche sur le net, toute bibliothèque trouvé utilise des pin digital via SoftwareSerial Library au lieu des pin RX/TX
avez vous regardé cette librairie (https://github.com/bportaluri/WiFiEsp).
Si vous regardez le code d'un des exemple fourni (WebServer.ino) (https://github.com/bportaluri/WiFiEsp/blob/master/examples/WebServer/WebServer.ino), vous verrez qu'il commence comme ceci:
#include "WiFiEsp.h"
// Emulate Serial1 on pins 6/7 if not present
#ifndef HAVE_HWSERIAL1
#include "SoftwareSerial.h"
SoftwareSerial Serial1(6, 7); // RX, TX
#endif
Le code teste (avec le
#ifndef HAVE_HWSERIAL1) si la carte Arduino sur laquelle on est a un port Serial1 en matériel et si oui c'est ce qui sera utilisé pour piloter les commandes AT de l'ESP, sinon il instancie une connexion SoftwareSerial qu'il appelle Serial1 et dans le setup il initialise la configuration en passant le port Série sur lequel il travaillera
// initialize ESP module
WiFi.init(&Serial1);
si j'ai un peu de temps dans l'après midi je vous poste un petit exemple
si j'ai un peu de temps dans l'après midi je vous poste un petit exemple
Inutile, c'est effectivement la seul que j'ai trouvé et en parallèle de mon code utilisant AT, j'ai travaillé dessus.
Il fonctionne mais j'ai le problème d'affichage, mot par mot dans la page web, suite au conseil sur un autre poste, j'ai essayer de stocker sur carte SD et c'est pire, il doit encore y avoir une histoire de mémoire la dedans ... et c'est ce qui complique tout pour moi.
je retrouve le code ou j'en était et vous poste.
Inutile, c'est effectivement la seul que j'ai trouvé et en parallèle de mon code utilisant AT, j'ai travaillé dessus.
Il fonctionne mais j'ai le problème d'affichage, mot par mot dans la page web, suite au conseil sur un autre poste
Oui mais comme vous utilisez une librairie qui gère le stockage en mémoire FLASH, ça ne va pas manger toute votre RAM et il n'y a pas besoin de la classe String. ça a l'avantage de simplifier l'insertion de valeur dynamique dans la page aux bons endroits sans avoir à analyser la ligne et gérer un truc un peu compliqué d'accès aux variables comme j'avais fait avec le tableau de variables dans l'autre post (en trouvant des lignes contenant juste $$$)
essayez ce code par exemple (esp-01 connecté sur Serial2)
si vous tapez l'URL - http://192.168.1.28 chez moi - ça vous dit bonjour et affiche des infos dynamiques
si vous tapez http://192.168.1.28/admin ça vous affiche une autre page et si vous tapez http://192.168.1.28/AutreChose alors vous aurez une page d'erreur
/* exemple d'utilisation de la librairie "WiFiEsp.h" // https://github.com/bportaluri/WiFiEsp */
#include "WiFiEsp.h" // https://github.com/bportaluri/WiFiEsp
#define ESPSEPRIAL Serial2
const byte hardRestPIN = 7; // la pin de l'arduino connectée au reset du module
char ssid[] = "*****"; // Nom du réseau (SSID). Mériterait d'être const mais warning à la compilation car librairie mal pensée sur ce point
char modDePasse[] = "*****"; // mot de passe du réseau. (pourrait être const char *)
int status = WL_IDLE_STATUS; // l'état de la radio
const uint16_t HTTPPort = 80;
WiFiEspServer server(HTTPPort); // serveur sur le port 80
const byte maxHTTPLine = 100;
char httpLine[maxHTTPLine + 1]; // +1 pour avoir la place du '\0'
const byte maxURL = 20;
char urlRequest[maxURL + 1]; // +1 pour avoir la place du '\0'
// **************************************************
// LA GESTION DU WEB
// **************************************************
void envoyerPageWeb(WiFiEspClient &client, const char * urlRequest)
{
// On envoie un en tête de réponse HTTP standard
client.print(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n" // la connexion sera close par le navigateur à la fin de la réponse
"\r\n"); // une ligne vide marque la fin du header HTTP
client.print(F("<!DOCTYPE HTML>\r\n"));
client.print(F("<html><head>\r\n"));
client.print(F("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n"));
client.print(F("<title>AQUABOUN</title>\r\n"));
if (!strcmp(urlRequest, "/")) { // on vérifie quelle page est demandée
// On envoie la page web
static int reqCount = 0;
client.print(F("<style type=\"text/css\">\r\n"));
client.print(F("body {background-color: #00979c}\r\n")); // couleur arduino
client.print(F("</style></head>\r\n"));
client.print(F("<body>\r\n"));
client.print(F("<h1>Salut djbouns !</h1>\r\n"));
client.print(F("Nb de demandes ="));
client.print(++reqCount);
client.print(F("<br>\r\n"));
client.print(F("Valeur lue sur A0: "));
client.print(analogRead(0));
client.print(F("<br>\r\n"));
client.print(F("</body>"));
} else if (!strcmp(urlRequest, "/admin")) { // on vérifie quelle page est demandée
// On envoie la page web d'admin
client.print(F("<title>ADMIN</title>\r\n"));
client.print(F("<style type=\"text/css\">\r\n"));
client.print(F("body {background-color: #89C8F3}\r\n")); // bleu clair
client.print(F("</style></head>\r\n"));
client.print(F("<body>\r\n"));
client.print(F("<h1>Salut djbouns !</h1>\r\n"));
client.print(F("Page d'administration<br>\r\n"));
client.print(F("</body>"));
} else {
// On envoie la page web d'erreyr
client.print(F("<title>ADMIN</title>\r\n"));
client.print(F("<style type=\"text/css\">\r\n"));
client.print(F("body {background-color: #FF0000}\r\n")); // rouge
client.print(F("</style></head>\r\n"));
client.print(F("<body>\r\n"));
client.print(F("<h1>ERREUR</h1>\r\n"));
client.print(F("</body>"));
}
client.print(F("</html>\r\n"));
// un peu d'attente pour que le module ESP ait fini de tout envoyer
delay(10);
}
// **************************************************
void printHTTPServerInfo()
{
IPAddress ip = WiFi.localIP();
Serial.print(F("Site web http://")); Serial.print(ip);
if (HTTPPort != 80) {
Serial.print(F(":"));
Serial.print(HTTPPort);
}
Serial.println();
}
void setup() {
Serial.begin(115200); // pour le debug
//reset du esp-01
pinMode(hardRestPIN, OUTPUT);
digitalWrite(hardRestPIN, LOW);
delay(100);
digitalWrite(hardRestPIN, HIGH);
delay(1000); // on attend le reboot
ESPSEPRIAL.begin(115200); // l'ESP-01 a été pré-configuré à 115200 bauds
WiFi.init(&ESPSEPRIAL); // initialisation de la librairie et du module ESP
// on vérifie l'état
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println(F("Composant wifi ne répond pas"));
while (true); // on meurt ici
}
// on essaye de se connecter au réseau
while ( status != WL_CONNECTED) {
Serial.print(F("."));
status = WiFi.begin(ssid, modDePasse);
}
Serial.println();
printHTTPServerInfo();
// démarre le serveur web sur le port HTTPPort
server.begin();
}
void loop() {
WiFiEspClient client;
boolean currentLineIsBlank = true;
byte indexMessage = 0;
char * ptrGET, *ptrEspace;
// on attend de recevoir une connexion
if (client = server.available()) {
while (client.connected()) {
if (client.available()) {
char c = client.read();
switch (c) {
case '\r': // on l'ignore
break;
case '\n':
httpLine[indexMessage] = '\0'; // on termine la ligne correctement (c-string)
indexMessage = 0; // on se reprépre pour la prochaine ligne
currentLineIsBlank = true;
if (ptrGET = strstr(httpLine, "GET")) {
// c'est la requête GET, la ligne continent "GET /URL HTTP/1.1", on extrait l'URL
ptrEspace = strstr(ptrGET + 4, " ");
*ptrEspace = '\0';
strncpy(urlRequest, ptrGET + 4, maxURL);
urlRequest[maxURL] = '\0'; // par précaution si URL trop longue
}
break;
default:
currentLineIsBlank = false;
if (indexMessage <= maxHTTPLine - 1) {
httpLine[indexMessage++] = c; // sinon on ignore le reste de la ligne
}
break;
}
if (c == '\n' && currentLineIsBlank) { // une requête HTTP se termine par une ligne vide
envoyerPageWeb(client, urlRequest);
break;
}
}
}
client.stop(); // termine la connexion
}
}
voila
#include "WiFiEsp.h"
WiFiEspServer server(80);
char ssid[] = "***"; // your network SSID (name)
char pass[] = "***"; // your network password
int status = WL_IDLE_STATUS; // the Wifi radio's status
void setup() {
Serial.begin(115200);
Serial2.begin(115200);
WiFi.init(&Serial2);
if (WiFi.status() == WL_NO_SHIELD) {
while (true);
}
while ( status != WL_CONNECTED) {
status = WiFi.begin(ssid, pass);
}
server.begin();
}
void loop() {
WiFiEspClient client = server.available();
if (client) {
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
if (c == '\n' && currentLineIsBlank) {
client.println("<!DOCTYPE html>");
client.println("<html>");
client.println("<head>");
client.println("<title>AQUABOUN'S</title>");
client.println("<style>body{background-color: LightBlue ;color: MediumBlue ;font-family: Comic Sans MS;font-size: 35px;}");
client.println("h1{color: MediumBlue ;text-align: center;font-family: Comic Sans MS;font-size: 100px; text-shadow: 5px 4px DodgerBlue ;border: 2px solid red;}");
client.println("h2{color: MediumBlue ;text-align: center;font-family: Comic Sans MS;font-size: 60px; text-shadow: 3px 2px DodgerBlue ;}");
client.println(".button {display: inline-block;padding: 5px 40px;font-size: 25px;cursor: pointer;text-align: center;background-color:mediumBlue;color: white;border-radius:20px;}");
client.println(".button:active {background-color: Blue;}");
client.println(".button2 {background-color:FireBrick;display: inline-block;padding: 5px 40px;font-size: 25px;cursor: pointer;text-align: center;color: white;border-radius:20px;}");
client.println(".button2:active {background-color:red;}");
client.println("</style>");
client.println("</head>");
client.println("<body>");
client.println("<h1>AQUABOUN'S</h1>");
client.println("<br />");
client.println( "<FONT size= 35px>");
client.println( "<text-align: left>");
client.println("<p>Heure : ");
client.println( "<FONT color= red>");
client.println( "<FONT color= MediumBlue >");
client.println( "H");
client.println( "<FONT color= red>");
client.println( "0");
client.println( "<FONT color= MediumBlue >");
client.println( " ");
client.println( "<FONT size= 3px>");
client.println( "<FONT color= red>");
client.println( "<FONT color= MediumBlue >");
client.println( "s");
client.println("<br />");
client.println( "<FONT size= 35px>");
client.println("<p>Temperature aquarium : ");
client.println( "<FONT color= red>");
client.println("°");
client.println("<br />");
client.println( "<FONT color= MediumBlue >");
client.println("<p>Temperature rampe : ");
client.println( "<FONT color= red>");
client.println("°");
client.println("<br />");
client.println( "<FONT color= MediumBlue >");
client.println("<p>PH: ");
client.println( "<FONT color= red>");
client.println( "sonde ph non instalee");
client.println("<br />");
client.println("<br />");
client.println("<FONT color= MediumBlue >");
client.println("<p>puissance blanc : ");
client.println("<FONT color= red>");
client.println("% ");
client.println("<br />");
client.println("<button class=button>-</button>");
client.println("<button class=button>+</button>");
client.println("<button class=button2>Valider</button></p>");
client.println("<FONT color= MediumBlue >");
client.println("<p>puissance bleu : ");
client.println("<FONT color= red>");
client.println("% ");
client.println("<br />");
client.println("<button class=button>-</button>");
client.println("<button class=button>+</button>");
client.println("<button class=button2>Valider</button></p>");
break;
}
if (c == '\n') {
currentLineIsBlank = true;
}
else if (c != '\r') {
currentLineIsBlank = false;
}
}
}
delay(10);
client.stop();
}
}
Chez moi 24 seconde par avoir la page afficher entièrement
/* exemple d'utilisation de la librairie "WiFiEsp.h" // https://github.com/bportaluri/WiFiEsp */
#include "WiFiEsp.h" // https://github.com/bportaluri/WiFiEsp
#define ESPSEPRIAL Serial2
const byte hardRestPIN = 7; // la pin de l'arduino connectée au reset du module
char ssid[] = "ddd"; // Nom du réseau (SSID)
char modDePasse[] = "ddd"; // mot de passe du réseau
int status = WL_IDLE_STATUS; // l'état de la radio
const uint16_t HTTPPort = 80;
WiFiEspServer server(HTTPPort); // serveur sur le port 80
const byte maxHTTPLine = 100;
char httpLine[maxHTTPLine + 1]; // +1 pour avoir la place du '\0'
const byte maxURL = 20;
char urlRequest[maxURL + 1]; // +1 pour avoir la place du '\0'
// **************************************************
// LA GESTION DU WEB
// **************************************************
void envoyerPageWeb(WiFiEspClient &client, const char * urlRequestL)
{
// On envoie un en tête de réponse HTTP standard
client.print(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n" // la connexion sera close par le navigateur à la fin de la réponse
"\r\n"); // une ligne vide marque la fin du header HTTP
client.print(F("<!DOCTYPE HTML>\r\n"));
client.print(F("<html><head>\r\n"));
client.print(F("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n"));
client.print(F("<title>AQUABOUN</title>\r\n"));
if (!strcmp(urlRequest, "/")) { // on vérifie quelle page est demandée
// On envoie la page web
static int reqCount = 0;
client.print(F("<style type=\"text/css\">\r\n"));
client.print(F("body {background-color: #00979c}\r\n")); // couleur arduino
client.print(F("</style></head>\r\n"));
client.print(F("<body>\r\n"));
client.print(F("<h1>Salut djbouns !</h1>\r\n"));
client.print(F("Nb de demandes ="));
client.print(++reqCount);
client.print(F("<br>\r\n"));
client.print(F("Valeur lue sur A0: "));
client.print(analogRead(0));
client.print(F("<br>\r\n"));
client.print(F("</body>"));
} else if (!strcmp(urlRequest, "/admin")) { // on vérifie quelle page est demandée
// On envoie la page web d'admin
client.print(F("<title>ADMIN</title>\r\n"));
client.print(F("<style type=\"text/css\">\r\n"));
client.print(F("body {background-color: #89C8F3}\r\n")); // bleu clair
client.print(F("</style></head>\r\n"));
client.print(F("<body>\r\n"));
client.print(F("<h1>Salut djbouns !</h1>\r\n"));
client.print(F("Page d'administration<br>\r\n"));
client.print(F("</body>"));
} else {
// On envoie la page web d'erreyr
client.print(F("<title>ADMIN</title>\r\n"));
client.print(F("<style type=\"text/css\">\r\n"));
client.print(F("body {background-color: #FF0000}\r\n")); // rouge
client.print(F("</style></head>\r\n"));
client.print(F("<body>\r\n"));
client.print(F("<h1>ERREUR</h1>\r\n"));
client.print(F("</body>"));
}
client.print(F("</html>\r\n"));
// un peu d'attente pour que le module ESP ait fini de tout envoyer
delay(10);
}
// **************************************************
void printHTTPServerInfo()
{
IPAddress ip = WiFi.localIP();
Serial.print(F("Site web http://")); Serial.print(ip);
if (HTTPPort != 80) {
Serial.print(F(":"));
Serial.print(HTTPPort);
}
Serial.println();
}
void setup() {
Serial.begin(115200); // pour le debug
//reset du esp-01
pinMode(hardRestPIN, OUTPUT);
digitalWrite(hardRestPIN, LOW);
delay(100);
digitalWrite(hardRestPIN, HIGH);
delay(1000); // on attend le reboot
ESPSEPRIAL.begin(115200); // l'ESP-01 a été pré-configuré à 115200 bauds
WiFi.init(&ESPSEPRIAL); // initialisation de la librairie et du module ESP
// on vérifie l'état
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println(F("Composant wifi ne répond pas"));
while (true); // on meurt ici
}
// on essaye de se connecter au réseau
while ( status != WL_CONNECTED) {
Serial.print(F("."));
status = WiFi.begin(ssid, modDePasse);
}
Serial.println();
printHTTPServerInfo();
// démarre le serveur web sur le port HTTPPort
server.begin();
}
void loop() {
WiFiEspClient client;
boolean currentLineIsBlank = true;
byte indexMessage = 0;
char * ptrGET, *ptrEspace;
// on attend de recevoir une connexion
if (client = server.available()) {
while (client.connected()) {
if (client.available()) {
char c = client.read();
switch (c) {
case '\r': // on l'ignore
break;
case '\n':
httpLine[indexMessage] = '\0'; // on termine la ligne correctement (c-string)
indexMessage = 0; // on se reprépre pour la prochaine ligne
currentLineIsBlank = true;
if (ptrGET = strstr(httpLine, "GET")) {
// c'est la requête GET, la ligne continent "GET /URL HTTP/1.1", on extrait l'URL
ptrEspace = strstr(ptrGET + 4, " ");
*ptrEspace = '\0';
strncpy(urlRequest, ptrGET + 4, maxURL);
urlRequest[maxURL] = '\0'; // par précaution si URL trop longue
}
break;
default:
currentLineIsBlank = false;
if (indexMessage <= maxHTTPLine - 1) {
httpLine[indexMessage++] = c; // sinon on ignore le reste de la ligne
}
break;
}
if (c == '\n' && currentLineIsBlank) { // une requête HTTP se termine par une ligne vide
envoyerPageWeb(client, urlRequest);
break;
}
}
}
client.stop(); // termine la connexion
}
}
P**** c'est génial ça !!!
Vous avez peut-être un soucis WiFi...
Chez moi ça prend environ 1 seconde
et il faudrait - pour ne pas tuer la mémoire vive - changer tous les
client.println("xxxxx");
en
client.println(F("xxxxx"));
histoire de stocker les chaînes en mémoire flash directement et passer le pointeur sur cette mémoire plutôt que de les emmener en mémoire vive
P**** c'est génial ça !!!
est-ce que ça répond plus vite ?
et sinon bravo pour tout votre "bricolage" - y'a des heures de boulot !
est-ce que ça répond plus vite ?
et sinon bravo pour tout votre "bricolage" - y'a des heures de boulot !
Mon exclamation était sur le fait d'avoir 2 page possible, donc une pour le paramétrage qui seras, en quelque sorte, caché.
Et oui ... des heures ... ou plutôt des jour ... quoi qu'on doit être a des semaine maintenant lol car certaine nuit son très courte quand on est dans le codage ... lol
Merci a vous
Vous avez peut-être un soucis WiFi...
Chez moi ça prend environ 1 seconde
a ... un problème de plus a résoudre alors :(
j'ai mis ma page html dans votre code et ca me fait pareil ... affichage mot par mot
vous utilisez quel navigateur?
oui j'imagine effectivement que c'est bcp de temps !!
google chrome ...
et ...
bon pronostique puisque c'est quasi instantanée dans IE ... !
en fait je pense que Chrome va balancer des requêtes supplémentaires pour d'autres choses et que vous retournez votre page plusieurs fois... en générant des erreurs au passage sur requête multiples vers votre ESP
J'ai testé avec mon téléphone avec l'adresse IP a distance et ça marche, sa s'affiche un peu moins vite que d'autre page internet mais c'est minime.
Je vais donc poursuivre cette voix.
J'ai l'impression que c'est un grand pas en avant
je vous tient au courant
voila :)
J'suis content.
petite question,je n'ai pas de PIN pour le hardRestPIN = 7
Je peut envoyer un at+rst a la place ?
Je doit a présent affecté les bouton a une fonction et sauvegarder ... connaissez vous un tuto bien fait a lire ?
Le AT+RST est envoyé sans doute à l'initialisation, donc pas la peine à mon avis (à vérifier dans le code source). le reset hardware est physique, ça veut dire que si pour une raison quelconque l'interpretteur De commandes AT ne répond pas ça force un vrai reset matériel
Pourquoi pas de pin reset? vous n'en n'avez plus de dispo?
Pour les pin dispo j'attend de recevoir mon dernier CI pour vérifier que les 7pin mentionné plus haut, normalement pris par l'écran son dispo.
sinon, non plus de dispo :/
Pour le reset physique ... j'utilise un adaptateur
https://www.banggood.com/fr/ESP8266-Serial-Wi-Fi-Wireless-ESP-01-Adapter-Module-3_3V-5V-Compatible-For-Arduino-p-1047322.html?currency=EUR&createTmp=1&cur_warehouse=CN&utm_source=googleshopping&utm_medium=cpc_elc&utm_content=zouzou&utm_campaign=pla-fr-dp-pc-esp8266&gclid=Cj0KCQjwvOzOBRDGARIsAICjxocU1pYs2MQt0wWKD_YvmnoJLf-TenFfjPvpPwyeZG_8_Djn__ABLoMaAqhtEALw_wcB (https://www.banggood.com/fr/ESP8266-Serial-Wi-Fi-Wireless-ESP-01-Adapter-Module-3_3V-5V-Compatible-For-Arduino-p-1047322.html?currency=EUR&createTmp=1&cur_warehouse=CN&utm_source=googleshopping&utm_medium=cpc_elc&utm_content=zouzou&utm_campaign=pla-fr-dp-pc-esp8266&gclid=Cj0KCQjwvOzOBRDGARIsAICjxocU1pYs2MQt0wWKD_YvmnoJLf-TenFfjPvpPwyeZG_8_Djn__ABLoMaAqhtEALw_wcB)
Ah c'est un adaptateur qui n'expose pas toutes les pins... il en existe d'autres qui sont plus complets
(http://forum.arduino.cc/index.php?action=dlattach;topic=504431.0;attach=229108)
(http://forum.arduino.cc/index.php?action=dlattach;topic=504431.0;attach=229106)
mais vous pouvez toujours attaquer directement la pin RST du ESP-01 (avec un pont diviseur de tension pour passer en 3.3V)
Je vais faire plus simple, je vais souder le pin reset dans l'autre sens pour qu'il ne soit pas sur l'adaptateur :)
pin rst Ok,
Bon je lit des poste sur le net mais affecter les boutons a des fonction (et je ne parle pas de la sauvegarde) a l'air hard core et ça m'inquiète beaucoup ... surtout que je ne trouve rien de tres bien expliquer, enfin, surtout compréhensible pour moi :)
bon 4h a lire tout se que je trouve en cherchant "arduino html bouton" et rien de concluant.
La plus par des exemple trouvé me font par exemple "http://192.*****/mavariable=1" et donc page d'erreur.
Je doit a présent affecté les bouton a une fonction et sauvegarder ... connaissez vous un tuto bien fait a lire ?
Si vous regardez mon tuto sur le shield ethernet (http://forum.arduino.cc/index.php?topic=460333.0) vous verrez comment je propose une approche relativement simple pour cela et le code HTML de base pour un bouton.
L'idée c'est que la page principale soit générée par http://adresse.ip et les boutons dans le code html sont définis et appellent le site principal mais avec une url différente
http://adresse.ip/param<ici des paramètres>
Vous avez vu ci dessus que l'on est capable de savoir quelle resource est demandée (/ pour la page principale, /admin pour la page d'admin) et là il faut soit que chaque bouton ait son url, soit aller un cran plus loin et passer des paramètres que votre fonction va analyser pour déterminer quoi faire
Vous verrez que cette approche simple a été reprise dans cet exemple (http://forum.arduino.cc/index.php?topic=493039.msg3363944#msg3363944) (serveur interactif de relais qui peut supporter jusqu ' a 2000 relais commandé par internet) qui au final n'est pas loin de ce que vous faites
Après on peut faire plus compliqué avec de l'ajax, des requêtes de type POST etc, mais je pense que ça va dépasser vos compétences et besoins
Merci, je lit et essai de comprendre et d'adapter et je revient ... dans une semaine :) :) :)
alors, le code avec les relais ne me correspond pas trop puisque il ne fait que duplique une même fonction ON/OFF selon une suite de déterminé de boutonx=PINx ect...
j'ai compris le principe du bouton qui complète l'url.
Le code du tuto correspondrait a mes besoin puisque il ajoute ou diminue des valeurs mais j'ai un problème de compréhension même si j'ai compris le principe.
J'ai don testé la chose , simpliste, suivante :
Sur ma page principale :
client.print(F("<button onclick=\"location.href='/V=1'\" type='button'>sfgqrg</button>"));
puis
else if (!strcmp(urlRequest, "/V=1")) {
test = test +1;
}
Le positif est que ma variable test est bien modifié a +1
Le problème c'est le retour aussi tôt a la page principal ...
J'ai tenté un return lol sa aurais été trop simple :)
Je continu ma croisade ...
Il faut régénérer la page totale
Il faut régénérer la page totale
on est bien d'accord
j'ai tenté urlRequest == "/";
avec ce que j'ai vu sur le net,
client.print("location.href='/'");
devrait marché :(
J'ai trouvé ca qui marche, que je trouve assez simple mais il faut attendre plusieurs seconde entre chaque click sinon sa marche pas :(
une idée pourquoi si long ?
client.println("<button onClick=location.href='./?LED=ON'>ON</button>");
et dans le loop
cadena.concat(c);
int posicion=cadena.indexOf("LED=");
if(cadena.substring(posicion)=="LED=ON")
{
test = test +1;
}
Que me conseille tu ? cette méthode est bonne et peut être adapter a tout mes nombreux bouton ou faut il que je creuse sur ton code, il ne me manque que le retour a la page après le clic ... et vu ton niveau, je pencherais pour ton code :)
la librairie n'est pas top pour plusieurs requêtes trop rapide en provenance du même navigateur et l'ESP-01 a ses propres contraintes aussi de ce côté là - un shield ethernet ou une carte wifi de plus haut niveau est bien utile si vous voulez plus de robustesse .
Sinon vous retombez dans l'usage de la classe String... pas bon...
avecclient.println("<button onClick=location.href='./?LED=ON'>ON</button>");
l'URL va être capturée par mon code du post #6 quand la loop appelle la fonction:
envoyerPageWeb(WiFiEspClient &client, const char * urlRequest)
la variable urlRequest vaudra alors "/?LED=ON" vous pouvez regardez dans mon autre tuto comment on analyse proprement cette requête si vous voulez de la dynamité (LED qui peut être autre chose et ON aussi) ou si vous avez peu de boutons faire comme dans mon code ci dessus en comparant la requête reçue avec "/" ou "/admin" --> tester avec "/?LED=ON"
si vous retournez toujours la même page web après modif éventuellement de certains param lors de la requête il suffit de modifier un peu la fonction pour d'abord tester l'URL et faire les modifs puis ensuite balancer la page
void envoyerPageWeb(WiFiEspClient &client, const char * urlRequest)
{
static int reqCount = 0;
// ************* ICI TESTER L'URL RECUE POUR DECIDER QUOI FAIRE *************
if (!strcmp(urlRequest, "/")) { // page de base
reqCount = 0; // par exemple une requête sur la home page remet à 0 le compteur du serveur
} else if (!strcmp(urlRequest, "/?LED=ON")) { // requête d'un bouton
reqCount++; // par exemple on modifie une variable utilisée plus tard dans la page
} else if (!strcmp(urlRequest, "/?LED=OFF")) { // requête d'un bouton
reqCount--; // par exemple on modifie une variable utilisée plus tard dans la page
}
// **********************************************************************
// puis on génère la page qui va bien
// On envoie un en tête de réponse HTTP standard client.print(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n" // la connexion sera close par le navigateur à la fin de la réponse
"\r\n"); // une ligne vide marque la fin du header HTTP
client.print(F("<!DOCTYPE HTML>\r\n"));
client.print(F("<html><head>\r\n"));
client.print(F("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n"));
client.print(F("<title>AQUABOUN</title>\r\n"));
// On envoie la page web
client.print(F("<style type=\"text/css\">\r\n"));
client.print(F("body {background-color: #00979c}\r\n")); // couleur arduino
client.print(F("</style></head>\r\n"));
client.print(F("<body>\r\n"));
client.print(F("<h1>Salut djbouns !</h1>\r\n"));
client.print(F("Nb de demandes ="));
client.print(reqCount);
client.print(F("<br>\r\n"));
client.print(F("Valeur lue sur A0: "));
client.print(analogRead(0));
client.print(F("<br>\r\n"));
client.print(F("</body>"));
client.print(F("</html>\r\n"));
// un peu d'attente pour que le module ESP ait fini de tout envoyer
delay(10);
}
pour tester même s'il n'y a pas de boutons dans la page générée, vous pouvez taper directement dans la barre d'adresse du navigateur http://<ip>/?LED=ON ou http://<ip>/?LED=OFF et vous allez voir la variable s'incrémenter ou se décrémenter
OK ?
Bonjour à toutes et à tous,
Ayant un peu galéré sur le sujet avant d'arriver à quelque chose de cohérent et qui marche, je me permets d'intervenir sur la façon de faire générer une requête à un navigateur...
Regardons cela sur un exemple, une page et la requête que le navigateur génère quand on clique sur un bouton après avoir rempli les champs de saisie. Le code complet de la page est en PJ.
La requête est une GET, la plus employée, qui ressemble à cela, aux variations de navigateur près:
GET /BTS10.htm?T80=admin&T81=pass&N01=01 HTTP/1.1
Host: 192.168.1.236
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.1.236/BTS10.htm?T80=&T81=&N01=01
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cette requête demande une ressource (URI) dont l'identification complète est :
192.168.1.236/BTS10.htm?T80=admin&T81=pass&N01=01
Il y a 3 parties:
- un site "192.168.1.236", ici identifié par une adresse IP. Le navigateur la place dans la ligne "Host".
- une page "BTS10.htm" située à la racine du site,
- des informations passées au serveur pour qu'il génère la page: "T80=admin&T81=pass&N01=01" (*) Cette partie est aussi appelée "tail" par les anglosaxons. Je garderai le mot anglais...
Comment mettre ce qu'il faut dans une page html pour que le navigateur génère une requête bien propre ?
Regardons cela sur l'exemple :
Le site : sauf si on veut en changer, il n'y a rien à mettre. Par défaut, le navigateur reste sur le site où il est,
La page : elle se précise dans la balise ouvrante <form action='BTS10.htm'> qui ouvre un formulaire. S'il n'y en a qu'une appelez la "index.htm" comme tout le monde,
Les infos passées à la page sont générées par chacune des commandes sur le formulaire : boutons, entrées, sélecteurs, etc. Cette partie peut être vide, par ex. pour la requête initiale sur un serveur,
Ici, deux boîtes de texte et un bouton.
Pour chaque commande, le même format au retour: "name=value"
Voyons comment décrire cela en html pour la première boîte de texte :
<input name='T80' type='text' size='30' maxlength='20' style='font-size: 12pt'>
Un champ "name", mais pas de "value", car le champ s'affiche vide avant la saisie. Si j'avais voulu mettre une valeur par défaut, j'aurais ajouté : value='defaut', écrit ici sans l'accent.
Il s'agit d'une saisie de texte type='text' et le reste sont des formatages.
A l'exécution, j'ai tapé "admin" que l'on retrouve au retour dans le champ "T80=admin".
La boîte de texte T2 est identique, sauf qu'il s'agit d'un mot de passe :
<input name='T81' type='password' size='30' maxlength='20' style='font-size: 12pt'>
Ayant saisi "pass", elle retourne T81=pass.
Maintenant, le bouton :
<button name='N01' type='submit' value ='01'><p class='Bt1'>Valider</p></button>
Les champs importants sont le "name" et la "value" que l'on retrouve au retour dans la requête : "N01=01".
Comment cela marche t'il ?
Rien de plus simple : lorsqu'on clique un bouton, le navigateur collecte toutes les "values" des boîtes d'entrée et termine par celle du bouton cliqué (il ne peut y en avoir qu'un).
Il met le tout à la file, les entrées étant séparées par "&" et l'on obtient :
"T80=admin&T81=pass&N01=01" placé, dans une requête GET, après la page séparé par un "?", sur la première ligne, après le GET et un blanc, soit :
/BTS10.htm?T80=admin&T81=pass&N01=01
Le "/" indique le page demandée est sur la racine du site.
Le traitement de la requête reçue comprend deux étapes :
- isoler la page et le "tail" dans la requête,
- dans le "tail", extraire les informations nécessaires.
Il y a plein de bibliothèques et de routines (dont celles de J-M-L) qui font cela, mais il faut d'abord avoir le bon code html pour générer une requête correcte.
Dans votre cas, vous utilisez le "onClick=selection.href="...". C'est du Javascript. Ca a marché dans votre exemple, mais cela n'est pas fait pour cela. Réservez le pour changer de site, pas pour passer des infos à votre serveur.
Voila, j'espère que cela va vous aider. Le fichier joint est le code html de la page exemple. Vous pouvez l'ouvrir avec votre navigateur. En cliquant sur un bouton, le navigateur générera une erreur, mais il affichera la page et le "tail" de sa requête (en tout cas Firefox le fait).
Je vous invite à bidouiller le code de la page, en regardant en direct sur votre navigateur ce qu'il fait. C'est super pour mettre au point une page.
Pour le détail du html, format, commandes, accents, couleurs, etc, un site un peu ancien mais très complet, avec plein d'exemples, même si la navigation y est un peu touffue:
http://www.info.univ-angers.fr/~gh/selfhtml_fr/ (http://www.info.univ-angers.fr/~gh/selfhtml_fr/)
Bon courage, ce n'est pas simple au début, mais une fois que l'on a quelque chose qui marche, cela fait plaisir. On perfectionne ensuite au fur et à mesure.
Enfin, il y a de multiples façons de faire, à chacun la sienne...
A+
MicroQuettas
(*) Les puristes auraient utilisé une requête POST pour ce cas. Ils ont raison, mais c'est pour un exemple de la requête GET.
l'ESP-01 a ses propres contraintes aussi de ce côté là
une carte wifi de plus haut niveau est bien utile si vous voulez plus de robustesse .
Plusieurs fois ou vous aborder ce sujet/problème.
Que me conseiller vous ? (il doivent etre utiliser via tx/rx)
esp12 (http://www.ebay.fr/itm/ESP8266-ESP12-NodeMcu-Lua-WeMos-D1-Sans-Fil-WiFi-Kit-Developpement-Carte-TE441-/152657664713?hash=item238b1b1ac9:g:X9oAAOSwzhRZjAeD) ?
esp32 (http://www.ebay.fr/itm/ESP32-Genuine-WeMos-LOLIN32-V1-0-0-compatible-with-Arduino-NodeMCU-MicroPython-/272710828821?hash=item3f7ed50315:g:EfgAAOSwPWRZO6fy) ?
Je ne vais pas rester bloquer sur ce projet a cause d'un composant ... j'ai déjà assez avec le code ... :)
OK ?
J'ai bien compris le principe dont cela doit fonctionner mais je doit continuer a travailler un code a partir de : envoyerPageWeb(WiFiEspClient &client, const char * urlRequest)
ou pas.
Il semblerais que ce ne soit pas adapter a mon utilisation au dire de MicroQuettas.
Pourtant, cela fonctionne mise a par se problème de vitesse avec le bouton qui n'est pas pris en compte.
Sinon, pour contrer cela, il est peut être possible de faire un champs pour saisir directement la valeur voulu et valider, se qui ne ferait qu'une demande ... ??? qu'en penser vous ?
J'arrive vraiment a un problème de compréhension a ce niveau a cause d'une chose assez simple.
Jusque a présent, mis a par les thermes d'écriture qui m'était inconnu, la saisie des ligne de commande avait une logique et un sens.
Ce qui me bloque dans les exemple ect ... c'est ce genre de chose :
httpLine[indexMessage++] = c;
(c == '\n' && currentLineIsBlank)
char * ptrGET, *ptrEspace;
Qui n'ons ni queue ni tète pour moi et ce n'est pas faute de lire et relire, de faire des recherche avec l'IDE avec la fonction trouvée pour essayer de comprendre a quoi se rapporte chaque élément.
Je passe ~3h le matin et ~4h la nuit depuis plusieurs jour a vous lire et lire des dizaines de post sur le net.
Ma motivation est vraiment importante mais j'arrive a un point ou je me demande si le poisson n'est pas trop gros et je suis conscient du temps que vous prenez.
Bon courage, ce n'est pas simple au début, mais une fois que l'on a quelque chose qui marche, cela fait plaisir. On perfectionne ensuite au fur et à mesure.
Bonjour MicroQuettas
Merci pour ton explication détaillé.
Je vais prendre le temps de tester cette page afin de comprendre les conséquence de chaque Click.
Pour comprendre cette ligne httpLine[indexMessage++] = c; il faut la décomposer
httpLine est un tableau de caractères d'une certaine taille dans lequel on va ranger les uns après les autres les caractères que l'on reçoit sur la ligne Série. dans notre cas la variable c. Pour les mettre les uns après les autres il nous faut une variable qui va parcourir le tableau et se souvenir de l'endroit où l'on doit ranger le prochain caractère. J'utilise la variable indexMessage pour cela.
donc pour mettre c dans le tableau à la position indexMessage, on écrirait simplement
httpLine[indexMessage] = c;
ensuite il faut se souvenir qu'on a rempli cette case et donc passer à la case d'après. Donc après avoir stocké c au bon endroit on devrait faire un un truc du genre indexMessage = indexMessage + 1;
il existe dans le langage C une instruction ++ qui incrémente (ajoute 1 unité) directement une variable.
donc au lieu de faire indexMessage = indexMessage + 1;, je pourrais écrire indexMessage++; et mon code serait
httpLine[indexMessage] = c;
indexMessage++;
Il faut savoir que l'on peut mettre le ++ avant ou après le nom de la variable et ça ne fait pas tout à fait la même chose:
a = 3;
b = a++; // ici on met a dans b et ensuite on augmente a de 1, donc b vaut 3 et a vaut 4 après exécution
b = ++a; // ici on augmente a de 1 d'abord puis met a dans b ensuite, donc b vaut 4 et a vaut 4 après exécution
En tenant compte de cette information on voit que l'on peut faire donc tout d'un seul coup:
httpLine[indexMessage++] = c;
ça veut dire mettre c dans le tableau httpLine à la position indexMessage, puis ensuite augmenter de 1 indexMessage
OK ?
if (c == '\n' && currentLineIsBlank) ça c'est plutôt simple. l'opérateur && c'est le ET logique (si condition1 est vraie ET condition2 est vraie alors...). Ici on regarde donc si j'ai reçu un retour chariot et que la ligne précédente était vide (une variable que l'on maintient pendant qu'on reçoit les caratères) alors faire quelque chose --> la raison de cela est expliquée dans mon tuto, une requête HTTP se termine par une ligne vide, c'est comme cela que l'on repère la fin de la demande du navigateur.
OK?
char * ptrGET, *ptrEspace; ça déclare simplement 2 pointeurs sur des caractères;
l'objectif du code qui suit est d'extraire du tableau la partie qui concerne l'URL reçue. c'est ce code
if (ptrGET = strstr(httpLine, "GET")) {
// c'est la requête GET, la ligne continent "GET /URL HTTP/1.1", on extrait l'URL
ptrEspace = strstr(ptrGET + 4, " ");
*ptrEspace = '\0';
strncpy(urlRequest, ptrGET + 4, maxURL);
urlRequest[maxURL] = '\0'; // par précaution si URL trop longue
}
En supposant que la ligne contient bien "GET /ADMIN HTTP/1.1" on veut alors extraire /ADMIN pour s'en souvenir et le passer ensuite à la fonction d'affichage de la page web.
voici ce que fait ce code
(http://forum.arduino.cc/index.php?action=dlattach;topic=504431.0;attach=229461)
est clair ?
dire oui serait un mensonge.
J'ai compris le principe de ce qui est fait/demander mais c'est l'écriture (surtout si il y a des "raccourci") que je ne fait pas correspondre a l'action.
Il faut que j'y arrive, je vais y passer du temps, je revient vers vous si question précise.
Bonjour
Sujet très intéressant.
Djbouns, j'ai déjà fait ton arduino balling, et il fonctionne impec, jusqu'à présent aucun bug.
Est il possible d'avoir le code du nouveau projet?
Perso je partirai sur un esp32 (26 Digital IO et 12 Analog input pins)
Si tu veux un esp12, j'en ai 1, je peux te l'envoyer, c'est Kdo!
A+
Sacha
Bonsoir Sachab,
J'ai utilisé mes pompe de dosage pour le balling plusieurs année sans aucun problème, juste de l'usure et de la corrosion sur les partie "moteur" (a 5€ l'unité, rien d'anormal)
J'espère que ce nouveau projet seras aussi stable et efficace.
J'ai commander un esp32. (3€ plus chere qu'un esp12) je pense le recevoir mardi.
Pour le code il est partager ici dans le but de le faire évoluer et aboutir.
Il n'as volontairement pas été mis pour le moment sur recifal france.
Bonjour
Sujet très intéressant.
Djbouns, j'ai déjà fait ton arduino balling, et il fonctionne impec, jusqu'à présent aucun bug.
Merci :)
Voir la troisième ligne (http://www.recifal-france.fr/post267136.html#p267136) du code.
:) et oui :smiley-wink:
(https://image.noelshack.com/fichiers/2017/41/5/1507846247-sans-titre.png)
Je ne suis pas du genre a m'approprier le travaille des autres :)
arfff ... déjà 3 ans et demi ... :smiley-eek-blue:
Bon ...
A part me remémorer de vieux souvenir :)
....
J'ai (enfin) réussi a adapter le code de JML (celui des variables pour led) a ma connexion WIFI.
J'ai donc la page qui s'affiche correctement.
Je me lance dans l'expérience pour remplacer la page d'origine par la mienne avec les bouton correspondant.
1er remarque :
Les boutons sont en "location.href=" tout comme le code que j'ai reussi a faire fonctionner, par contre, le click rapide sur le bouton réagi beaucoup mieux que dans mon code (mais c'est pas encore sa) mais de façon aléatoire, les Click rapide finisse par générer une page "la page ne peut s'afficher". a voir si avec l'esp32 cela seras réduit.
2eme remarque :
Ma page étant assez grande, je doit descendre avec ma roulette de souris pour faire défiler les fonctions.
Des que je clik sur un bouton, je re bascule en haut de la page, se qui oblige a redescendre ... ect ...
Si besoin je mettrait le code, en cours de test, il est pas propre la :)
Problème rencontré a l'adaptation avec mon code pour modifier les variables correspondant aux haure de lever et coucher soleil (pwm led)
Je déclarais ces variable en hhmmss exemple 123000 pour 12h30.
il me parraisait compliquer de pouvoir modifier cette variable du fait quelle ne peut etre traiter en centiemme puisque il s'agit d'heure.
j'ai donc testé la chose suivante
long Hlever = 12;
int Mlever = 34;
long LEVER = ((Hlever*10000)+(Mlever*100)); // heure debut levé
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.print(Hlever);
Serial.print("H");
Serial.println(Mlever);
}
void loop() {
// put your main code here, to run repeatedly:
long LEVER = ((Hlever*10000)+(Mlever*100));
Serial.println(LEVER);
Mlever = Mlever +10;
delay (2000);
Serial.print(Hlever);
Serial.print("H");
Serial.println(Mlever);
Mlever = Mlever+10;
delay (2000);
Serial.print(Hlever);
Serial.print("H");
Serial.println(Mlever);
Mlever = Mlever+10;
delay (2000);
if (Mlever>=60){
Mlever = Mlever-60;
Hlever = Hlever+1;
}
Serial.print(Hlever);
Serial.print("H");
Serial.println(Mlever);
LEVER = ((Hlever*10000)+(Mlever*100));
Serial.println(LEVER);
delay (20000);
}
Y avait il une solution plus simple qui ne me faisait pas modifier mon code d'origine ?
je ne comprends pas la question
(généralement on code l'heure sous forme de secondes depuis le début de la journée 3600 x h + 60 x m + s) comme ça on peut ajouter ou soustraire pour calculer des durées plus simplement que dans l'autre représentation )
il me semble que vous avez une horloge RTC dans votre projet, vous pouvez donc aussi utiliser les classes DateTime et TimeSpan de la RTCLib (https://github.com/adafruit/RTClib) pour représenter le temps de manière compatible avec votre RTC
Dans mon code d'origine :
long LEVER = 100000; // heure debut levé
Pour :
if ((time >= LEVER) && (time < finLEVER)){
Maintenant que je veut modifier cette variable par le click d'un bouton, si :
variable = 125500 donc 12h50
Que le click d'un bouton ajoute 10 minute, je vais me retrouver a 126500 ...
et vu cette long variable il me paraissait compliquer de mettre un condition pour que si les 4 dernier chiffre égal ou au dessus de 6000 sa passe a 0000 et ajoute 10000.
vous pouvez donc aussi utiliser les classes DateTime et TimeSpan de la RTCLib (https://github.com/adafruit/RTClib) pour représenter le temps de manière compatible avec votre RTC
a ... connait pas ... je vais tout de suite voir sa
si au lieu de coder h*1000 + m*100 + s vous tenez compte réellement des secondes et faites h*3600 + m*60 + s le nombre obtenu est moins "compréhensible" pour les humains mais les maths sont ensuite super simples. ajouter 1h 10 minutes et 22 secondes c'est simplement ajouter 1 * 3600 + 10*60 + 22 par exemple
et les tests du genre if ((time >= LEVER) && (time < finLEVER)){
vont continuer à fonctionner. Faut juste vous assurer de coder le temps partout pareil.
- dans l'absolu j'utiliserai des unsigned long, le temps c'est positif
- si vous faites des macros, pensez au signe ul au bout des constantes pour forcer l'évaluation non pas en entier mais en unsigned long
unsigned long t = 12*3600 + 35*60 + 10; // 12h35 et 10 secondes (pas bon en entier!!!)
va bugger car le calcul numérique est effectué en int soit sur 2 octets signés par le pré-processeur et donc il faut l'écrireunsigned long t = 12*3600ul + 35*60 + 10; // 12h35 et 10 secondes
pour forcer le calcul en unsigned long
Ok cela va fonctionner pour faire varier le programme mais après impossible(difficile) de pouvoir afficher l'heure modifier puisque elle est devenu un nombre du genre 46530 (12h55 30s)
Bonjour,
heure=t/3600;
minute=(t%3600)/60;
seconde=t%60;
Non c'est facile - en calcul entier:
(46530 / 3600) = les heures
(46530 / 60) % 60 = les minutes
(46530 % 60) = les secondes
EDIT: grillé par kamill :)
on n'a pas la même formule pour les minutes. le résultat et le même
Arf ... sa marche ...
Vous avez réponse a tout ...
C'est agacent :) :) :)
Je commence a comprendre ce que ressente mes alternant BTS que j'ai dans mon équipe au boulo ... :smiley-razz:
jmet tout sa en forme ... j'ai pris ma journée, j'espère avoir bien avancer ce soir.
Arf ... sa marche ...
Vous avez réponse a tout ...
C'est agacent :) :) :)
Je commence a comprendre ce que ressente mes alternant BTS que j'ai dans mon équipe au boulo ... :smiley-razz:
:) :smiley-mr-green:
Donc, pour ne pas avoir a modifier tout mon code d'origine if ((time >= LEVER) && (time < finLEVER)){
(car foncion "time" lit hhmmss) et les fonctions j'ai fait ca :
unsigned long LEVERV = 36000; // 10h00 00
unsigned long LEVER;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
}
void loop() {
// put your main code here, to run repeatedly:
LEVERV = LEVERV +40*60 ; // +40 min
LEVER = ((LEVERV/3600*10000ul)+(((LEVERV%3600)/60)*100ul)+(LEVERV%60));
Serial.print(LEVER);
Serial.println();
delay (5000);
}
Si sa vous paret correct jme lance dans toute la modif sur mon code principal
et une autre petit question,
Il faut que je fixe des condition pour certaine variable (par exemple pompe a 100% pour ne pas passer a 101%)
Cela pose t'il problème si elle sont juste après l'action du bouton ?
case 4:
puissanceblanc += labelValue;
if (puissanceblanc >100){
puissanceblanc = 100;
}
else if (puissanceblanc <0){
puissanceblanc = 0;
}
break;
Et encore une :)
Certain bouton doivent modifier des variable de 0.1
Visiblement ça ne marche pas.
du au " long int labelValue;" ?
J'ai tenté de le remplacer par un double mais sans succès non plus
préférez l'utilisation de fonctions plutôt que de coder les trucs en dur partout dans le code.
unsigned long LEVERV = 36000ul; // 10 h00 00
void printTime(unsigned long t)
{
unsigned int h, m, s;
h = t / 3600;
m = (t / 60) % 60;
s = t % 60;
Serial.print(h);
Serial.print(F(":"));
if (m < 10) Serial.print(F("0"));
Serial.print(m);
Serial.print(F(":"));
if (s < 10) Serial.print(F("0"));
Serial.println(s);
}
void setup() {
Serial.begin(115200);
printTime(LEVERV);
}
void loop() {
delay (1000);
LEVERV += 40 * 60 ; // +40 min
printTime(LEVERV);
}
attention bien sûr au débordement, rien dans ce code ne vérifie que vous allez au delà de minuit
Cela pose t'il problème si elle sont juste après l'action du bouton ?
Non, je ne vois pas pourquoi ce serait un soucis
Certain bouton doivent modifier des variable de 0.1
un long c'est un nombre entier, donc ajouter 0.1 ne change rien...
je suppose que ce n'est pas pour le temps? si quelque chose varie de 0.1 en 0.1 alors il faut stocker cela dans un float ou un double (même représentation sur 4 octets sur un MEGA). Ensuite quand vous afficher la valeur il faut vous assurer de ne pas uniquement représenter la valeur entière.
JML, j'ai vu que vous utiliser souvent le void
Quelle différence avec un *****.h ?
void ça veut dire que la fonction en renvoie rien.
un .h c'est souvent quelque chose qui contient une collection de variables et définitions de fonctions ou macros que l'on veut incorporer dans son projet pour que le compilateur sache compiler un fichier donné
J'ai justement changer pour un double
du au " long int labelValue;" ?
J'ai tenté de le remplacer par un double mais sans succès non plus
Mes variable initial sont bien en float.
ah vous faites référence au code de mon autre tuto, c'est ça ?
de mémoire je crois que le code lit les valeurs passées dans l'URL avec un atoi() qui convertit un texte ASCII en nombre entier. il ne faut donc pas utiliser cette fonction pour lire un nombre flottant mais atof()
float x;
const char * valeurTxt = "123.4";
void setup() {
Serial.begin(115200);
x = atof(valeurTxt);
Serial.println(x, 4);
x += 0.1;
Serial.println(x, 4);
x += 0.1;
Serial.println(x, 4);
}
void loop() {}
ah vous faites référence au code de mon autre tuto, c'est ça ?
euh ... ba oui, vous étes un peu ma base de travaille lol
mon maître :smiley-wink:
Quote from: J-M-L on Today at 11:57 am
en renvoie rien.
? :o
une fonction en C ou C++ peut retourner une valeur ou rien du tout. quand elle ne retourne rien, on déclare son type de retour comme étant
voidvoid printTime(unsigned long t) {...} --> la fonction
printTime ne renvoie aucune valeur
int ajouter(int a, int b) {return a+b;} --> la fonction
ajouter renvoie un entier (la somme des 2 param)
A ...
En gros c'est une fonction, qui non utilisé ne génére rien (je exprime mal)
Dans le cas pressent, se void est une opération mathématique. si elle n'est pas appeler a la suite d'un numérique elle ne génèreras aucune valeur.
je vais finir par avoir une image :)
ah vous faites référence au code de mon autre tuto, c'est ça ?
de mémoire je crois que le code lit les valeurs passées dans l'URL avec un atoi()
Je n'ai trouvé aucune ligne "atoi"
Donc je ne sait pas ou remplacer par :
float x;
const char * valeurTxt = "123.4";
void setup() {
Serial.begin(115200);
x = atof(valeurTxt);
Serial.println(x, 4);
x += 0.1;
Serial.println(x, 4);
x += 0.1;
Serial.println(x, 4);
}
void loop() {}
Je n'ai trouvé aucune ligne "atoi"
Donc je ne sait pas ou remplacer par :
ah, je devais le faire en
long avec atol() au lieu de atoi()
labelValue = atol(item);
ah, je devais le faire en long avec atol() au lieu de atoi()
labelValue = atol(item);
Bingo !
labelValue = atol(item);
Qui est dans un void parseCommand()
A ...
En gros c'est une fonction, qui non utilisé ne génére rien (je exprime mal)
Dans le cas pressent, ce void est une opération mathématique. si elle n'est pas appeler a la suite d'un numérique elle ne génèreras aucune valeur.
vous voyez ce qu'est une fonction ?
void n'est pas un raccourci de langage pour dire une fonction.
void c'est un mot clé du langage de programmation
Il a différents usages:
- Quand il est utilisé comme type retourné par la fonction, ça veut dire que la fonction ne retourne pas de valeur.
void maFonction(int x) {...} est une fonction qui ne retourne aucune valeur et qui prend un paramètre de type entier appelé x dans le corps de la fonction. --> par exemple la fonction
pinMode(pin, mode) ne retourne rien
- Quand il est utilisé pour la liste des paramètres d'une fonction, void spécifie que la fonction ne prend aucun paramètre (mais peut être ignoré).
unsigned long maFonction(void) {...} est une fonction qui ne retourne un unsigned long et ne prend aucun param (on l'écrira souvent
void maFonction() {...} tout simplement)--> par exemple la fonction
millis()- Quand il est utilisé dans la déclaration d'un pointeur, void spécifie que le pointeur est « universel »,
void * ptr; // ptr est un pointeur générique qui peut pointer vers tout type de données, le compilateur ne fait aucune vérification de cohérence
oui, je crois, c'est un peu comme un presse a billet qui attend son papier ... pas de papier, pas de resultat ...
:smiley-money: :smiley-money: :smiley-money:
oui, je crois, c'est un peu comme un presse a billet qui attend son papier ... pas de papier, pas de resultat ...
:smiley-money: :smiley-money: :smiley-money:
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
vous aurez l'image après 10 bons points :)
:smiley-cry: :smiley-cry:
c'est le premier de toute ma vie :smiley-cry:
YESSS
Le changement des variables avec décimal = OK
Jcroc un bout et jmi remet
Bonne appétit a vous aussi
Bonjour
Désolé Bricoleau pour avoir attribué ton travail à Djouns.
Dans les 2 cas, merci à vous deux pour ce que vous faites.
A+
Sa commence a prendre forme.
Je n'arrive pas a modifier les variable correspondant au horraire.
Exemple bonton ci dessous qui doit diminuer la variable de -600 (10min)
client.print(F("<button onclick=\"location.href='/LEVER=-600'\" type='button'class=button>-10</button> "));
Avez vous une idee du pourquoi ?
Les autre fonction fonctionne :)
je vous ai mis un petit mot perso, lisez le.
ça m'étonnerait que tout fonctionne. Voici vos variables que vous recherchez dans l'URL
const char * labelsOfInterest[] = {
"tempbacvent", "tempbacmax", "temprampemax", "consigneph",
"puissanceblanc", "puissancebleu", "puissancejebao1", "puissancejebao2",
"puissancejebao3", "angle1oscillo1", "angle2oscillo1", "angle1oscillo2",
"angle2oscillo2", "angle1oscillo3", "angle2oscillo3", "delaisnouritureminute",
"delaistempetteminute", "nourissage1", "nourissage2", "LEVER",
"COUCHER"
};
l'ordre de déclaration est important puisque labelIndex va nous dire lequel a été trouvé
Cependant dans le switch/case vous faites
switch (labelIndex) {
....
case 15: // W
LEVER += labelValue;
break;
case 16: // W
COUCHER += labelValue;
break;
}
Le soucis est là: LEVER et COUCHER ne sont pas à l'entrée 15 et 16 du tableau labelsOfInterest mais en 19 et 20... faut revoir le switch/case il doit y en avoir d'autres de faux
Vous devriez aussi changer le nom des fonction printTime() car elle n'imprime rien mais plutôt calculent les éléments de temps dans des variables globales (pas top).
Ce serait plus cohérent soit de passer client en paramètre de la fonction et l'écrire
void printTime(unsigned long t, WiFiEspClient& client)
{
h = t / 3600;
m = (t / 60) % 60;
s = t % 60;
client.print(h);
client.print(F(":"));
if (m < 10) client.print(F("0"));
client.print(m);
}
comme cela ça l'imprime vraiment. vous l'appelez en faisant printTime(LEVER, client);
(et vous enlevez bien sûr le code de la partie appelante qui écrit l'heure)
ou alors appelez la fonction updateHMS() par exemple pour dire qu'elle met simplement à jour les variables globales
void updateHMS(unsigned long t)
{
h = t / 3600;
m = (t / 60) % 60;
s = t % 60;
}
la première approche est plus propre et vous n'avez pas vraiment besoin d'avoir h,m, s comme variables globales (à moins que ça ne vous serve ailleurs ? )
Le soucis est là: LEVER et COUCHER ne sont pas à l'entrée 15 et 16 du tableau labelsOfInterest mais en 19 et 20... faut revoir le switch/case il doit y en avoir d'autres de faux
Pfff c'est a force d'y trifouillé ... :(
C'est pourtant si simple bref, c'est corrigé.
Ce serait plus cohérent soit de passer client en paramètre de la fonction et l'écrire
C'est la première chose que j'ai voulu faire mais des que j'ai remplacé Serial par client il me disait, client est pas déclaré ...)
Il me manquait le , WiFiEspClient& client)
Pour h m s en variable global, Il le sont dans mon code global, il recuper l'heure du ds1307.
Ma page étant assez grande, je doit descendre avec ma roulette de souris pour faire défiler les fonctions.
Des que je clik sur un bouton, je re bascule en haut de la page, se qui oblige a redescendre ... ect ...
Y a t'il un solution pour rafraîchir en restant a la même hauteur de page ?
Pfff c'est a force d'y trifouillé ... :(
C'est pourtant si simple bref, c'est corrigé.
une bonne technique pour ne pas vous tromper est d'utiliser une énumération.
une fois votre tableau défini vous copier le contenu, et le collez dans un éditeur de texte et remplacez les guillemets par '_' puis vous vous servez de cela pour faire un enum
const char * labelsOfInterest[] = {
"tempbacvent", "tempbacmax", "temprampemax", "consigneph",
"puissanceblanc", "puissancebleu", "puissancejebao1", "puissancejebao2",
"puissancejebao3", "angle1oscillo1", "angle2oscillo1", "angle1oscillo2",
"angle2oscillo2", "angle1oscillo3", "angle2oscillo3", "delaisnouritureminute",
"delaistempetteminute", "nourissage1", "nourissage2", "LEVER", "COUCHER"
};
vous aide alors à créer cela:
enum : byte {
_tempbacvent_, _tempbacmax_, _temprampemax_, _consigneph_,
_puissanceblanc_, _puissancebleu_, _puissancejebao1_, _puissancejebao2_,
_puissancejebao3_, _angle1oscillo1_, _angle2oscillo1_, _angle1oscillo2_,
_angle2oscillo2_, _angle1oscillo3_, _angle2oscillo3_, _delaisnouritureminute_,
_delaistempetteminute_, _nourissage1_, _nourissage2_, _LEVER_, _COUCHER_
};
ensuite c'est simple au lieu de vous embêter à compter dans quel ordre ils sont vous pouvez faire un switch avec les petits noms car le premier élément d'un enum vaut 0 donc vous avez le bon ordre
switch (labelIndex) {
case _tempbacvent_ :
break;
case _tempbacmax_ :
break;
case _temprampemax_ :
break;
case _consigneph_ :
break;
case _puissanceblanc_ :
break;
case _puissancebleu_ :
break;
case _puissancejebao1_ :
break;
case _puissancejebao2_ :
break;
case _puissancejebao3_ :
break;
case _angle1oscillo1_ :
break;
case _angle2oscillo1_ :
break;
case _angle1oscillo2_ :
break;
case _angle2oscillo2_ :
break;
case _angle1oscillo3_ :
break;
case _angle2oscillo3_ :
break;
case _delaisnouritureminute_ :
break;
case _delaistempetteminute_ :
break;
case _nourissage1_ :
break;
case _nourissage2_ :
break;
case _LEVER_ :
break;
case _COUCHER_ :
break;
}
en plus comme cela c'est bcp plus lisible et vous éviter le problème d'avoir un numéro qui ne correspond pas
Y a t'il un solution pour rafraîchir en restant a la même hauteur de page ?
pas simplement non... pour le navigateur web vous avez tout rechargé, c'est une nouvelle page...
dans l'absolu il faudrait essayer de travailler la page HTML pour avoir une représentation un peu plus "tableau de bord" comme ça il n'y aurait pas besoin de scroller
une bonne technique pour ne pas vous tromper est d'utiliser une énumération.
effectivement
Mais du coup il n'y a plus :
const char * labelsOfInterest[]
si si il faut les conserver pour que la fonction d'analyse fonctionne
je m'en doutait m'ai c'est la ligne const char * labelsOfInterest[] qui etait devant que je n'arrive pas a re placer ...
avant, non, apres non, sur la meme ligne non ... >:(
vous devez avoir cela:
const char * labelsOfInterest[] = {
"tempbacvent", "tempbacmax", "temprampemax", "consigneph",
"puissanceblanc", "puissancebleu", "puissancejebao1", "puissancejebao2",
"puissancejebao3", "angle1oscillo1", "angle2oscillo1", "angle1oscillo2",
"angle2oscillo2", "angle1oscillo3", "angle2oscillo3", "delaisnouritureminute",
"delaistempetteminute", "nourissage1", "nourissage2", "LEVER", "COUCHER"
};
enum : byte {
_tempbacvent_, _tempbacmax_, _temprampemax_, _consigneph_,
_puissanceblanc_, _puissancebleu_, _puissancejebao1_, _puissancejebao2_,
_puissancejebao3_, _angle1oscillo1_, _angle2oscillo1_, _angle1oscillo2_,
_angle2oscillo2_, _angle1oscillo3_, _angle2oscillo3_, _delaisnouritureminute_,
_delaistempetteminute_, _nourissage1_, _nourissage2_, _LEVER_, _COUCHER_
};
a ok le tableau est en plus ...
Je l'avait remplacé ...
Merci
Chui sur la page HTML, sa va tenir sur une page, dans un tableau.
J'ai l'impression que l'affichage est plus long (ou c'est pars que je ne voyant pas la fin de la page avant ?)
vous avez sans doute un peu plus de HTML à envoyer... mais ça ne devrait pas être significatif....
en réfléchissant à votre projet je me dis que si j'étais vous (je sais ça complique un peu) je prendrai un Wemos D1 complet ou similaire qui ne servirait qu'à faire l'interface web.
Le Wemos communiquerait par le port série potentiellement avec votre arduino dans un langage de commande que vous pourriez définir (en gros exporter des variables ou les lire). Ce serait bien plus efficace pour la partie web. c'est un puce qui a bcp plus de mémoire dans laquelle vous pourrez stocker votre page web sans soucis (4M bytes de mémoire flash !) -, il tourne à 80MHz au lieu de 16MHz de votre arduino, intègre directement à travers l'API web la communication wifi sans passer par les commandes AT et vous le programmez aussi avec le langage arduino.
J'ai vu cette solution (puisque les esp12 ou esp32 peuve être programmer directement et gère des sortie numérique.
Sa soulagerais l'arduino.
Comme je vous l'ai dit au paravent, les idée ne me manque pas ... c'est les connaissance pour y arrivé qui ne me font pas prendre ce chemin ...
Je reçoit mardi mon esp32 qui, il me semble tourne a 240 MHz en double core, 520 KB SRAM, 16 MB flash.
j'aurais donc la possibilité de faire évoluer mon projet.
De la manière dont vous m'épauler (j'ai un réacteur d'A380 au fion :smiley-zipper: ) je me dit que tout est possible :)
comment comptez vous l'utiliser ?
il n'a pas les commandes AT donc ne va pas fonctionner comme votre ESP-01
y'a des trucs qui existent (https://github.com/espressif/esp32-at) mais ça va vous compliquer la vie
Pourtant j'ai vu ca :
ici (https://pcbreflux.blogspot.fr/2017/02/esp32-playing-with-esp32-at-commands.html)
et sur le datasheet il y a des pin rx/tx
datasheet (http://espressif.com/en/support/download/documents?keys=&field_type_tid%5B%5D=54)
Photo page 9
ma page (https://img15.hostingpics.net/pics/746829Sanstitre.png) donne sa.
Vous remarquerez l'arrivé d'un bouton rouge ...
Nous y voici, nous y voila ...
case 23: // W
valider += labelValue;
// y faut tout sauvegarder :)
// c une histoire d'epprom ...
// Une gros dossier totalement inconnu :(
// on va faire dodo et verras sa demain
// Encore merci JML pour ton aide qui m'as permis de bien bien avancer aujourd'hui.
valider = 0;
break;
;)
Pour l'esp il faudra vérifier quand vous le recevez quel OS est installé dessus et mettre si nécessaire celui des commandes AT
Ok, pour 3 € de différence avec esp12, vu les performance sur la papier ...
Je croise led doigt pour que tout fonctionne simplement et plus efficacement.
Le successeur de l'ESP8266 qui est dans les Wemos mini par exemple est l'ESP32 qui a été lancé en septembre 2016 - il y a un an environ donc. Au début il était cher mais avec des produits basés sur le module ESP-WROOM-32 (http://www.iotsharing.com/2017/05/introduction-to-esp32.html) c'est devenu compétitif
ils sont livrés généralement avec FreeRTOS nu et vous pouvez installer au dessus une "couche arduino (https://github.com/espressif/arduino-esp32)" - je n'ai jamais essayé d'installer un langage de commande AT
Vous avez pris quoi finalement comme esp32? Le wemos Lolin 32 (https://wiki.wemos.cc/products:lolin32:lolin32) ou une chinoiserie un peu moins connue (https://www.amazon.fr/Hrph-Development-Bluetooth-antenne-Ultra-Low/dp/B0717759B7/ref=sr_1_2) ? Le Lolin n'a pas vraiment de documentation pour le moment (essayez de cliquer sur les liens (https://wiki.wemos.cc/tutorials:lolin32) pour voir)
J'ai pris celui ci (https://www.amazon.fr/gp/product/B06Y6HXXY1/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1)
Je n'est pas trouvé de doc précise non plus pour celui ci mais juste pour la "puce"
OK faut parler chinois pour celui là. (https://item.taobao.com/item.htm?&id=542459608596) mais il y a un gitHub associé en anglais (https://github.com/SmartArduino/SZDOITWiKi/wiki/ESP8266---ESP32) mentionné dans la doc en chinois - faudra sans doute le flasher avec autre chose que son OS de base...
faudra sans doute le flasher avec autre chose que son OS de base...
Encore une mission ...
J'ai deja un esp12 que je n'arrive pas a uploader le firmeware ...
J'espère que ce seras plus simple.
oui c'est un peu galère, faut prendre le coup :)
perso je suis sur Mac, je le fais par le script python esptool (https://github.com/espressif/esptool) qui supporte aussi des ESP32
- faut s'assurer d'avoir un adaptateur genre FTDI qui sorte bien du 3.3V ou faire les adaptation de tension 5V <-> 3.3V
- faut mettre la puce en mode "flash" donc
GPIO 0: LOW
GPIO 2: HIGH (3.3V)
GPIO 15: LOW
alimenté avec une bonne source de courant (pas à travers le FTDI) 3.3V and GND
Oui c'est bien se que je fait, comme sur les post du net ... bref ... j'espere que tout se passeras bien avec l'esp 32 ...
Je cherche sur le net pour comprendre l'utilisation de l'epprom.
Une chose ma chagrine
Syntaxe : EEPROM.write(adresse, valeur)
Adresse : place dans laquelle vous voulez enregistrer la mémoire. Pour l'ATmega168, entre 0 et 511
Valeur : entre 0 et 255
J'ai des variable de avec des nombres de plusieurs milliers ... y a forcement un astuce
Le EEPROM.PUT() ??
sa marche :) :) :)
J'ai fait un premier code avec
#include <EEPROM.h>
/** the current address in the EEPROM (i.e. which byte we're going to write to next) **/
int addr = 123;
int val = 456;
void setup() {
EEPROM.put(1, addr);
EEPROM.put(10, val);
Puis un second avec
#include <EEPROM.h>
// start reading from the first byte (address 0) of the EEPROM
int addr = 0;
int val = 0;
void setup() {
// initialize serial and wait for port to open:
Serial.begin(9600);
EEPROM.get(1, addr);
EEPROM.get(10, val);
}
void loop() {
// read a byte from the current address of the EEPROM
Serial.print(addr);
Serial.println();
Serial.print(val);
Serial.println();
Et j'ai bien mes valeur 123 et 456 qui sont afficher sur le moniteur serie
Je suis etoner que sa est ete si simple ::)
Trop beau pour etre vrai ... :smiley-roll-sweat:
je vais attendre votre confirmation avant de l'intégrer a mon bouton sauvegarde ...
Et si vous pouvez me confirmer, l'adresse que je donne a l'epprom doit prendre en compte le nombre de bytes enregistrer avant ? exemple, a l'adresse 10 je stock un int, le prochaine adresse a utiliser est 12 (int = 2 bytes) ?
Avec un peu de chance je vais avoir mon 2eme bon point :smiley-cool:
Oui tout à fait chaque case contient 1 octet et ça fonctionne tout seul. se souvenir cependant qu'on n'a que 100,000 écritures possibles et donc à utiliser à bon escient, pas dans un boucle qui tourne à fond
Pour savoir de combien d'octets il faut sauter, vous pouvez utiliser sizeof(votreVariable)
HSon // rupture de bon point // HSoff
J'ai l'intention de mettre EEPROM.get juste après la déclaration de met variable, comme cela, ce sont les dernière variable enregistré qui seront pris en compte.
Le EEPROM.put seras utilisé 1 fois dans le code, lorsque l'on sauvegarde les variable modifier sur la page internet. ( j'ai vu que cette fonction n'enregistrait que si la variable avait été modifier)
De ce fait, il y auras très très peu d'écriture (la lecture ne rentre pas dans l'usure de ce composant si j'ai bien tout lu)
Aller j'inclue tout ça ... Jsuis content, jpensait que c'était une étape galere de OUF ... j'ai presque réussi tout seul :)
Je suis en train de muté en JML ... (peut être pas quand même lol)
Quand on bosse ça se voit :)
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
YESSSS
et de deux lol
:)
Bon, ça fonctionne ... mis a part au premier démarrage, ça a été galère.
Vu que je demande de lire les variable sur l'epprom et qu'il n'y en a pas au premier démarrage = erreur sur la page et donc même si on sauvegarde, l'epprom sauvegarde les erreur :)
Donc obliger de faire un code sans la partie lecture de l'epprom pour pouvoir sauvegarder et après remettre la lecture et c'est bon ...
Pour les prochaine fois j'ai penser faire un boolean sauvegarder dans l'eeprom pour que si il n'y a pas eu de sauvegarde l'eeprom ne lise pas.
Il y a peut être une solution de prévu ?
La partie page de commande étant opérationnel, je me retourne vers le code générant 2 page (page "d'accueil" et page "admin") il faut que je combine les deux page ...
Pour traiter le cas de savoir si oui ou non l'EEPROM a été initialiséé généralement on écrit à une adresse connue de l'EEPROM une sorte de message qui a peu de chance d'être là par hasard, sur 4 octets consécutifs par exemple. ça a donné des idées sur l'usage de codes rigolos en hexadécimal du genre 0xDEADBEEF (dead beef) ou encore 0x8BADF00D (ate bad food) ou 0xBAADF00D (bad food), 0xBAADCAFE (bad café)
Bref dans le startup() vous allez lire à l'adresse connue, si vous trouvez votre petit message vous savez que les valeurs des paramètres en EEPROM sont bons sinon vous y écrivez des valeurs par défaut raisonnables et le mot clé magique à l'adresse connue
Bref dans le startup() vous allez lire à l'adresse connue, si vous trouvez votre petit message vous savez que les valeurs des paramètres en EEPROM sont bons sinon vous y écrivez des valeurs par défaut raisonnables.
Donc cette "phrase" doit être ajouter lorsque je fait une sauvegarde dans l'eeprom avec les autre paramètre sinon je lirait toujours les valeur par default.
Voici un bout de code d'exemple
#include <EEPROM.h>
const uint32_t motClef = 0xDEADBEEF;
const uint16_t adresseMotClef = 0x00;
const byte tailleMax = 50;
// __attribute__ ((packed)) permet de forcer la conservation de l'ordre des champs
// c'est utile pour éviter que le compilateur mette le bazar quand on en rajoute au fil du temps
struct __attribute__ ((packed)) _paramS {
int8_t minimumT; // -128 à 127°
int8_t maximumT; // -128 à 127°
char ville[tailleMax + 1]; // la ville d'installation
} lesParametres;
const uint16_t adresseDesParametres = adresseMotClef + sizeof(motClef);
// *************************
char messageRecu[tailleMax + 1]; // +1 pour stocker un '\0' à la fin de la chaîne
byte index;
boolean recevoirMessage(const char finDeChaine)
{
boolean fin = false;
if (Serial.available()) { // si on a au moins un charactère en attente
int recu = Serial.read(); // -1 si erreur, sinon notre charactère
if (recu != -1) {
if (recu == finDeChaine) fin = true;
else {
if (recu != '\r') { // on ignore CR
messageRecu[index++] = (char) recu;
messageRecu[index] = '\0'; // on marque la fin de chaîne
if (index >= tailleMax) index = tailleMax - 1; // évite de déborder
}
}
}
}
return fin;
}
void printParams()
{
Serial.println(F("\n************* PARAMS *************"));
Serial.print(F("Temp min =\t")); Serial.println(lesParametres.minimumT);
Serial.print(F("Temp max =\t")); Serial.println(lesParametres.maximumT);
Serial.print(F("Ville =\t")); Serial.println(lesParametres.ville);
}
void etablirValeursParDefaut()
{
uint32_t lectureMotClef;
Serial.print(F("Mot Clef = 0x")); Serial.println(motClef, HEX);
Serial.print(F("Adresse Mot Clef = 0x")); Serial.println(adresseMotClef, HEX);
Serial.print(F("Adresse Params = 0x")); Serial.println(adresseDesParametres, HEX);
EEPROM.get(adresseMotClef, lectureMotClef);
if (lectureMotClef == motClef) {
// la mémoire a déjà été initialisée, on peut charger les paramètres
Serial.println("params en memoire = OK");
EEPROM.get(adresseDesParametres, lesParametres);
} else {
// la mémoire n'avait pas encore été initialisée
Serial.println("params en memoire = pas OK");
lesParametres.minimumT = -22;
lesParametres.maximumT = 44;
strcpy(lesParametres.ville, "une ville inconnue");
EEPROM.put(adresseDesParametres, lesParametres);
EEPROM.put(adresseMotClef, motClef);
}
printParams();
}
void setup() {
Serial.begin(115200);
Serial.println(F("\n\n-------- DEMO --------\n"));
etablirValeursParDefaut();
// pas nécessaire car le compilateur le fait pour nous avec des variables globales
// juste pour vous rappeller qu'il est bon de définir les bonnes conditions initiales
index = 0;
messageRecu[0] = '\0';
Serial.println(F("\nEntrez le nom de la ville ou reset"));
}
void loop() {
if (recevoirMessage('\n')) {
// on a reçu le message met à jour l'EEPROM
// à titre de démo, si l'entrée contient "reset", on efface le mot clé
if (!strcmp(messageRecu, "reset")) {
EEPROM.put(adresseMotClef, 0xABADCAFE); // mauvais mot clé - au reboot les params seront rétablis au défaut
Serial.println(F("Reset mot clef. rebootez l'Arduino pour voir"));
} else {
memset(lesParametres.ville, '\0', tailleMax + 1); // on met tous les octets à 0
strcpy(lesParametres.ville, messageRecu); // on stocked le nom de la ville
EEPROM.put(adresseDesParametres, lesParametres); // on remet tout en mémoire
printParams(); // on affiche les params
// On re-initalise notre buffer
index = 0;
messageRecu[0] = '\0';
}
}
}
la première fois que vous le lancez vous allez voir cela sur la console (réglée à 115200, avec envoi de CR/LF)
-------- DEMO --------
Mot Clef = 0xDEADBEEF
Adresse Mot Clef = 0x0
Adresse Params = 0x4
params en memoire = pas OK
************* PARAMS *************
Temp min = -22
Temp max = 44
Ville = une ville inconnue
Entrez le nom de la ville ou reset
le code a detecté que votre carte n'avait pas le bon mot clé (cf la partie en rouge) et a donc mis les paramètres par défaut et établi un environnement correct.
Vous voyez que la ville est inconnue.
Tapez dans la console "Toulouse" par exemple et validez
la console va alors afficher************* PARAMS *************
Temp min = -22
Temp max = 44
Ville = Toulouse
On a changé les paramètres et on le voit. Pour s'assurer que c'est bien en mémoire maintenant, fermez la console série rebootez l'arduino. (ou l'ouverture de la console le reboot)
Vous verrez alors s'afficher
-------- DEMO --------
Mot Clef = 0xDEADBEEF
Adresse Mot Clef = 0x0
Adresse Params = 0x4
params en memoire = OK
************* PARAMS *************
Temp min = -22
Temp max = 44
Ville = Toulouse
Entrez le nom de la ville ou reset
On voit donc que le mot clé a été reconnu et les paramètres bien initialisés non pas aux valeurs par défaut mais avec le contenu de la mémoire (on voit bien qu'il s'est souvenu de Toulouse).
Maintenant tapez reset dans la console et validez.
si vous regardez le code, j'ai fait un petit test quand on reçoit la ligne entrée qui vérifie si c'est ce mot qu'on a tapé et si oui efface le mot clé en mémoire (le remplace par une valeur fausse)
la console va vous dire
Reset mot clef. rebootez l'Arduino pour voir
effacez la console, fermez là. rouvrez là en rebootant votre arduino et là vous verrez que la console affiche
-------- DEMO --------
Mot Clef = 0xDEADBEEF
Adresse Mot Clef = 0x0
Adresse Params = 0x4
params en memoire = pas OK
************* PARAMS *************
Temp min = -22
Temp max = 44
Ville = une ville inconnue
Entrez le nom de la ville ou reset
--> comme le code n'a pas trouvé le mot clé, il a décidé que la mémoire n'était pas bonne et a remis des paramètres par défaut. Toulouse a été effacé.
--> bref ce n'est pas compliqué
Notez au passage:
- le mot clé est rangé en mémoire AVANT les données
- je mets tous mes paramètres globaux que je dois mémoriser dans une structure. ça permet de simplifier fortement l'écriture de la sauvegarde, on écrit directement la structure en EEPROM.
ça va aussi simplifier l'évolution du code pendant qu'on développe et améliore le projet si on a d'autres paramètres à mémoriser
- en utilisant __attribute__ ((packed)) dans la déclaration de la structure, vous évitez les optimisations du compilateur et les champs en mémoire seront bien déclarés dans cet ordre.
--> si vous avez besoin de nouveaux paramètres, mettez les à LA FIN de la structure.
--> Au prochain lancement il y aurait lecture des params existants et du bazar pour les reste. Pour traiter cela généralement on a aussi en EEPROM, comme premier élément des paramètres, un numéro de version. Au boot, Si vous voyez le motClef, vous savez qu'une partie de la mémoire est déjà initialisé. Vous lisez alors le No de version. Si elle ne correspond pas à la version actuelle de votre logiciel, alors en fonction de la version vous chargez les paramètres existants, établissez des valeurs par défaut pour les nouveaux et restockez le tout en mettant à jour le numéro de version. --> ça donne un truc d'évolutif.
OK?
oui sa marche
Mais je coince pour adapter sur le programme "double page"
si j'ai bien compris c'est if (!strcmp qui lit l'adresse GET
Mais dans le code pour gerer les bouton il est utilisé:
if (!strcmp(labelsOfInterest, item)) {
alors quand dans le code double page il est utilisé :
(!strcmp(urlRequest, "/admin"
il faudrait donc que le :
if (!strcmp(labelsOfInterest, item)) { soi t plutot en :
if (!strcmp "/admin"(labelsOfInterest, item)) {
?
strcmp() (http://en.cppreference.com/w/cpp/string/byte/strcmp) est une fonction de la librairie standard qui compare 2 chaînes de caractères (c-strings)
Donc dans un cas on l'utilise pour voir si ce que l'on a reçu c'est «GET »
Si c'est un GET je sais que j'ai derrière une URL que je vais extraire
L'URL elle même va contenir une affectation de variable et donc dans l'autre cas j'utilise strcmp() pour chercher dans le tableau des noms de variables laquelle on a reçu
On pourrait dire que admin est une variable mais si vous la trouvez alors derrière il ne faut pas essayer de lire une valeur
donc je fait
(!strcmp(urlRequest, "/admin" et ajoute ensuite
if (!strcmp(labelsOfInterest, item)) {
c'est bien ça
Par contre les bouton ramène a ip/** au lieu de ip/admin/**
dit rien ... je vais trouver
vous êtes dans l'intégration dans votre gros projet ou toujours dans un "petit" projet à part?
si dans le second cas, pouvez vous poster le code de là où vous en êtes?
je suis dans le projet "moyen" lol
J'essais d'adapter le projet "page de programmation" au programme "double page"
Je vous envoie
j'ai trouver pour l'url des bouton mais du coup, plus d'action ...
Je vais trouver ...
Y doit y avoir des "conflit" car l'url "admin" ne fonctionne pas toujours ...
des fois on reste sur la page principal ou vis versa ...
Pour les boutons, je n'est pas trouvé, je pence que ca tourne autour de ca et qu'il faut ajouter /admin ...
Mais ou ... ?
int commandLength = strlen(urlRequest);
item = strtok(urlRequest, "=");
while (item) {
labelFound = false;
for (int i = 0; i < maxLabelsOfInterest; ++i) {
if (!strcmp(labelsOfInterest[i], item)) {
...
J'ai quand meme reussi a se que les bouton donne
http://192.168.1.52/admin/nourissage2=600
votre code contient
if (strlen(urlRequest) != 0) parseCommand();{
// une requête HTTP se termine par une ligne vide
envoyerPageWeb(client, urlRequest);
break;
}
il y a ne paire de {} qui ne sert à rien
if (c == '\n' && currentLineIsBlank) {
// une requête HTTP se termine par une ligne vide
if (strlen(urlRequest) != 0) parseCommand();
envoyerPageWeb(client, urlRequest);
break;
}
quand vous arrivez là, on a reçu l'intégralité de la requête HTTP et l'URL est dans la variable urlRequest
Elle peut être / par exemple ou si j'ai bien compris ce que vous voulez faire /admin/puissanceblanc=-5.
Dans les 2 cas vous appelez parseCommand() qui commence par faire un
item = strtok(urlRequest, "=");
ça initialise item sur le début de urlRequest et remplace le premier "=" trouvé par un caractère nul.
et ensuite dans la boucle while vous faites un test pour comparer avec vos labelsOfInterest
if (!strcmp(labelsOfInterest[i], item)) {
dans le premier cas /, comme il n'y a pas de '=' item est NULL et vous n'analysez rien.
Dans le second cas /admin/puissanceblanc=-5 le '=' est remplacé par un '\0' (on modifie la chaîne urlRequest en utilisant strtok()) et item pointe sur /admin/puissanceblanc --> qui n'est pas un labelsOfInterest et donc vous ne trouvez rien non plus...
--> il faudrait démarrer l'analyse de la chaîne après /admin/ donc tester si l'url commence par /admin/ (avec strncmp par exemple) et si oui commencer le strtok 7 caractères plus loin car il y a 7 caractères dans /admin/
item = strtok(urlRequest+7, "=");
si j'ai bien compris ce que vous voulez faire
Dans un premier temps j'ai adapter un code de façon a avoir une page "d'accueil" et une page "/admin"
Dans un second temps j'ai adapter un code de façon a avoir des boutons fonctionnel sur une page web.
Les bouton de cette page web sont ceux présent sur la page /admin.
A l'heure actuel j'essai donc de combiner ces deux code.
ce qui fait que mes bouton répondant a (exemple) ip/puissanceblanc=-5 doivent a présent être en ip/admin/puissanceblanc=-5
n'ésiter pas a me dire si je ne suis pas claire ou si des détails vous échappés.
tester si l'url commence par /admin/ (avec strncmp par exemple) et si oui commencer le strtok 7 caractères plus loin car il y a 7 caractères dans /admin/
item = strtok(urlRequest+7, "=");
Je me doutait que c'était ici mais je cherchais a ajoute /admin plutôt que de faire +7 ...
J'ai fait ces deux changement, suprimer la paire de {} et modifier en
item = strtok(urlRequest+7, "=");
mais toujours pareil, les bouton rafraîchisse la page mais sans changer les variables.
Imprimez ce que item vaut dans le while en faisant les test par rapport aux labelsOfInterest
ça me donne ça :
[WiFiEsp] New client 0
avant while
puissancebleu
dans while
[WiFiEsp] Disconnecting 0
J'ai donc tout de suite penser a :
const byte maxURL = 20;
et je l'ai modifier a 35
et ca donne ca :
[WiFiEsp] New client 0
avant while
puissanceblanc
dans while
-5
[WiFiEsp] Disconnecting 0
Donc bien pris en compte mais page blanche.
Mais la variable est bien modifier lorsque je rafraîchi la page.
Es que cette ligne s'arrete au caractère indiquer (/admin) ou analyse tout (/admin/peutimporte) se qui suit d'ou l'erreur ?
(!strcmp(urlRequest, "/admin"))
Ah oui il faut grandement allonger l'url Et la ligne lue, elle a quelle taille dans votre code?
strcmp(urlRequest, "/admin") Compare vraiment les 2 donc pas bon si pas juste /admin
Il faut utiliser comme je l'ai dit plus haut strncmp()
strncmp(urlRequest, "/admin", strlen("/admin"))
Ou si vous voulez compter vous même et économiser de la mémoire strncmp(urlRequest, "/admin", 6)
en ajoutant le ! avant ça fonctionne :)
La plus grosse fait
http://192.168.1.52/admin/delaismouvementoscillo=-1
32 :)
J'ai remarque un bug qui n'était pas présent avant la fusion des deux codes.
Lorsque l'on clic sur un bouton, puis un autre, le premier Click sur le second bouton rafraîchi la page mais sans action.
exemple,
1er Click /// je Click "tempbacvent-5", variable modifier, page rafraîchi.
2eme Click /// Je Click ensuite sur tempbacmax+5, variable non modifier, page rafraîchi.
En regardant dans le moniteur, je vois qu'il est affiché :
1er click /// avant while / tempbacvent / dans while / -0.5
2eme Click /// avant while / tempbacvent /dans while /
Il faut attendre min ~3sec entre chaque click
Une idee pourquoi le "item = strtok(urlRequest+7, "=");" ne se met pas a jour du premier coup ?
Du au "hardware" faiblard ?
repostez votre code, sans cela on je ne peux pas suivre vos modifs.
(je pars en vacances une semaine donc moins présent les jours qui viennent)
Voila,
BONNE VACANCE
(http://alterego.ccilvn.be/wp-content/uploads/2015/08/bonnes-vacances.jpg)
esp32 recu
Bien sur, firmware pas en AT, 4h a essayer de mettre a jour avec le firmware AT ... :smiley-sleep:
Ca y est, firmware AT instalé
Répond bien dans le moniteur
et fonctionnel avec le code.
Aucune amélioration sur la vitesse d'affichage
et après plusieurs test, pareil pour les boutons ...
On click , ca rafraîchi et seulement 1 fois sur 3 la variable est modifié ... :smiley-confuse:
Je trouve rien qui pourrait justifier ce bug ...
En parallèle, j'ai réfléchit a une méthode pour pouvoir indiquer et sauvegarder le ssid et mdp du réseau WIFI.
J'ai donc réalise un code utilisant le sim900 déjà présent pour prévenir des coupure de courant et autre alerte.
Le fonctionnement est simple, envoie d'un sms au sim900 de type, "ssid ***votre ssid***" soit "mdp ***votre mdp***".
il lit le message et ne garde que le ssid ou mot de passe, il vous répond (il répond au numéro du sms reçu) ensuite par sms que c'est OK.
Il ne reste qu'a sauvegarder dans l'epprom ces deux donner une fois reçu.
De cette façon, l'appareil sera totalement autonome une fois le code téléverser une fois.
Mais ...
Et oui, il a toujours un mais avec moi :/
J'ai une question et un probleme.
Mon problème : dans mon code principal, ssid et mdp sont déclarer en temps que char alors que dans ce code il sont en string.
du coup, je n'arrive pas fusionner les deux code.
Ma question : ce code utilise des String, cela pose t'il un gros problème ?
Merci
Bonjour rapide
si vous pouvez éviter la classe String c'est mieux :)
votre fonction message - vous ne trouvez pas qu'elle ressemble étrangement à ce que l'on a déjà écrit pour gérer l'attente de la réponse de l'ESP aux commandes AT
--> donc jetez celle ci à la poubelle et adaptez celle que vous avez déjà
Bonjour,
Je ne connais pas ton code, mais si ça peux aider, je tourne depuis 1 an, sans bug, avec un code pour ma rampe led qui a un lever et un couché de soleil sans palier visible. Si ça intéresse je peux le poster.
A+
Merci sachab mais pas de soucis de se coté la.
--> donc jetez celle ci à la poubelle et adaptez celle que vous avez déjà
On a les meme idee, mais une fois bloqué a :
request for member 'concat' in 'reponse', which is of non-class type 'char'
j'en ai conclu que le .concat de reponse.concat ne pouvais rentree dans la class char.
Je vais continuer de regarder ...
J'ai reçu mon CI définitif (v1.0)
(https://image.noelshack.com/minis/2017/42/7/1508662237-img-20171020-234611.png) (https://www.noelshack.com/2017-42-7-1508662237-img-20171020-234611.jpg)
(https://image.noelshack.com/minis/2017/42/7/1508662235-img-20171020-234549.png) (https://www.noelshack.com/2017-42-7-1508662235-img-20171020-234549.jpg)
donc ... au boulot :)
(https://image.noelshack.com/minis/2017/42/7/1508662229-img-20171021-102400.png) (https://www.noelshack.com/2017-42-7-1508662229-img-20171021-102400.jpg)
(https://image.noelshack.com/minis/2017/42/7/1508662204-img-20171021-102502.png) (https://www.noelshack.com/2017-42-7-1508662204-img-20171021-102502.jpg)
(https://image.noelshack.com/minis/2017/42/7/1508662183-img-20171021-102434.png) (https://www.noelshack.com/2017-42-7-1508662183-img-20171021-102434.jpg)
(https://image.noelshack.com/minis/2017/42/7/1508662194-img-20171021-102424.png) (https://www.noelshack.com/2017-42-7-1508662194-img-20171021-102424.jpg)
(https://image.noelshack.com/minis/2017/42/7/1508662185-img-20171021-102411.png) (https://www.noelshack.com/2017-42-7-1508662185-img-20171021-102411.jpg)
(https://image.noelshack.com/minis/2017/42/7/1508662225-img-20171021-102538.png) (https://www.noelshack.com/2017-42-7-1508662225-img-20171021-102538.jpg)
(https://image.noelshack.com/minis/2017/42/7/1508662155-img-20171022-103803.png) (https://www.noelshack.com/2017-42-7-1508662155-img-20171022-103803.jpg)
bonjour
je m'interroge sur la provenance et le prix du circuit ?
merci
Beau travail Djbouns
joli poisson !
bonjour
je m'interroge sur la provenance et le prix du circuit ?
merci
Passer par une petite société.
Livré pour 36.93€
Sur vos photos, là où les composants sont montés, on ne voit pas de remontée de soudure. Les trous ne sont pas métallisés ?
Cordialement.
Pierre
Sur vos photos, là où les composants sont montés, on ne voit pas de remontée de soudure. Les trous ne sont pas métallisés ?
Cordialement.
Pierre
non, pas l'utilité puisque le circuit est en simple couche. la couche TOP n'est la qu'en remplacement de la serigraphie
Vu la teinte du support, j'ai l'impression qu'il est en bakélite et non en verre époxy. Faites attention à l'humidité.
Cordialement.
Pierre
apres toute cette pares midi, j'en suis arrivé a ca :
#define gsm Serial1 // Si utilisation MEGA
char reponse, numeroSMS,SMS[8];
unsigned long t0;
char ssid [] = "ratata"; // Nom du réseau (SSID)
char mdp [] = "prout";
boolean message(char attente, unsigned int timeout, boolean affiche) {
t0 = millis();
reponse="";
while (millis() - t0 < timeout) {
while(gsm.available()) reponse = gsm.read();
if (!strcmp(reponse,attente)>0){ // Lit encore 100 ms le port série
while(gsm.available()) reponse = gsm.read();
break;}}
//if (affiche || !strcmp(reponse,attente)==-1 ) {
// Serial.print("Attente = " (attente) " "+ !strcmp(reponse,attente)+" duree ");
// Serial.print(millis()-t0); Serial.println(" ms");
// Serial.println(reponse);}
if (!strcmp(reponse,attente)>0) return true;
else return false;
}
void LireSMS(){
gsm.println("AT+CMGF=1"); // Mode Texte
message("OK",1000,0);
gsm.println("AT+CMGR=1"); // Lit le premier message disponible sur la carte SIM
message("OK",2000,1);
// Récupérer N° de telephone emetteur pour lui répondre
int test= !strcmp(reponse,"+33");
numeroSMS = strtok(test,test+12);
Serial.println("SMS recu depuis : " + numeroSMS);
// Analyse du message reçu
if (!strcmp(reponse,"Ssid")>0){
int sms = !strcmp(reponse,"Ssid");
SMS = ("SSID : ") + (strtok(sms+5, "="));
}
else if (!strcmp(reponse,"Mdp")>0){
int mdp=!strcmp(reponse,"Mdp");
SMS = ("Mots de passe : ") + (strtok(mdp+4, "="));
}
else {
SMS = "Ordre non compris !";
}
gsm.println("AT+CMGD=1,4"); // effacer les SMS de la Carte SIM
message("OK",1000,0);
// Envoyer la confirmation de l'ordre par SMS
gsm.println("AT+CMGS=\"" numeroSMS "\"");
message(">",1000,0);
gsm.println(SMS);
gsm.write(26); // Caractère de fin 26 <Ctrl-Z>
message("+CMGS:",10000,1);
}
void setup() {
Serial.begin(9600);
gsm.begin(9600);
Serial.println("Initialisation...");
gsm.println("AT");
while (!message("OK",1000,0)) gsm.println("AT");
gsm.println("AT+CMGF=1"); // Mode Texte
message("OK",1000,0);
gsm.println("AT+CMGD=1,4"); // effacer les SMS en mémoire dans la carte SIM
message("OK",2000,0); // car on lit toujours le message N°1 de la carte SIM...
}
void loop() {
if (message("+CMTI:",20000,0)) LireSMS(); // Si nouveau SMS disponible SIM800 envoie +CMTI:
}
et se foutu message qui persite ...
exit status 1
invalid operands of types 'const char [8]' and 'char*' to binary 'operator+'
Êtes vous sur que response doit être un char ? (1 seul caractère?) idem pour numeroSMS
A la base, numeroSMS et réponse etait en String.
donc il comporte plus qu'un caractere :/
il faut que j'utlise atoi ?
un peu comme sa ?
char str[20];
strcpy(str, "98993489");
val = atoi(str);
Oui, il faut assez d'espace pour stocker le texte
Et while(gsm.available()) reponse = gsm.read(); ça ne va pas bien marcher..
Regardez comment on a fait dans l'autre fonction qui écoutait la réponse de l'ESP ou alors relisez mon petit tuto en français (http://forum.arduino.cc/index.php?topic=500683.0)
oui j'ai bien compris qu'il faut utiliser c string, c'est ce que j'essaie de faire en remplaçant par strcmp par exemple
donc je remplace
reponse.concat(char(gsm.read()));
par
strcat(reponse,(gsm.read()))
et
reponse.indexOf("+33");
par
strpbrk(reponse,"+33");
Non parce que gsm.read() retourne juste un caractère, pas une chaîne
Regardez le tuto du port série ci-dessus il y a du code
mais donc il faut changer complètement la structure du code actuel, aucun intérêt de travailler a partir de celui la
oui - c'est juste une fonction hein... pas la fin du monde :)
oui - c'est juste une fonction hein... pas la fin du monde :)
:smiley-yell:
Entre deux tirage de cheveux, ton mess ma bien faire rire :D :D :D
tu a raison il faut relativiser :smiley-roll:
c'est vraiment exactement comme le code de l'ESP. vous envoyez des commandes AT ou +CMTI au GSM au lieu du Wi-Fi et vous attendez une réponse sur la ligne série avec un mot clé
c'est vraiment exactement comme le code de l'ESP.
oui et justement toute la partie dont je n'arrive pas a trouver la logique ...
bref, une demi journée, j'arrive déjà a pas avoir d'erreur en compilant
#define gsm Serial1
void setup()
{
Serial.begin(9600);
gsm.begin(9600);
gsm.println("AT");
delay (2000);
gsm.println("AT+CMGF=1");
delay (2000);
}
void loop()
{
if(gsm.available()) {
char c = gsm.read();
if (c != -1) {
switch (c) {
case '\r':
break;
case '\n':
break;
default:
if(strstr (c, "Mdp")){
Serial.print(F("mdp recu"));}
else if(strstr (c, "ssid")){
Serial.print(F("ssid recu"));}
break;
}
}
}
}
Dans les chose que je ne comprend pas,
pourquoi juste en ajoutent
default:
Serial.print((char) c);
break;
J'ai les question/réponse de
gsm.println("AT");
delay (2000);
gsm.println("AT+CMGF=1");
alors que sinon, non
parce que le composant est en mode "echo" donc répète la commande qu'il a reçu, puis l'exécute et vous envoie sa réponse
A ...
Jsuis en train de tout reprendre le code qui affiche dans le moniteur en char puis en une phrase
http://forum.arduino.cc/index.php?topic=500683.0
j'ai modifier pour que le serial imprime le message recu par sms plutot que celui ecrit dans le moniteur
#define gsm Serial1
gsm.begin(9600);
while (gsm.available() && messageEnCours) {
int c = gsm.read();
Mais aucune reaction
peut etre pasrque le gsm n'est pas mis en mode texte ?
jessaie ...
il faut que le GSM soit configuré pour sortir les SMS reçu automatiquement sur son interface série. ça se fait avec des commandes AT
oui avec ("AT+CMGR=1")
parfois il faut faire un AT+CMGF=1 avant pour passer en SMS text mode (selon les composants le mode par défaut est SMS PDU mode) --> vérifiable en envoyant AT+CMGF=? et regardant la réponse donnée
parfois il faut faire un AT+CMGF=1 avant pour passer en SMS text mode (selon les composants le mode par défaut est SMS PDU mode) --> vérifiable en envoyant AT+CMGF=? et regardant la réponse donnée
il me repond +CMGF: (0,1)
donc je le passe en mode texte sistematiquement avant tout autre demande
j'en suis la, dans un premier temps, tout dans le serial, j'envoie mon texte, il me repond (sauf que meme quand j'envoie ssid# il me repond qu'il ne comprend pas mais bon ...)
et des que je bascul sur gsm(serial1) plus rien
#define gsm Serial1
const byte tailleMessageMax = 50;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
const char marqueurDeFin = '#';
boolean ecouter()
{
gsm.println("AT+CMGF=1"); // Mode Texte
delay(1000);
gsm.println("AT+CMGR=1");
delay(2000);
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case marqueurDeFin:
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void setup() {
Serial.begin(9600);
gsm.begin(9600);
Serial.println("Initialisation...");
gsm.println("AT");
delay(1000);
gsm.println("AT+CMGF=1"); // Mode Texte
delay(2000);
gsm.println("AT+CMGD=1,4"); // effacer les SMS en mémoire dans la carte SIM
delay(2000);
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
Serial.println(message);
if (!strcmp(message, "ssid")) {
Serial.println(F("Ssid recu"));
}
else if (!strcmp(message, "mdp")) {
Serial.println(F("Mdp recu"));
}
else {
Serial.println(F("pardon, pouvez vous repeter ?"));
}
}
}
Vous n'avez pas le bon marqueur de fin, ça devrait être '\n' pas '#'
sinon il ne faut absolument pas mettre gsm.println("AT+CMGF=1"); // Mode Texte
delay(1000);
gsm.println("AT+CMGR=1");
delay(2000);
dans la fonction ecouter()
on lui dit une fois dans le setup() et ça bascule dans le bon mode, ensuite on écoute.
je crois (de mémoire) qu'il faut faire un AT+CNMI pour router automatiquement les SMS vers le port série et pas les lire un par un depuis la mémoire
Ok je vais faire la modif du # (je ne m'etait pas atarder cela ne me paraissait pas primordial)
Pour les fonction AT+CMGF=1 et AT+CMGR=1 je les est mis la car en plus d'etre dans le setup (du code en string) il etait de nouveau au moment de lire le sms.
Pour la commande AT je vais voir dans le datasheet.
effectivement si j'ai compri les post en anglais que j'ai lu, cnmi pourait envoyer directement le message.
il se compose : AT+CNMI=1,2,0,0,0 <<< c'est dernier corresponde surement a une fonctionne mais se que j'ai trouver pour le moment est en anglais et pas compréhensible pour moi.
En le metant dans le code, "message" ne vaut plus rient (vide) et le moniteur indique +CMTI: "SM",9 se qui correspondrait a l'emplacement du message recu ...
je continu de chercher
fin du 4eme jours ... :smiley-confuse:
déprimé ou dégoûté ... pourquoi choisir :)
Alors au moins savoir pourquoi passer tout ce temps a faire tout ça (je doit être a ~100h pour le hardware et ~250h de recherche et codage en 4 mois et je commence a avoir ma femme sérieusement sur le dos lol):
Pourquoi tout le monde (oui,oui, puisque des heures sur le net a jamais trouvé des code/projet en AT sans String) utilise la class String puisque elle semble si néfaste a long thermes ?
je continu de chercher
La meilleur traduction/explication que j'ai trouvé :
Rechercher le CNMI dans les URC
Maintenant, vous recevrez enfin les informations sur un message texte entrant. Il ressemblera à ceci:
+ CMTI: "SM", 1
CMTI signifie qu'il s'agit d'un URC lié à un message texte. SM signifie que le message a été stocké sur la carte SIM. '1' signifie qu'il a été sauvegardé sur le 1er enregistrement du stockage.
Vous avez juste besoin d'analyser cette URC pour obtenir l'index du message dans le stockage. Vous pouvez le faire comme ceci:
int index = arg0.indexOf (",");
String sms_positionInMemory = arg0.substring (index + 1, index + 3);
Maintenant, vous savez où le message est stocké et est capable de le lire Utilisez la commande AT + CMGR . La réponse contiendra le message texte.
Réponse chaîne = atc.send ("AT + CMGR =" + sms_posisionInMemory + "\ r");
Vous avez lu le message!
Donc cette fonction serait pour avoir des info sur le message comme sont emplacement par exemple afin de pouvoir activer une fonction sur un message/emplacement précis.
Donc la fonction pour lire le message est bien AT + CMGR =
Il y a deux approches pour lire les sms.
Par défaut les SMS sont routés vers le stockage SIM et il faut donc écouter si on en a reçu un, si oui aller lire la case où est stocké le sms sur la carte SIM, puis faire le ménage et l'effacer
--> Il faut d'abord dire à votre module que vous êtes intéressé par le routage des événements (URC) de type sms vers le terminal.
ça se fait avec AT+CNMI=2,1 (le 2 pour dire je veux les URC de type SMS et le 1 pour dire je veux les SMS « normaux » (il y a différents types de SMS comme la class3))
- vous écoutez le port série en attente d'un message qui ressemble à + CMTI: "SM", 1
==> c'est un peu comme quand on écoutait le port série de l'esp et que l'on voyait une requête TCP entrante, on analysait alors la requête pour trouver le n° de canal et l'URL et on bâtit la réponse en conséquence. Ici un + CMTI: "SM", 1 nous dit, vous avez un SMS dans la zone de stockage « SM » (la carte sim) à la position 1, donc il faut dans l'absolu reconnaître +CMTI, trouvez la position et aller lire ce qu'il y a de rangé à cette position avec la commande AT+CMGR=1 puis ensuite faut vider la mémoire 1 de la SIM et donc envoyer AT+CMGD=1
Mais vous pouvez aussi passer en mode routage direct des SMS sur le port série avec un AT+CNMI=0,2,0,0,0 (le 2 c'est le champ <mt> de la commande qui dit de router vers le port série. les messages ne sont pas sauvegardés, il sont perdus si rien ne les lit sur le port série). Ils sont transmis alors directement sur le port série en notification sous la forme +CMT: "N° de tel",,date heure\r\n et suivi par le contenu du message. donc votre code dans ce cas doit écouter pour trouver le +CMT: et sur la ligne suivante vous aurez le contenu du SMS
J'ai trouvé un document en français qui en parle -> page 24-26 de ce document (http://thibaut.legaye.free.fr/stock/irist/modemGSM/Aide_AT_V13.pdf)
Donc oui il faut choisir une approche pour la lecture, soit en mémoire avec ensuite effacement, soit juste en écoutant le flux
La méthode lecture direct me parait plus approprié car :
elle ne sauvegarde pas (donc pas d'effacement)
Elle indique le numéro de tel (donc utile puisque il faut répondre a ce même numéro)
et tout cela pourras être fait en indiquant a partir de quelle caractère nous voulons analyser.
Tout parait si simple ... :) au bout du cinquième jour au point mort :/
Je vois sa mais j'ai un problème un peu bloquant ...
tout s'affiche dans le moniteur serie et se répond ... l'echo comme tu dit. mais comment ne plus avoir ca ?
pardon, pouvez vous repeter ?
ATAT+CAT+CMGF=1
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
AT+CAT+CMGF=1
pardon, pouvez vous repeter ?
OAT+CAT+CMGF=1
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
AT+CAT+CMGF=1
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
ATAT+CAT+CMGF=1
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
AT+CMGF=1
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
AAT+CAT+CMGF=1
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
AT+CAT+CMGF=1
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
ATAT+CAT+CMGF=1
pardon, pouvez vous repeter ?
AT+CAT+CMGF=1
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
AAT+CAT+CMGF=1
pardon, pouvez vous repeter ?
Bonjour Djouns,
Ton problème est simple, il te demande juste de répéter. :) :-[
Non mais blague à part félicitation pour ce post.
Lorsque le code sera fonctionnel, va-tu le proposer au partage, car je pense que beaucoup seraient intéressés?
A+
Sacha
Lorsque le code sera fonctionnel, va-tu le proposer au partage, car je pense que beaucoup seraient intéressés?
A+
Sacha
C'est le but, couplé au montage hardware afin d'être un appareil complet.
Mais la parti programmation est vraiment très compliquer pour moi et je passe des heure t des jour pour des fonction qui pourtant n'ont pas l'air très compliquer quand on si connait un peu plus.
n'étant qu'épauler que par jml, cela devient compliquer car ni ici ni sur recifal france on ne m'apporte de l'aide a par lui.
Si cela continu, je risque de bâcler cette partie au détriment du bon fonctionnement a long thermes.
c'est dommage. et surtout, il n'y auras pas d'intérêt a partager un projet bâcler pour ensuite avoir des retour néfaste.
Vraiment dommage vu le nombre de personne qui compte sur ce genre de projet (mais qui continu d'utiliser l'ackduino avec les problème qu'on lui connait)
J'ai essayé de télécharger les fichiers de ton projet à https://www.petit-fichier.fr/2017/10/07/aquabouns/
mais rien y fait j'ai comodo qui me bloque ce lien avec un Warning: Unsafe Website Blocked!
www.petit-fichier.fr.
Je ne sais pas si mes compétences seront suffisantes, mais en regardant les fichiers, peut être qu'une idée ou suggestion surgira de mon esprit.
Part contre si tu as besoin d'aide sur le circuit imprimé ou l'electronique, j'ai de bonnes bases.
A+
Sacha
Djbouns,
Dans un projet complexe comme le vôtre, il y a plusieurs façons de faire, qui peuvent toutes marcher. Il vaut mieux qu'une seule personne vous aide plutôt que plusieurs avec chacune son idée sur la question...
Bon courage,
MicroQuettas.
PS : une question si vous avez le temps. Je crois avoir vu sur votre site que vous injectiez du CO2 dans l'aquarium ? Quel est l'effet, pas de l'eau à bulles quand même ? :)
J'ai essayé de télécharger les fichiers de ton projet à https://www.petit-fichier.fr/2017/10/07/aquabouns/
mais rien y fait j'ai comodo qui me bloque ce lien avec un Warning: Unsafe Website Blocked!
Je vient de le faire et pas de probleme.
Sa vient soit de ton navigateur soit de ton anti virus.
Sinon, envoie moi ton mail, normalement ca doit passer il fait moins de 10mo
Part contre si tu as besoin d'aide sur le circuit imprimé ou l'electronique, j'ai de bonnes bases.
Oui sa peut mettre utile, au moins pour vérifier que je n'ai pas fait de bêtise lol
Pour désactiver l'echo Généralement on envoie ATE0 (et ATE1 pour le remettre)
(suis juste sur mon smartphone donc peut pas trop,aider pour le moment, ce week end je regarderai plus précisément votre soucis)
Djbouns,
Dans un projet complexe comme le vôtre, il y a plusieurs façons de faire, qui peuvent toutes marcher. Il vaut mieux qu'une seule personne vous aide plutôt que plusieurs avec chacune son idée sur la question...
tout a fait d'accord, c'est ce qui c'est passer avec l'ackduino ... un ptit bout de chaqun ... avec chaqun une facon differente de coder ...
PS : une question si vous avez le temps. Je crois avoir vu sur votre site que vous injectiez du CO2 dans l'aquarium ? Quel est l'effet, pas de l'eau à bulles quand même ? :)
vous injectiez du CO2 dans l'aquarium ?
Alors oui et non :)
En eau douce, le
CO2 est injecter directement dans le bac pour alimenter en dioxyde de carbone les plantes.
Le problème du CO2 est qu'il acidifie l'eau. Il faut donc le réguler via un ph mètre et une électrovanne.
Les bac d'eau douce on un PH autour de 7.
En eau de mer, c'est different.
le CO2 n'est pas injeter dans le bac mais dans un reacteur a calcaire (http://jmsnat.free.fr/site/rac+++.html) qui contient du substrat (le plus souvent de l'aragonite).
Les bac recifal on un PH autour 8.
l'aragonite ce dissout a partir d'un PH inférieur a 7. le but est donc de decendre le PH dans le Reacteur A Calcaire pour dissoudre l'aragonite qui fournira en calcium et KH l'aquarium ( le calcium et le KH son consommer par les coraux pour construire leur squelette (http://www2.ggl.ulaval.ca/personnel/bourque/s3/3.35b.jpg) et donc "grandire"
Il faut donc bien réglé le RAC et l'apport en CO2 pour ne pas faire chuter le PH de l'aquarium.
(suis juste sur mon smartphone donc peut pas trop,aider pour le moment, ce week end je regarderai plus précisément votre soucis)
Ok JM, je continu tout de même de regarder, a la recherche du declic lol
Vous avez quoi comme carte GPRS? (quelle version du jeu de commandes)
J'ai une SIM800L :
AT+GSV
SIMCOM_Ltd
SIMCOM_SIM800L
Revision:1418B03SIM800L24
Et une SIM900A:
AT+GSV
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
Étonnant qu'elle me réponde SIM800A ...
J'ai téléchargé ton zip.
Félicitation pour ce codage bien organisé, même moi, je le comprends :)
Je vote pour que J-M-L fasse du récifal.
Je vais commander un écran afin de pouvoir suivre et participer dans la limite de mes compétences à l'avancer du projet.
Voici quelques réflexions, qui, si tu en retiens, risque de t'occuper un peu voir beaucoup.
- Sans parler de l'eau douce, en eau de mer plusieurs variantes existent, le fish only, le récifal, les méthodes balling, jaubert, berlinoise etc.. Afin que l'aquabouns touche le plus d'aquariophile peut être que l'activation/désactivation des options soit possibles.
- Pouvoir modifier toutes les valeurs personnalisables (levé, couché, etc...) sans devoir se connecter, par exemple grace a un encodeur rotatif ou avec un clavier. ma préférence va à l'encoder rotatif qui simplifie grandement les choses.
- Ajouter la gestions de moteurs pour pompes doseuses. Mais pas avec l'algorithme de la méthode balling qui limite l'utilisation, mais en faisant un déclenchement à une heure déterminée, par jour/ semaine, dosage, calibrage, durée etc indépendant pour chaque pompe.
- Pourquoi créer un circuit avec des relais, alors qu'il existe des cartes de relais pour arduino peu chères.
Si tu comptes vraiment créer une carte pour des relais, pourquoi ne pas mettre des relais reed qui sont silencieux et prennent peux de places.
Tes sorties pwm attaquent directement tes alim meenwell drivers intégrés ou des driver mean well style ldd-1000.
Voici mes premières réflexions/ suggections.
A+
- Sans parler de l'eau douce, en eau de mer plusieurs variantes existent, le fish only, le récifal, les méthodes balling, jaubert, berlinoise etc.. Afin que l'aquabouns touche le plus d'aquariophile peut être que l'activation/désactivation des options soit possibles.
C'est un détail qui est surement a ma porté.
Je pencherais sur ce point une fois le code complet.
- Pouvoir modifier toutes les valeurs personnalisables (levé, couché, etc...) sans devoir se connecter, par exemple grace a un encodeur rotatif ou avec un clavier. ma préférence va à l'encoder rotatif qui simplifie grandement les choses.
Ce genre de chose reviendrait a avoir un écran tactile :)
Et en plus, ajouter ce genre de bouton reviendrais a etre obliger d'avoir un ecran. C'est justement le sujet que j'ai lancer ici (http://www.recifal-france.fr/sondage-gestion-d-aquarium-avec-ecran-ou-sans-ecran-t32914.html#p568540) car je penche de plus en plus vert le "sans écran" ... a suivre ...
- Ajouter la gestions de moteurs pour pompes doseuses. Mais pas avec l'algorithme de la méthode balling qui limite l'utilisation, mais en faisant un déclenchement à une heure déterminée, par jour/ semaine, dosage, calibrage, durée etc indépendant pour chaque pompe.
J'ai pas ou peu parlée de mon "cahier des charges" fixer de par mes différente expérience arduino/récifal.
Cette automate ne gère que des appareil "secondaire" si je puits dire ...
Plus précisément, je ne veut pas confier la gestion de la température a l'arduino.(le chauffage est gérer directement par sonde interne a celui ci ou alors par un mh1210 ou équivalent). l'arduino n'as comme fonction que de relever la température et dans le cas ou la température du bac dépasse la limite max, d'ouvrir un relais (relais non alimenté = fermé = ON)
Je ne veut pas gérer l'osmolation par arduino (ceci est gérer de manière mécanique/électronique sur le C.I. sans passer par l'arduino)
Je ne veut pas gérer l'allumage/extinction de l'écumeur par l'arduino (ceci est gérer de manière mécanique/électronique sur le C.I. sans passer par l'arduino)
Je ne veut pas gérer pompe de remonté par arduino (l'arduino n'as comme fonction que de couper la remonté lorsque l'on active le nourrissage manuel (cube congeler par exemple) donc nous somme a coté a ce moment la (relais non alimenté = fermé = pompe ON)
Tout ces points n'ayant comme but qu'il n'y est aucun ( ne jamais dire jamais :)) risque pour le bac en cas de BUG (comme j'ai subit avec l'ackduino, bug quand osmolation allumer = vidage de la réserve = chute salinité = débordement = pompe osmolation cramé = disjonction ... Ou bug quand chauffage éteint ... en pleine hiver ...)
Bref, cela peut avoir une contrainte pour certain (même si je vois pas en quoi) mais au prix de notre bac ...
- Pourquoi créer un circuit avec des relais, alors qu'il existe des cartes de relais pour arduino peu chères.
Si tu comptes vraiment créer une carte pour des relais, pourquoi ne pas mettre des relais reed qui sont silencieux et prennent peux de places.
Si tu regarde bien mon C.I, il ne comporte pas de relais.
Encore un point sur le quelle je ne transigerait pas, PAS DE 220V EN DEHORS DU TABLEAU.
Ces pour cette raison que le circuit est la "a moitier", il suffis de brancher ces sorties dans le tableau a des relais, chez moi des Finder
(http://www.conrad.fr/medias/global/ce/3000_3999/3800/3800/3804/1217711_BB_00_FB.EPS_1000.jpg)
Mais chacun pourras faire comme il le veut niveau montage électrique. si tu regarde bien le C.I il est possible de se bracher sur les sortie des l'arduino correspondante avant de rentrée dans le circuit relais (il suffit de ne pas alimenter en 12v la partie relais)
Tes sorties pwm attaquent directement tes alim meenwell drivers intégrés ou des driver mean well style ldd-1000.
Tout les branchement vu jusque a présent était comme cela. babyfish ou autre egalement, non ?
Afin de pouvoir si retrouver, je te propose que la partie "Hardware" soit discuter/partager sur le poste de mon bac (http://www.recifal-france.fr/400l-de-djbouns-t22449-378.html#p566418) afin de ne pas tout mélanger ici.
J-M-L, tu y est le bienvenu :) :) :)A+
[/quote]
C'est un détail qui est surement a ma porté.
Je pencherais sur ce point une fois le code complet.
fait sur la page WEB (https://img4.hostingpics.net/pics/398636Sanstitre.jpg) :)
revenons a nos moutons ...
j'ai fait quelque modif et rechercher la fonction AT+CNMI=0,2,0,0,0.
J'ai bien un retour du numero de tel qui envoyai le sms + date et heure mais pas le sms en lui meme.
pour avoir le sms il faut faire AT+CNMI=2,2,0,0,0.
Donc j'ai bien le SMS qui safiche dans la console mais meme quand le message est Ssid ou Mdp, le serial affiche "pardon, pouvez vous repeter ?"
J'ai bien suprimé l'echo mais j'ai du coup les reponse sans les question lol
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
pardon, pouvez vous repeter ?
OK
pardon, pouvez vous repeter ?
Le code donne ca ... j'ai l'impression de m'approcher de la fin du tunnel mais y a encore un gros precipisse a franchir lol
#define gsm Serial1
const byte tailleMessageMax = 50;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void setup() {
Serial.begin(9600);
gsm.begin(9600);
Serial.println("Initialisation...");
gsm.println("AT");
delay(1000);
gsm.println("AT+CMGDA=\"DEL READ\"");
delay(1000);
gsm.println("AT+CNMI=2,2,0,0,0");
delay(1000);
gsm.println("AT+CSAS");
delay(1000);
gsm.println("AT+CMGF=1"); // Mode Texte
delay(1000);
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
Serial.println(message);
if(!strcmp(message,"Ssid")) {
Serial.println(F("Ssid recu"));
}
else if (!strcmp(message,"Mdp")) {
Serial.println(F("Mdp recu"));
}
else {
Serial.println(F("pardon, pouvez vous repeter ?"));
}
}
}
J'ai réussi a avoir les réponse correspondante a mes SMS en faisant sa :
if(strncmp(message,"Ssid",4)==0) {
ssid = strtok(message+5, "=");
Serial.println(ssid);
Serial.println(F("Ssid recu"));
}
else if (strncmp(message,"Mdp",3)==0) {
mdp = strtok(message+4, "=");
Serial.println(mdp);
Serial.println(F("Mdp recu"));
}
else {
Serial.println(F("pardon, pouvez vous repeter ?"));
Donc la, il lit le sms, trouve si il s'agit du ssid ou mdp, et "pioche" les caractères correspondant modifi le char ssid ou mdp.
Une bonne avancé
Il me reste a faire pareille avec le numéro de tel pour pouvoir répondre par sms OK
Bonsoir
je n'ai pas avec moi de quoi tester réellement si ça va fonctionner mais jetez un oeil à un truc comme ça
prévu pour avoir le SIM800 sur Serial1, fonctionnant à 9600 bauds et la console à 115200
comme dans le tuto ESP il y a une fonction gsmPrintlnATCommand() pour envoyer une commande AT, qui attend une réponse passée aussi en paramètre, pendant un certain temps (et qui affiche le contenu de ce que l'on lit si on dit "vrai" en dernier param). l'attente se fait en appelant la fonction waitForString()
il y a une fonction un peu générique getGSMLine() qui va lire le port Série du module pour bâtir une phrase jusqu'à un marqueur
Le code ci dessous est supposé configurer le module GPRS puis détecter ce qui arrive. S'il reconnait une réponse alors il essaye de l'analyser.
j'ai mis un petit bout de code qui permet de taper dans la console série des commandes AT pour voir le résultat, par exemple si vous tapez AT+CSQ alors le code reconnait le début de la réponse +CSQ: xxx,yyy et extrait puis vous affiche xxx et yyy
#define gsm Serial1
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 1000ul;
const byte maxMessageSize = 200;
char GSM_MessageLine[maxMessageSize + 1]; // +1 as we want to be able to store the trailing '\0'
// --------------------------------------
// gsmPrintlnATCommand executes an AT commmand by adding at the end a CR LF
// then it checks if endMarker string is receivedon the GSM Serial port
// for max duration ms returns a boolean stating if the marker was found
// with the verbose option, the output from the GSM is also printed to Serial
// --------------------------------------
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
// --------------------------------------
// gsmPrintATCommand or gsmpWriteATCommand is used when you don't want to send the CR LF
// at the end of the commmand line; use it to build up a multi part command
// same syntax as print as they are Variadic Macros using print or write
// --------------------------------------
#define gsmPrintATCommand(...) gsm.print(__VA_ARGS__)
#define gsmWriteATCommand(...) gsm.write(__VA_ARGS__)
// --------------------------------------
// read a line from gsm, ignore '\r' and returns true if '\n' is found
// --------------------------------------
boolean getGSMLine()
{
static byte indexMessage = 0;
boolean incomingMessage = true;
while (gsm.available() && incomingMessage) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
GSM_MessageLine[indexMessage] = '\0'; // trailing NULL char for a correct c-string
indexMessage = 0; // get ready for next time
incomingMessage = false;
break;
case '\r': // don't care about this one
break;
default:
if (indexMessage <= maxMessageSize - 1) GSM_MessageLine[indexMessage++] = (char) c; // else ignore it..
break;
}
}
}
return !incomingMessage;
}
// --------------------------------------
// waitForString wait max for duration ms whilst checking if endMarker string is received
// on the GSM Serial port returns a boolean stating if the marker was found
// --------------------------------------
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
void setup() {
Serial.begin(115200);
gsm.begin(9600); // suivant votre config, essayez 9600 19200 38400 57600 74880 115200
// prévoir éventuellement AT+CPIN=1234 pour rentrer le PIN
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, false)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
/*
On passe en Message en verbal (ATV1)
OK --> Commande acceptÈe
RING --> Appel entrant
NO CARRIER --> déconnexion par perte de porteuse
ERROR --> Commande non acceptée
BUSY --> Distant occupé
NO ANSWER --> Pas de réponse du distant
*/
if (!gsmPrintlnATCommand("ATV1", OKString, oneSecond, false)) {
Serial.print(F("Erreur Message en Verbal"));
dieHere();
}
// exemple pour envoyer des commandes
if (!gsmPrintlnATCommand("AT+CNMI=0,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
Serial.println("Initialisation terminé");
}
void loop()
{
int r;
// on lit une commande AT que l'utilisateur tape sur la console série et on l'envoie au gsm
while (Serial.available()) {
r = Serial.read();
if (r != -1) gsm.write((char) r);
}
if (getGSMLine()) {
// ici on peut tester si la réponse est quelque chose d'intéressant
// ********************************************
// par exemple analyse de la réponse à AT+CSQ
if (!strncmp(GSM_MessageLine, "+CSQ:", 5)) { // 5 parce que "+CSQ:" c'est 5 caractères, on compare le début de la chaîne
int rssi, ber;
if ( sscanf(GSM_MessageLine, "+CSQ: %d,%d", &rssi, &ber) == 2) {
Serial.print(F("Qualité de reception = ")); Serial.println(rssi);
Serial.print(F("Code Erreur = ")); Serial.println(ber);
}
}
// ********************************************
// TEST RECEPTION D'UN SMS
// +CMT: "+33620000000",,"01/03/22,16:33:31+00"<CRLF>
// texte bla bla bla<CRLF>
//
else if (!strncmp(GSM_MessageLine, "+CMT:", 5)) {// 5 parce que "+CMT:" c'est 5 caractères, on compare le début de la chaîne
char phoneNb[30];
if ( sscanf(GSM_MessageLine, "+CMT: \"%[+0-9]s\"", phoneNb) == 1) {
Serial.print(F("SMS de = ")); Serial.println(phoneNb);
while (!getGSMLine()); // on attend le texte du SMS
Serial.print(F("Message = [")); Serial.print(GSM_MessageLine); Serial.println(F("]"));
}
}
// ********************************************
// Autres test...
else if (!strncmp(GSM_MessageLine, "+TOTO:", 6)) {
} else {
Serial.print(F("ignore ["));
Serial.print(GSM_MessageLine);
Serial.println(F("]"));
}
}
}
j'ai utiliser sscanf() pour la démo - mais vaut mieux passer par des fonctions moins gourmandes en mémoire comme vous le faites avec strtok() par exemple
dites moi si ça aide
Toute la partie initialisation, NICKEL !
Pour le reste, quand je tape +CSQ: xxx,yyy j'ai error.
Pour la fonction strtok j'y suis presque ... j'arrive a récupérer mon numéro (le numéro émetteur du sms) sous la forme +336*********" pour cela je fait tel = strtok(message+8,",") mais il faut que je coupe au " et non pas au , mais quand on met tel = strtok(message+8,""") sa marche pas.
Je croit que quand cela seras bon, le code serait fonctionnel (ou presque)
faut taper
AT+CSQ dans la console, ça va l'envoyer au module qui doit répondre
mais quand on met tel = strtok(message+8,""") sa marche pas.
il ne faut pas oublier de mettre un '\' pour que le guillemet soit pris en compte dans une chaîne "\""
essayez ce code pour voir strtok()
char reponse1[] = "+CMT: \"+33600123400\",,\"17/10/28,20:50:31+01\"";
char reponse2[] = "+CMT: \"+33600123400\",,\"17/10/28,20:50:31+01\"";
char *ptr;
void setup() {
int i;
Serial.begin(115200);
ptr = strtok(reponse1, ",\"");
i = 1;
while (ptr) {
Serial.print(i++); Serial.print("\t"); Serial.println(ptr);
ptr = strtok(NULL, ",\"");
}
Serial.println(F("----------------"));
ptr = strtok(reponse2, ",/:\"");
i = 1;
while (ptr) {
Serial.print(i++); Serial.print("\t"); Serial.println(ptr);
ptr = strtok(NULL, ",/:+\"");
}
}
void loop() {}
suivant les caractères séparateurs que l'on donne on peut couper plus ou moins finement la phrase d'entrée. vous devriez voir cela sur la console
1 +CMT:
2 +33600123400
3 17/10/28
4 20:50:31+01
----------------
1 +CMT
2
3 33600123400
4 17
5 10
6 28
7 20
8 50
9 31
10 01
ça devrait vous donner des idées :) (un petit coup de strcpy() pour récupérer la chaîne ou un petit coupe de atoi() pour lire un entier (pour le jour et l'heure si vous voulez le détail)
J'ai enfin réussi a récupérer le numéro de tel correctement puis a le remettre comme numéro pour envoyer un sms avec strncmp
Je n'est pas besoin de connaitre la date ni l'heure, le seul but est de récupérer le numéro de tel
du coup j'en suis la
#define gsm Serial1
const byte tailleMessageMax = 200;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
char *ssid ;
char *mdp ;
char *tel;
char *SMS;
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 3000ul;
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void envoiesms(){
if (gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, false)) {
gsm.print("AT+CMGS=");
gsm.print(tel);
gsm.println();
delay(500);
gsm.print(SMS);
delay(500);
gsm.print((char)26);
}
}
void setup() {
Serial.begin(115200);
gsm.begin(9600);
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, true)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CMGDA=\"DEL READ\"", OKString, oneSecond, true)) {
Serial.print(F("Erreur delete message"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CNMI=2,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
}
if (!gsmPrintlnATCommand("AT+CSAS", OKString, oneSecond, true)) {
Serial.print(F("Error Save"));
}
if (!gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, true)) {
Serial.print(F("Error mode texte"));
}
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
if (!strncmp(message,"+CMT:",5)){
Serial.println(message);
tel = strtok(message+6,(","));
SMS = "tel recu :";
Serial.println(SMS);
envoiesms();
}
else if(strncmp(message,"Ssid",4)==0) {
ssid = strtok(message+5, "=");
SMS = "SSID RECU : ",(ssid);
Serial.println(SMS);
envoiesms();
}
}
else if (strncmp(message,"Mdp",3)==0) {
mdp = strtok(message+4, "=");
SMS = "MDP RECU : ",(mdp);
Serial.println(SMS);
envoiesms();
}
else {
SMS = "instruction pas comprise";
Serial.println(SMS);
envoiesms();
}
}
sa marche partiellement ...
Une fois sur deux je reçoit pas de sms mais il y a bien du mouvement dans la console... je comprend pas pourquoi ...
Par contre, si il ny a pas de sms arrivé, le loop ne devrait pas continue non ? il il me lit en boucle les dernier "else" instruction non comprise.
essayez ce code pour voir strtok()
utiliser cela serait peut être mieux entre autre dans le cas d'un numéro de tel ans un format différent (je pense déjà a exporter mdr)
Mais je ne comprend pas comment ensuite récupérer la chaîne numéro 2.
Je continu de chercher mais je ne comprend pas pourquoi sa repond un coup sur deux et pourquoi se loop continuel des lors qu'un sms est recu ...
#define gsm Serial1
const byte tailleMessageMax = 200;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
char *ssid ;
char *mdp ;
char *tel;
char *SMS;
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 2000ul;
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void envoiesms(){
gsm.print("AT+CMGS=");
gsm.print(tel);
gsm.println();
delay(500);
gsm.print(SMS);
delay(500);
gsm.print((char)26);
}
void setup() {
Serial.begin(115200);
gsm.begin(9600);
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, true)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CMGD=1,4", OKString, oneSecond, true)) {
Serial.print(F("Erreur delete message"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CNMI=2,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
}
if (!gsmPrintlnATCommand("AT+CSAS", OKString, oneSecond, true)) {
Serial.print(F("Error Save"));
}
if (!gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, true)) {
Serial.print(F("Error mode texte"));
}
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
if (!strncmp(message,"+CMT:",5)){
Serial.println(message);
tel = strtok(message+6,(","));
SMS = "sms recu";
Serial.println(SMS);
envoiesms();
}
else if(strncmp(message,"Ssid",4)==0) {
ssid = strtok(message+5, "=");
SMS = "SSID RECU : ",(ssid);
Serial.println(SMS);
delay(500);
envoiesms();
}
else if (strncmp(message,"Mdp",3)==0) {
mdp = strtok(message+4, "=");
SMS = "MDP RECU : ",(mdp);
Serial.println(SMS);
delay(500);
envoiesms();
}
else {
SMS = "instruction pas comprise";
Serial.println(SMS);
delay(500);
envoiesms();
}
gsm.println("AT+CMGD=1,4");
Serial.println("sms effacer");
}
}
dans votre vrai code il ne faudra pas mettre de void dieHere()
{
while (true);
}
il faudra gérer quand un truc ne fonctionne pas (reset / rebooter le composant / désactiver la fonction)
const uint32_t oneSecond = 3000ul;
--> changez le nom de la variable si ce n'est plus une seconde...const uint32_t attente3s = 3000ul;
par exemple
dans la fonction boolean ecouter() vous devriez tester si vous avez reçu '\r' et ne pas l'ajouter au message car il ne sert à rien
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
case '\r': // on laisse tomber le <CR>
break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
ce bout de code (vous pourriez utiliser les macros que j'avais proposé) void envoiesms() {
if (gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, false)) {
gsm.print("AT+CMGS=");
gsm.print(tel);
gsm.println();
delay(500);
gsm.print(SMS);
delay(500);
gsm.print((char)26);
}
}
vous pouvez simplifier le début en gsm.print("AT+CMGS=");
gsm.println(tel);
et virer le gsm.println();
il n'y a pas besoin d'attendre 1/2 secondes avant d'envoyer le caractère de fin de SMS gsm.print((char)26); - vous pouvez virer le delay(500);
char *SMS;
...
SMS = "SSID RECU : ", (ssid);
...
SMS = "MDP RECU : ", (mdp);
ça ne va pas faire ce que vous voulez
vous devriez déclarer SMS comme un tableau de caractère suffisamment long pour votre message le plus long et faire un strcpy() et un strcat() pour rajouter des trucs dans la chaîne (en fait comme quand vous envoyez le SMS vous ne passez pas dans la boucle d'attente de message, vous pouvez sans doute utiliser temporairement ce tableau pour ne pas trop manger de mémoire
dans votre vrai code il ne faudra pas mettre de
void dieHere()
{
while (true);
}
il faudra gérer quand un truc ne fonctionne pas (reset / rebooter le composant / désactiver la fonction)
J'y avait penser mais cela ne me paraissait pas prioritaire, je pense utiliser la fonction RESET
const uint32_t oneSecond = 3000ul;
--> changez le nom de la variable si ce n'est plus une seconde...
const uint32_t attente3s = 3000ul;
par exemple
oui sa fait partie des chose que je trifouille lors de mes test sans remettre après
dans la fonction boolean ecouter() vous devriez tester si vous avez reçu '\r' et ne pas l'ajouter au message car il ne sert à rien
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
case '\r': // on laisse tomber le <CR>
break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
ok je vait voir
ce bout de code (vous pourriez utiliser les macros que j'avais proposé)
void envoiesms() {
if (gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, false)) {
gsm.print("AT+CMGS=");
gsm.print(tel);
gsm.println();
delay(500);
gsm.print(SMS);
delay(500);
gsm.print((char)26);
}
}
vous pouvez simplifier le début en
gsm.print("AT+CMGS=");
gsm.println(tel);
et virer le gsm.println();
il n'y a pas besoin d'attendre 1/2 secondes avant d'envoyer le caractère de fin de SMS gsm.print((char)26); - vous pouvez virer le delay(500);
Oui sa fait encore partie des chose je je n'est pas remis en place
char *SMS;
...
SMS = "SSID RECU : ", (ssid);
...
SMS = "MDP RECU : ", (mdp);
ça ne va pas faire ce que vous voulez
vous devriez déclarer SMS comme un tableau de caractère suffisamment long pour votre message le plus long et faire un strcpy() et un strcat() pour rajouter des trucs dans la chaîne (en fait comme quand vous envoyez le SMS vous ne passez pas dans la boucle d'attente de message, vous pouvez sans doute utiliser temporairement ce tableau pour ne pas trop manger de mémoire
oui, ca ne fait pas se que je veut lol
faire un tableau ... oula ... faut je retourne voir http://www.cplusplus.com
char SMS[160]; // tableau de caractères, réserve 160 cases mémoires (octets)
...
strcpy(SMS, "SSID="); // écrit SSID= au début du tableau suivi du '\0'
strcat(SMS, ssid); // rajoute ssid à la suite, si ssid est bien une chaîne de caractères
Mais ce que je disais c'est au lieu de recréer encore un buffer pour SMS, utilisez celui déjà existant pour la réception sur le port série
donc :
SMS = "SSID RECU : ";
strncat(SMS,ssid,50);
au lieu de ca
SMS = "SSID RECU : ", (ssid);
Ça fonctionne sans que j'ai utilisé de tableau (chose que je ne maîtrise pas vraiment, même si j'ai compris le principe d'index :) )
Cela pose t'il problème de faire comme ça ?
dans la fonction boolean ecouter() vous devriez tester si vous avez reçu '\r' et ne pas l'ajouter au message car il ne sert à rien
Je ne vois pas ou je l'ai ajouter au message ... ni dans se que je tape sur mon tel :) :) :)
char SMS[160]; // tableau de caractères, réserve 160 cases mémoires (octets)
...
strcpy(SMS, "SSID="); // écrit SSID= au début du tableau suivi du '\0'
strcat(SMS, ssid); // rajoute ssid à la suite, si ssid est bien une chaîne de caractères
Mais ce que je disais c'est au lieu de recréer encore un buffer pour SMS, utilisez celui déjà existant pour la réception sur le port série
Ce qui veut dire qu'a chaque fois qu'il est lu strcpy(SMS,"fff") la tableau "sms"est effacer et recommence a zero ?
j'ai toujours le loop qui envoie message recu des qu'il a recu un sms
ne tient pas compte de se message, j'ai trouvé
écrire cela n'est pas bon
char * SMS;
...
SMS = "SSID RECU : ";
strncat(SMS,ssid,50);
ce qu'il se passe c'est que SMS est un pointeur générique vers la mémoire. mais il n'y a aucune case allouée pour stocker du contenu, vous dites simplement que SMS est variable pour stocker l'adresse d'une zone en mémoire qui va contenir des caractères.
Quand vous effectuez
SMS = "SSID RECU : "; vous dites au compilateur stocke en mémoire quelque part la chaîne
"SSID RECU : " et initialise le pointeur SMS comme pointant sur le premier caractère. ça c'est bon. Mais vous n'avez réservé aucune case mémoire supplémentaire, il se peut qu'il y ait d'autres variables de votre programme en mémoire juste après les cases réservées pour
"SSID RECU : ". Donc quand vous faites ensuite
strncat(SMS,ssid,50); vous allez écraser ce qu'il y a en mémoire juste après la fin de la chaîne de caractère et ça va mettre le souc dans votre code et c'est super dur ensuite à débugger
-->
principe: quand on veut écrire une chaîne de caractère en mémoire, il faut réserver un certain nombre de cases mémoires (octets) pour écrire dedans tout ce que l'on veut - SANS DEBORDER. pour cela on crée un tableau
char SMS[160]; // tableau de caractères, réserve 160 cases mémoires (octets)
et on peut ranger dans le tableau les caractères les uns après les autres pour faire sa phrase. attention en C et C++ une chaîne de caractère bien formée se termine par le caractère null
'\0' donc il faut toujours prévoir une case pour ce caractère même si on ne le voit pas. Par exemple
char toto[] = "bonjour";
va réserver dans le tableau toto 7 octets pour les lettres plus un octet pour le null à la fin, donc c'est un tableau de 8 caractères
OK ?
Ce qui veut dire qu'a chaque fois qu'il est lu strcpy(SMS,"fff") la tableau "sms"est effacer et recommence a zero ?
oui
strcpy() commence à stocker au niveau du pointeur et rajoutera le
'\0' à la fin tout seul. quand vous faites ensuite
strcat() la fonction cherche où est le
'\0', et écrit le texte en commençant par la case contenant le
'\0' et remet un
'\0' tout à la fin
(http://forum.arduino.cc/index.php?action=dlattach;topic=504431.0;attach=231464)
Ok jean marc,
J'avais fais cette modif et j'ai commencer a avoir les problème ci dessous, du coup j'ai trifouillé :(
Je refait les modif, je vois et jvous dis.
#define gsm Serial1
const byte tailleMessageMax = 200;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
char *ssid ;
char *mdp ;
char *tel;
char *SMS;
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 2000ul;
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
// case '\r':
// break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void envoiesms(){
Serial.print("AT+CMGS=");
Serial.println(tel);
delay(500);
Serial.print(SMS);
Serial.print((char)26);
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("erreur effacement"));
}
Serial.println("sms effacer");
}
void setup() {
Serial.begin(115200);
gsm.begin(9600);
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, true)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("Erreur delete message"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CNMI=2,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
}
if (!gsmPrintlnATCommand("AT+CSAS", OKString, oneSecond, true)) {
Serial.print(F("Error Save"));
}
if (!gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, true)) {
Serial.print(F("Error mode texte"));
}
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
Serial.println(message);
if (!strncmp(message,"+CMT:",5)){
tel = strtok(message+6,(","));
//Serial.println(tel);
SMS = "sms recu";
//Serial.println(SMS);
}
else if(strncmp(message,"Ssid",4)==0) {
ssid = strtok(message+5, "=");
SMS = "SSID RECU : ";
strncat(SMS,ssid,50);
//Serial.println(SMS);
delay(500);
envoiesms();
}
else if (strncmp(message,"Mdp",3)==0) {
mdp = strtok(message+4, "=");
SMS = "SSID RECU : ";
strncat(SMS,mdp,50);
//Serial.println(SMS);
delay(500);
envoiesms();
}
else {
//SMS = "?";
//Serial.println(SMS);
//delay(500);
envoiesms();
}
}
}
et voila ce que j'ai dans le moniteur
GPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
OK
OK
OK
OK
OK
OK
Initialisation terminé
AT+CMGS=
+CMT: "+33********","","17/10/29,10:30:20+04"
Ssid hfgjh
OK
sms effacer
Alors que je devrais avoir :
AT+CMGS="+33********"
SSID RECU : hfgjh
OK
sms effacer
J'ai remis comme tu me l'avais conseillé
#define gsm Serial1
const byte tailleMessageMax = 200;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
char *ssid ;
char *mdp ;
char *tel;
char SMS[100];
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 2000ul;
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
//case '\r':
//break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void envoiesms(){
Serial.print("AT+CMGS=");
Serial.println(tel);
delay(500);
Serial.print(SMS);
Serial.print((char)26);
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("erreur effacement"));
}
Serial.println("sms effacer");
}
void setup() {
Serial.begin(115200);
gsm.begin(9600);
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, true)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("Erreur delete message"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CNMI=2,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
}
if (!gsmPrintlnATCommand("AT+CSAS", OKString, oneSecond, true)) {
Serial.print(F("Error Save"));
}
if (!gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, true)) {
Serial.print(F("Error mode texte"));
}
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
if (!strncmp(message,"+CMT:",5)){
tel = strtok(message+6,(","));
//Serial.println(tel);
strcpy(SMS, "sms recu");
//Serial.println(SMS);
}
else if(strncmp(message,"Ssid",4)==0) {
ssid = strtok(message+5, "=");
strcpy(SMS, "SSID recu :");
strncat(SMS,ssid,50);
//Serial.println(SMS);
delay(500);
envoiesms();
}
else if (strncmp(message,"Mdp",3)==0) {
mdp = strtok(message+4, "=");
strcpy (SMS, "Mdp recu :");
strncat(SMS,mdp,50);
//Serial.println(SMS);
delay(500);
envoiesms();
}
else {
strcpy (SMS, "Instructions non comprise");
//Serial.println(SMS);
//delay(500);
envoiesms();
}
}
}
Et quand j'envoie sms j'ai sa :
GPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
OK
OK
OK
OK
OK
OK
Initialisation terminé
AT+CMGS=
Instructions non comprise+CMT: "+336*******","","17/10/29,10:46:13+04"
Ssid hfgjh
OK
sms effacer
Il ne lit plus la ligne +CMT et du coup lit le sms, trouve Ssid et envoie le sms, bien sur le numero de tel n'est pas enregistré du coup ...
Je n'arrive pas a comprendre pourquoi la ligne + cmt apparaît après ... et se cumul a Instructions non comprise
si vous voulez conserver en mémoire le No de tel il ne faut pas un pointeur dans la ligne qui sert à la réception, car au prochain tour c'est effacé. il faut que vous déclariez aussi tel comme un tableau dans lequel vous dupliquez les données
regardez cet exemple:
char reponse[] = "+CMT: \"+33600123400\",,\"17/10/28,20:50:31+01\"";
char *ptr;
const byte maxTelSize = 30;
char tel[maxTelSize + 1]; // +1 pour le caractère nul en fin de chaîne
void setup() {
int i;
Serial.begin(115200);
tel[0] = '\0'; // initalise à vide
if (!strncmp(reponse, "+CMT: ", 6)) {
ptr = strtok(reponse, ",\""); // met un '\0' sur le premier guillemet et ptr pointe sur +CMT:
ptr = strtok(NULL, ",\""); // met un '\0' sur le second guillemet et ptr pointe sur +33600123400
if (ptr) {
strncpy(tel, ptr, maxTelSize);
tel[maxTelSize] = '\0'; // juste au cas où le N° de tel était trop long, la fonction strncpy ne met pas le null en fin de chaîne
Serial.print("J'ai le No de tel suivant: [");
Serial.print(tel);
Serial.println("]");
}
} else {
Serial.println("ce n'est pas une commande +CMT");
}
}
void loop() {}
ça va afficher
J'ai le No de tel suivant: [+33600123400]
oui ton code affiche bien le numéro
Et une fois dans mon code, même chose qu'avant ...
GPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
OK
OK
OK
OK
OK
OK
Initialisation terminé
AT+CMGS=
Instructions non comprise+CMT: "+336*******","","17/10/29,11:43:57+04"
Ssid hfgjh
OK
sms effacer
#define gsm Serial1
const byte tailleMessageMax = 200;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
char *ssid ;
char *mdp ;
char SMS[100];
char *ptr;
const byte maxTelSize = 30;
char tel[maxTelSize + 1]; // +1 pour le caractère nul en fin de chaîne
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 2000ul;
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
//case '\r':
//break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void envoiesms(){
Serial.print("AT+CMGS=");
Serial.println(tel);
delay(500);
Serial.print(SMS);
Serial.print((char)26);
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("erreur effacement"));
}
Serial.println("sms effacer");
}
void setup() {
Serial.begin(115200);
gsm.begin(9600);
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, true)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("Erreur delete message"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CNMI=2,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
}
if (!gsmPrintlnATCommand("AT+CSAS", OKString, oneSecond, true)) {
Serial.print(F("Error Save"));
}
if (!gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, true)) {
Serial.print(F("Error mode texte"));
}
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
if (!strncmp(message, "+CMT: ", 6)) {
ptr = strtok(message, ",\""); // met un '\0' sur le premier guillemenet et ptr pointe sur "+CMT: "
ptr = strtok(NULL, ",\""); // met un '\0' sur le second guillemenet et ptr pointe sur "+33600123400"
if (ptr) {
strncpy(tel, ptr, maxTelSize);
tel[maxTelSize] = '\0'; // juste au cas où le N° de tel était trop long, la fonction strncpy ne met pas le null en fin de chaîne
}
}
/*if (!strncmp(message,"+CMT:",5)){
tel = strtok(message+6,(","));
//Serial.println(tel);
strcpy(SMS, "sms recu");
//Serial.println(SMS);
}*/
else if(strncmp(message,"Ssid",4)==0) {
ssid = strtok(message+5, "=");
strcpy(SMS, "SSID recu :");
strncat(SMS,ssid,50);
//Serial.println(SMS);
delay(500);
envoiesms();
}
else if (strncmp(message,"Mdp",3)==0) {
mdp = strtok(message+4, "=");
strcpy (SMS, "Mdp recu :");
strncat(SMS,mdp,50);
//Serial.println(SMS);
delay(500);
envoiesms();
}
else {
strcpy (SMS, "Instructions non comprise");
//Serial.println(SMS);
//delay(500);
envoiesms();
}
}
}
en retirant la fin du code (ou l'on cherche si ssid ou mds) le numero de tel est bien lu ... mais me repond par sms recu (puis vide) au lieu de sms recu : "le sms recu."
Y a un truc pas claire la dedant
#define gsm Serial1
const byte tailleMessageMax = 200;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
char *ssid ;
char *mdp ;
char SMS[100];
char *ptr;
const byte maxTelSize = 30;
char tel[maxTelSize + 1]; // +1 pour le caractère nul en fin de chaîne
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 2000ul;
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
//case '\r':
//break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void envoiesms(){
gsm.print("AT+CMGS=\"");
gsm.print(tel);
gsm.println("\"");
delay(5000);
gsm.println(SMS);
gsm.println((char)26);
gsm.println();
delay(1000);
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("erreur effacement"));
}
else Serial.println("sms effacer");
}
void setup() {
Serial.begin(115200);
gsm.begin(9600);
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, true)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("Erreur delete message"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CNMI=2,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
}
if (!gsmPrintlnATCommand("AT+CSAS", OKString, oneSecond, true)) {
Serial.print(F("Error Save"));
}
if (!gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, true)) {
Serial.print(F("Error mode texte"));
}
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
if (!strncmp(message, "+CMT:", 5)) {
ptr = strtok(message, ",\""); // met un '\0' sur le premier guillemenet et ptr pointe sur "+CMT: "
ptr = strtok(NULL, ",\""); // met un '\0' sur le second guillemenet et ptr pointe sur "+33600123400"
if (ptr) {
strncpy(tel, ptr, maxTelSize);
tel[maxTelSize] = '\0'; // juste au cas où le N° de tel était trop long, la fonction strncpy ne met pas le null en fin de chaîne
}
envoiesms();
}
strcpy(SMS, "sms recu :");
strncat(SMS,message,50);
}}
:smiley-sad: :smiley-sad: :smiley-sad:
Chui a bout :'(
Sa fait 10 jours chui la dessus
Sa marche a moitié après sa marche plus du tout.
et rebelote :(
(https://s-i.huffpost.com/gen/4393678/images/n-THE-MATRIX-628x314.jpg)
en retirant la fin du code (ou l'on cherche si ssid ou mds) le numero de tel est bien lu ... mais me repond par sms recu (puis vide) au lieu de sms recu : "le sms recu."
Y a un truc pas claire la dedant
#define gsm Serial1
const byte tailleMessageMax = 200;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
char *ssid ;
char *mdp ;
char SMS[100];
char *ptr;
const byte maxTelSize = 30;
char tel[maxTelSize + 1]; // +1 pour le caractère nul en fin de chaîne
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 2000ul;
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
//case '\r':
//break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void envoiesms(){
gsm.print("AT+CMGS=\"");
gsm.print(tel);
gsm.println("\"");
delay(5000);
gsm.println(SMS);
gsm.println((char)26);
gsm.println();
delay(1000);
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("erreur effacement"));
}
else Serial.println("sms effacer");
}
void setup() {
Serial.begin(115200);
gsm.begin(9600);
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, true)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("Erreur delete message"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CNMI=2,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
}
if (!gsmPrintlnATCommand("AT+CSAS", OKString, oneSecond, true)) {
Serial.print(F("Error Save"));
}
if (!gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, true)) {
Serial.print(F("Error mode texte"));
}
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
if (!strncmp(message, "+CMT:", 5)) {
ptr = strtok(message, ",\""); // met un '\0' sur le premier guillemenet et ptr pointe sur "+CMT: "
ptr = strtok(NULL, ",\""); // met un '\0' sur le second guillemenet et ptr pointe sur "+33600123400"
if (ptr) {
strncpy(tel, ptr, maxTelSize);
tel[maxTelSize] = '\0'; // juste au cas où le N° de tel était trop long, la fonction strncpy ne met pas le null en fin de chaîne
}
envoiesms();
}
strcpy(SMS, "sms recu :");
strncat(SMS,message,50);
}}
Vous appelez
envoiesms() avant de mettre me message dans la variable SMS
tel[maxTelSize] = '\0'; // juste au cas où le N° de tel était trop long, la fonction strncpy ne met pas le null en fin de chaîne
}
envoiesms(); // envoi du message
}
strcpy(SMS, "sms recu :"); // pourquoi c'est faut ensuite?
strncat(SMS,message,50);
Je vois plus rien a force de tout essayer :( et jpasse pour un ksoss en plus :smiley-confuse:
donc une fois remis dans l'ordre, en ne laissant que la condition
if (!strncmp(message, "+CMT:", 5)) {
le code donne ça :
#define gsm Serial1
const byte tailleMessageMax = 200;
char message[tailleMessageMax + 1]; // +1 car on doit avoir un caractère de fin de chaîne en C, le '\0'
char *ssid ;
char *mdp ;
char SMS[100];
char *ptr;
const byte maxTelSize = 30;
char tel[maxTelSize + 1]; // +1 pour le caractère nul en fin de chaîne
const char * ATString = "AT";
const char * OKString = "\r\nOK\r\n";
const uint32_t oneSecond = 2000ul;
boolean gsmPrintlnATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
boolean ecouter()
{
static byte indexMessage = 0; // static pour se souvenir de cette variable entre 2 appels consécutifs. initialisée qu'une seule fois.
boolean messageEnCours = true;
while (gsm.available() && messageEnCours) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
message[indexMessage] = '\0'; // on termine la c-string
indexMessage = 0; // on se remet au début pour la prochaine fois
messageEnCours = false;
break;
//case '\r':
//break;
default:
if (indexMessage <= tailleMessageMax - 1) message[indexMessage++] = (char) c; // on stocke le caractère et on passe à la case suivante
break;
}
}
}
return messageEnCours;
}
void envoiesms(){
gsm.print("AT+CMGS=\"");
gsm.print(tel);
gsm.println("\"");
delay(5000);
gsm.print(SMS);
gsm.println((char)26);
delay(1000);
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("erreur effacement"));
}
else Serial.println("sms effacer");
}
void setup() {
Serial.begin(115200);
gsm.begin(9600);
if (gsmPrintlnATCommand(ATString, OKString, oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+GSV", OKString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnATCommand("ATE0", OKString, oneSecond, true)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CMGD=4", OKString, oneSecond, true)) {
Serial.print(F("Erreur delete message"));
dieHere();
}
if (!gsmPrintlnATCommand("AT+CNMI=2,2,0,0,0", OKString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
}
if (!gsmPrintlnATCommand("AT+CSAS", OKString, oneSecond, true)) {
Serial.print(F("Error Save"));
}
if (!gsmPrintlnATCommand("AT+CMGF=1", OKString, oneSecond, true)) {
Serial.print(F("Error mode texte"));
}
Serial.println("Initialisation terminé");
}
void loop() {
if (! ecouter()) {
if (!strncmp(message, "+CMT:", 5)) {
ptr = strtok(message, ",\""); // met un '\0' sur le premier guillemenet et ptr pointe sur "+CMT: "
ptr = strtok(NULL, ",\""); // met un '\0' sur le second guillemenet et ptr pointe sur "+33600123400"
if (ptr) {
strncpy(tel, ptr, maxTelSize);
tel[maxTelSize] = '\0'; // juste au cas où le N° de tel était trop long, la fonction strncpy ne met pas le null en fin de chaîne
}
strcpy(SMS, "sms recu du :");
strncat(SMS,tel,100);
envoiesms();
}
/*else if(strncmp(message,"Ssid",4)==0) {
ssid = strtok(message+5, "=");
strcpy(SMS, "SSID recu :");
strncat(SMS,ssid,50);
envoiesms();
}
else if (strncmp(message,"Mdp",3)==0) {
mdp = strtok(message+4, "=");
strcpy (SMS, "Mdp recu :");
strncat(SMS,mdp,50);
envoiesms();
}
else {
strcpy (SMS, "Instructions non comprise");
envoiesms();
}*/
}
}
dans le serial :
GPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
OK
OK
OK
OK
OK
OK
Initialisation terminé
Azerty
>
> erreur effacement
(l'erreur d'effacement est du au fait d'être en "AT+CNMI=2,2,0,0,0" ? pas d'enregistrement mémoire donc rien a effacer = erreur ?)
et par sms je reçoit :
sms recu du :+3365******
Donc tout est OK (a par l'effacement.)
Des que je remet juste la condition:
else {
strcpy (SMS, "Instructions non comprise");
envoiesms();
}
envoie du même sms (azerty)
Dans la console :
GPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
OK
OK
OK
OK
OK
OK
Initialisation terminé
+CMT: "+3365*****","","17/10/30,09:59:46+04"
Azerty
ERROR
OK
sms effacer
Et rien par sms
par contre, si je met toute les condition sauf "else" j'ai bien un retour par sms du ssid ou mdp recu (et rien des que les message commence apr autre chose que ssid ou mdp)
J'essaye de jeter un œil en fin d'après midi je serai devant un ordi. Je bouge bcp, donc la majorité de mes posts sont faits depuis un smartphone ou une tablette - peux pas trop tester, c'est donc souvent du code "à l'aveugle" que je vous propose
Pas de problème jm, on a une vie a coté :) c'est déjà sympa de me suivre comme ça.
Quand jsuis bloqué, jcontinu de travaillé sur les chose plus simple dans le code principal / html ou sur le hardware
J'ai intégré la parti web a mon code principal.
Pas de problème a la compilation.
Mais une fois televersé, rien ne se lance.
J'ai placé des "Serial.print" a différent endroit pour voir ou était le problème et même pas uns ligne sur le moniteur ... donc j'en déduit un bug total :(
Trop lourd ?
J'ai commander une DUE, ça me permettra de pouvoir comparer les performance.
Si c'est concluant, je continurais la dessus mais je serait obliger de modifier le hardware ne serait ce que pour les pwm 3v3>>>5v
Pour esp dédié web, la partie échange avec l'arduino me parait compliqué. j'ai déjà assez de mal comme cela.
Pour récapituler l'avancement du projet :
>>>Code principal ok sans wifi et gsm
>>>Wifi seul presque ok (reste un problème de lenteur de réponse des bouton (~3 seconde avant de pouvoir re clicker pour qu'il soit pris en compte))
>>>Gsm seul presque ok ( reste un problème avec la ligne "else" correspondant a envoyer un message quand la commende n'est pas comprise)
>>>Hardware opperationel (mais déjà plein de modif dans ma tète pour la v2 surtout si utilisation d'une DUE)
Vous pouvez faire un récapitulatif complet du cahier des charges?
Vous pouvez faire un récapitulatif complet du cahier des charges?
Sans prendre en compte les difficulté de codage :gestion eclarage
gestion brassage + oscillo
Gestion alimentation
gestion température
page web visualisation des paramètre actuel / page web administrateur,paramétrage.
gsm pour alerte
Fonction supplémentairement suite échange/demande d'autre aquariophile :écran tactile pour visu + paramétrage
osmolation
pompe de dosage.
Coté hardware:220v exclusivement dans le tableau
Pas de restriction sur les composants, mega/due, esp01/esp12/esp32, écran 3.2"/5"/7"
CI "de base" + modul additionnel (le ph, écran,pompe doseuse doivent etre "plug and play" de t'elle sorte que chacun puisse adapter et faire évoluer l'automate en fonction de ces besoin/budget)
Le montage actuel:fabrication CI ~45€ (~10€ si fait maison)
Divers fourniture/connecteur/résistance ect ... ~15€
écran 3.2 ~12€
wifi esp01 ~5€
sim800 ~15€
modul ph + sonde ~15€
arduino mega ~10€
Sonde température 5€
relais finder ~7€ pièce (actuellement 4)
soit ~115€ si CI maison
Pompe de dosage ~10€ piece
écrans 7" tactil en spi, il tourne ~50€ (permet de libérer des pin sur l'arduino)
Pour "la total écran 7" + 4 pompe dosage + ph bac+rac = 208€ si CI maison ou 243€ si fait faire.
Soit 3 ou 4 fois moins chère que les automate du commerce.
La « gestion » des différents éléments ça veut dire juste pilotage d'un relai ON/OFF ? Ou plus finement chaque composant ?
Le pilotage c'est quoi? une liste (éditable?) de moment de la journée où on passe de OFF à ON puis de ON à OFF ?
Sinon ce n'est pas une super pratique d'organiser le code comme cela
void loop(){
#include"heure_temp_ph.h"
#include"eclairage.h"
#include"oscillo.h"
#include"jebao.h"
#include "alimentation.h"
#include "edf.h"
#include "action_temperature.h"
#include"affichage.h"
}
Non, la gestion comporte un code bien précis pour chaque fonction.
Pwm pour éclairage, pompe brassage ...
Fonction millis pour déplacement des oscillo
Il y a quelque fonction simple on / off celon plusieurs critère ... nourrissage, tempête ...
Le pilotage serait de mettre via un bouton (web?) la fonction en on ou off
Exemple, j'ai un automate sans écran, mettre écran "off" et que le code "oublie" cette partie du code. avec un bouton et une fonction if ca m'as l'aire assez simple.
Meme chose pour les pompe doseuse, pompe ON on lit l'ensemble de la fonction associé.
Pour l'organisation du code, moins pratique mais plus simple a comprendre et a retrouvé ces petit (pour moi en tout cas)
Bonsoir
bon j'ai enfin eu un peu de temps chez moi donc j'ai repris mon SIM800L avec ma carte SIM de test
(http://forum.arduino.cc/index.php?action=dlattach;topic=504431.0;attach=232224)
J'alimente correctement la carte par une alimentation séparée (5V 1.5A fonctionne pour moi) , je joins le GND Arduino à mon alim externe et je connecte le Rx du module SIM800L au Tx de Serial3 de mon MEGA ainsi que le Tx du module SIM800L au Rx de Serial3 de mon MEGA.
Ce module se charge des correspondances de tension donc pas besoin d'adapter à 3.3V
Je charge le code suivant dans mon arduino MEGA
#define gsm Serial3
const char * _SSID = "monSSID";
const char * _PWD = "motDePasse";
const byte ctrlZ = 0x1A;
const char * ctrlZString = "\x1A";
const char * escapeString = "\x1B";
const char * ATString = "AT";
const char * OKShortString = "OK";
const char * OKLongString = "\r\nOK\r\n";
const uint32_t oneSecond = 1000ul;
const byte maxMessageSize = 200;
char GSM_MessageLine[maxMessageSize + 1]; // +1 pour le '\0' à la fin de la c-string
// --------------------------------------
// gsmPrintlnAndWaitATCommand executes an AT commmand by adding at the end a CR LF
// then it checks if endMarker string is receivedon the GSM Serial port
// for max duration ms returns a boolean stating if the marker was found
// with the verbose option, the output from the GSM is also printed to Serial
// --------------------------------------
boolean gsmPrintlnAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean gsmPrintAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.print(command);
return waitForString(endMarker, duration, verbose);
}
// --------------------------------------
// gsmPrintATCommand or gsmpWriteATCommand is used when you don't want to send the CR LF
// at the end of the commmand line; use it to build up a multi part command
// same syntax as print as they are Variadic Macros using print or write
// --------------------------------------
#define gsmPrintATCommand(...) gsm.print(__VA_ARGS__)
#define gsmPrintlnATCommand(...) gsm.println(__VA_ARGS__)
#define gsmWriteATCommand(...) gsm.write(__VA_ARGS__)
// --------------------------------------
// read a line from gsm, ignore '\r' and returns true if '\n' is found
// --------------------------------------
boolean getGSMLine()
{
static byte indexMessage = 0;
boolean incomingMessage = true;
while (gsm.available() && incomingMessage) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
GSM_MessageLine[indexMessage] = '\0'; // trailing NULL char for a correct c-string
indexMessage = 0; // get ready for next time
incomingMessage = false;
break;
case '\r': // don't care about this one
break;
default:
if (indexMessage <= maxMessageSize - 1) GSM_MessageLine[indexMessage++] = (char) c; // else ignore it..
break;
}
}
}
return !incomingMessage;
}
// --------------------------------------
// waitForString wait max for duration ms whilst checking if endMarker string is received
// on the GSM Serial port returns a boolean stating if the marker was found
// --------------------------------------
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
void setup() {
Serial.begin(115200);
gsm.begin(9600); // suivant votre config, essayez 9600 19200 38400 57600 74880 115200
// prévoir éventuellement AT+CPIN="1234" pour rentrer le PIN
if (gsmPrintlnAndWaitATCommand(ATString, OKLongString, 5 * oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("AT+GSV", OKLongString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("ATE0", OKLongString, oneSecond, false)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("ATV1", OKLongString, oneSecond, false)) {
Serial.print(F("Erreur Message en Verbal"));
dieHere();
}
// on passe en les SMS en mode texte
if (!gsmPrintlnAndWaitATCommand("AT+CMGF=1", OKLongString, oneSecond, true)) {
Serial.print(F("Error AT+CMGF"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
// on passe en mode routage des SMS vers le terminal
if (!gsmPrintlnAndWaitATCommand("AT+CNMI=1,2,0,0,0", OKLongString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
Serial.println("Initialisation terminé");
}
boolean sendSMS(const char * noDeTel, const char * messageAEnvoyer)
{
boolean SMSenvoye = false;
gsmPrintATCommand("AT+CMGS=\"");
gsmPrintATCommand(noDeTel);
if (gsmPrintlnAndWaitATCommand("\"", ">", oneSecond, false)) {
gsmPrintlnATCommand(messageAEnvoyer);
SMSenvoye = gsmPrintlnAndWaitATCommand(ctrlZString, OKShortString, 5 * oneSecond, false);
} else {
gsmPrintlnATCommand(escapeString);
}
return SMSenvoye;
}
void loop()
{
if (getGSMLine()) {
// ********************************************
// TEST RECEPTION D'UN SMS
// +CMT: "+33620000000",,"01/03/22,16:33:31+00"<CRLF>
// texte bla bla bla<CRLF>
// ********************************************
if (!strncmp(GSM_MessageLine, "+CMT:", 5)) {// 5 parce que "+CMT:" c'est 5 caractères, on compare le début de la chaîne
const byte maxDigitsPhoneNb = 30;
char phoneNb[maxDigitsPhoneNb + 1];
char *ptr = strtok(GSM_MessageLine, "\"");
if (ptr) {
ptr = strtok(NULL, "\"");
strncpy(phoneNb, ptr, maxDigitsPhoneNb);
phoneNb[maxDigitsPhoneNb] = '\0';
// *** DEBUG *** POUR VOIR CE QU'IL SE PASSE
Serial.print(F("SMS de = ")); Serial.println(phoneNb);
// ***
while (!getGSMLine()); // on attend le texte du SMS
// *** DEBUG *** POUR VOIR CE QU'IL SE PASSE
Serial.print(F("Message = [")); Serial.print(GSM_MessageLine); Serial.println(F("]"));
// ***
if (!strncmp(GSM_MessageLine, "CONFIG", 6)) { // 6 parce que 6 caractères dans le mot "CONFIG"
// on a rerçu un SMS nous demandant la config, on construit la réponse
strcpy(GSM_MessageLine, "La config est la suivante:\r\nSSID = ");
strcat(GSM_MessageLine, _SSID);
strcat(GSM_MessageLine, "\r\nMot de Passe = ");
strcat(GSM_MessageLine, _PWD);
sendSMS(phoneNb, GSM_MessageLine);
} else {
sendSMS(phoneNb, "Pas compris");
}
}
}
}
}
le lance, la console me dit
GPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800L
Revision:1418B02SIM800L24
OK
OK
OK
Initialisation terminéJ'envoie depuis mon iPhone un SMS au module dans lequel j'écris "Coucou", la console série me dit
SMS de = +336xxxxxxxx
Message = [Coucou]
et je reçois en retour sur mon iPhone un SMS en provenance de ma carte qui me dit
Pas Compris
J'envoie un nouveau SMS au module dans lequel j'écris "CONFIG" (en majuscules, sans les guillemets), la console série me dit
SMS de = +336xxxxxxxx
Message = [CONFIG]
et je reçois en retour sur mon iPhone un SMS en provenance de ma carte qui me dit
La config est la suivante:
nSSID = monSSID
Mot de Passe = motDePasse
si j'envoie d'autres SMS ça fonctionne sans soucis - mais le seul SMS compris c'est CONFIG
testez si ça fonctionne pour vous
oui, tout fonctionne
GPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
OK
OK
OK
Initialisation termin⸮
SMS de = +336****
Message = [Coucou]
SMS de = +336****
Message = [CONFIG]
SMS de = +336****
Message = [Test autre message]
et j'ai bien recu:
Pas Compris
puis
La config est la suivante:
nSSID = monSSID
Mot de Passe = motDePasse
puis
Pas Compris
Si je compare ce code a celui que testai précédemment ce qui me saute au yeux est cette ligne
while (!getGSMLine()); // on attend le texte du SMS
Qui, suite a ligne "+cmt, atend la réception du message pour lancer la suite d'analyse via la commande strncmp
Alors que dans le code precedent, le message était analysé avec tout les condition d'affilé que ce soit pour les message +cmt ou pour le sms recu.
C'est bien ca ?
Le +CMT est sur une ligne et le SMS derrière (d'ailleurs je me rends compte que ça ne fonctionne pas si le sms est sur plusieurs lignes, en fait faudrait lire jusqu'au marqueur de fin de sms)
Les sms envoyer seront de format "Ssid monssid" ou "Mdp monmdp" donc il ne seront pas sur plusieurs lignes (sauf si chaque ligne est limité en caractère?)
Pour recuperer le ssid recu j'ai donc
Modifier const char * _SSID = "monSSID" en char * _SSID (meme declaration que la page web)
Puis utiliser la même formule que dans le code précédent et que dans la page web a savoir
_SSID = strtok(GSM_MessageLine+5,"=");
Qui devrait faire correspondre _SSID a la suite de caractère situer a partir du caractère 6 inclu.
Exemple sms envoyer "Ssid pourtest", ssid = pourtest
Au lieu de ça, j'ai un reboot du modul :(
GPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
OK
OK
OK
Initialisation terminé
SMS de = +336*****
MessageGPRS Modem OK
SIMCOM_Ltd
SIMCOM_SIM800A
Revision:1418B04SIM800A24
OK
OK
OK
Initialisation terminé
attention e suis en serial1 au lieu de 3 comme vous
#define gsm Serial1
char * _SSID;
char * _PWD;
const byte ctrlZ = 0x1A;
const char * ctrlZString = "\x1A";
const char * escapeString = "\x1B";
const char * ATString = "AT";
const char * OKShortString = "OK";
const char * OKLongString = "\r\nOK\r\n";
const uint32_t oneSecond = 1000ul;
const byte maxMessageSize = 200;
char GSM_MessageLine[maxMessageSize + 1]; // +1 pour le '\0' à la fin de la c-string
// --------------------------------------
// gsmPrintlnAndWaitATCommand executes an AT commmand by adding at the end a CR LF
// then it checks if endMarker string is receivedon the GSM Serial port
// for max duration ms returns a boolean stating if the marker was found
// with the verbose option, the output from the GSM is also printed to Serial
// --------------------------------------
boolean gsmPrintlnAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean gsmPrintAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.print(command);
return waitForString(endMarker, duration, verbose);
}
// --------------------------------------
// gsmPrintATCommand or gsmpWriteATCommand is used when you don't want to send the CR LF
// at the end of the commmand line; use it to build up a multi part command
// same syntax as print as they are Variadic Macros using print or write
// --------------------------------------
#define gsmPrintATCommand(...) gsm.print(__VA_ARGS__)
#define gsmPrintlnATCommand(...) gsm.println(__VA_ARGS__)
#define gsmWriteATCommand(...) gsm.write(__VA_ARGS__)
// --------------------------------------
// read a line from gsm, ignore '\r' and returns true if '\n' is found
// --------------------------------------
boolean getGSMLine()
{
static byte indexMessage = 0;
boolean incomingMessage = true;
while (gsm.available() && incomingMessage) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
GSM_MessageLine[indexMessage] = '\0'; // trailing NULL char for a correct c-string
indexMessage = 0; // get ready for next time
incomingMessage = false;
break;
case '\r': // don't care about this one
break;
default:
if (indexMessage <= maxMessageSize - 1) GSM_MessageLine[indexMessage++] = (char) c; // else ignore it..
break;
}
}
}
return !incomingMessage;
}
// --------------------------------------
// waitForString wait max for duration ms whilst checking if endMarker string is received
// on the GSM Serial port returns a boolean stating if the marker was found
// --------------------------------------
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
void setup() {
_SSID = "monSSID";
_PWD = "motDePasse";
Serial.begin(115200);
gsm.begin(9600); // suivant votre config, essayez 9600 19200 38400 57600 74880 115200
// prévoir éventuellement AT+CPIN="1234" pour rentrer le PIN
if (gsmPrintlnAndWaitATCommand(ATString, OKLongString, 5 * oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("AT+GSV", OKLongString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("ATE0", OKLongString, oneSecond, false)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("ATV1", OKLongString, oneSecond, false)) {
Serial.print(F("Erreur Message en Verbal"));
dieHere();
}
// on passe en les SMS en mode texte
if (!gsmPrintlnAndWaitATCommand("AT+CMGF=1", OKLongString, oneSecond, true)) {
Serial.print(F("Error AT+CMGF"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
// on passe en mode routage des SMS vers le terminal
if (!gsmPrintlnAndWaitATCommand("AT+CNMI=1,2,0,0,0", OKLongString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
Serial.println("Initialisation terminé");
}
boolean sendSMS(const char * noDeTel, const char * messageAEnvoyer)
{
boolean SMSenvoye = false;
gsmPrintATCommand("AT+CMGS=\"");
gsmPrintATCommand(noDeTel);
if (gsmPrintlnAndWaitATCommand("\"", ">", oneSecond, false)) {
gsmPrintlnATCommand(messageAEnvoyer);
SMSenvoye = gsmPrintlnAndWaitATCommand(ctrlZString, OKShortString, 5 * oneSecond, false);
} else {
gsmPrintlnATCommand(escapeString);
}
return SMSenvoye;
}
void loop()
{
if (getGSMLine()) {
// ********************************************
// TEST RECEPTION D'UN SMS
// +CMT: "+33620000000",,"01/03/22,16:33:31+00"<CRLF>
// texte bla bla bla<CRLF>
// ********************************************
if (!strncmp(GSM_MessageLine, "+CMT:", 5)) {// 5 parce que "+CMT:" c'est 5 caractères, on compare le début de la chaîne
const byte maxDigitsPhoneNb = 30;
char phoneNb[maxDigitsPhoneNb + 1];
char *ptr = strtok(GSM_MessageLine, "\"");
if (ptr) {
ptr = strtok(NULL, "\"");
strncpy(phoneNb, ptr, maxDigitsPhoneNb);
phoneNb[maxDigitsPhoneNb] = '\0';
// *** DEBUG *** POUR VOIR CE QU'IL SE PASSE
Serial.print(F("SMS de = ")); Serial.println(phoneNb);
// ***
while (!getGSMLine()); // on attend le texte du SMS
// *** DEBUG *** POUR VOIR CE QU'IL SE PASSE
Serial.print(F("Message = [")); Serial.print(GSM_MessageLine); Serial.println(F("]"));
// ***
if (!strncmp(GSM_MessageLine, "Ssid", 4)) { // 6 parce que 6 caractères dans le mot "CONFIG"
// on a rerçu un SMS nous demandant la config, on construit la réponse
_SSID = strtok(GSM_MessageLine+5,"=");
strcpy(GSM_MessageLine, "La config est la suivante:\r\nSSID = ");
strcat(GSM_MessageLine, _SSID);
sendSMS(phoneNb, GSM_MessageLine);
} else {
sendSMS(phoneNb, "Pas compris");
}
}
}
}
}
faire ça ce n'est pas top _SSID = "monSSID";
_PWD = "motDePasse";
quel est l'objectif ? soit vous voulez un tableau de caractères, soit un pointeur, mais faire pointer votre pointeur sur une chaîne constante va faire aboyer le compilo....
Qu'est-ce que vous envoyez comme SMS? un truc du genre Ssid=coucou ?
en supposant que ce soit cela et donc qu'on ait ça dans GSM_MessageLine
quand vous faites
if (!strncmp(GSM_MessageLine, "Ssid", 4))
ça compare les 4 caractères du début de GSM_MessageLine avec Ssid --> ici c'est bon donc on rentre dans le if
vous exécutez alors: _SSID = strtok(GSM_MessageLine+5,"=");
qui a pour effet de remplacer le prochain = par un caractère null '\0' et de faire pointer votre variable _SSID au milieu du buffer GSM_MessageLine (une c-string qui contient donc plus que " coucou". (et éventuellement tout ce qui avait aussi derrière coucou)
Notez que c'est aussi simple d'écrire _SSID = GSM_MessageLine+6;...
ensuite vous faites strcpy(GSM_MessageLine, "La config est la suivante:\r\nSSID = ");
strcat(GSM_MessageLine, _SSID);
et c'est là le drame :)
votre premier strcpy écrase GSM_MessageLine avec la chaîne "La config est la suivante:\r\nSSID = " et votre pointeur _SSID pointe toujours au milieu de cette chaîne qui vient d'être écrasée... et faire un strcat() avec des éléments se chevauchant c'est pas une bonne idée. (cf la doc de strcat() (http://en.cppreference.com/w/cpp/string/byte/strcat)--> The behavior is undefined if the strings overlap.
c'est pour cela que si vous regardez mon code où j'extrais le N° de tel de l'appelant, j'ai un petit buffer local dans lequel je copie depuis GSM_MessageLine le numéro de téléphone pour les mettre au chaud ailleurs --> faut faire pareil
essayez avec ce bout de code (je suis sur mon tel, donc peut pas tester)
if (!strncmp(GSM_MessageLine, "Ssid", 4)) { // 4 parce que 4 caractères dans le mot "Ssid"
// on a rerçu un SMS nous demandant la config, on construit la réponse
const byte maxSSID = 30;// prévoir la bonne longueur
char copieDuSSID[maxSSID+1];
_SSID = strchr(GSM_MessageLine + 5, "="); // Notez que c'est aussi simple d'écrire _SSID = GSM_MessageLine+6;...
strncpy(copieDuSSID, _SSID,maxSSID);
copieDuSSID[maxSSID] = '\0';
strcpy(GSM_MessageLine, "La config est la suivante:\r\nSSID = ");
strcat(GSM_MessageLine, copieDuSSID);
sendSMS(phoneNb, GSM_MessageLine);
} else {
sendSMS(phoneNb, "Pas compris");
}
Je ne voyait pas sa comme sa ...
une fois _SSID = je pensait que la valeur était enregistrer dans _SSID et donc que la chaîne GSM_MessageLine pouvait être modifier sans que cela affecte _SSID
_SSID = "monSSID";
_PWD = "motDePasse";
etait la pour le test puisque dans le code final il ne seras pas present vu qu'il sera definit par le ssid ou mds recu par sms.
Donc dans le serial le sms est bien lu mais la réponse recu par sms est toujours la même quelque soit le sms envoyer commencant pas ssid
La config est la suivante : SSID = d
Bon, matinée passé sur l'aquabouns bien sur mais pas sur le code ...
Voici un détail de les composant utilisé et de toute les fonctionnalités prévu de l'aquaboun's
N'hésité pas a me dire si certain point ne vous semble pas clair.
Fichier trop gros pour etre joint ici
aquaboun's.doc (https://www.partage-fichiers.com/upload/ks5nt5qf/)
Et oui je suis toujours la :)
Et plutôt avec du positif :)
J'ai reçu il y a 3 jours mon d1 pro
J'ai donc passé 3 soirée a adapter le code web pour le téléverser directement dans le d1.
Se matin, ça y est !!!
C'est fonctionnel :)
Mais avec un petit lot de problème quand même Pour le positif :- affichage des pages web et web admin OK
- fonctionnalité des boutons OK, problème de "non prise en compte des Click" disparu et même mieux puisque le Click sur un bouton avant même que la page soit complètement chargé est pris en compte!
Pour le negatif :- ~10 secondes pour chargé complètement la page admin (codage client.print(F a modifier ?)
- sauvegarde dans l'eeprom du d1 au lieu de l'arduino
- ? avec mon code la lumière bleu du D1 est éteinte, avec les code exemple allumé. problème ?
- mon adaptation a surement du crée des fautes dans le code même si fonctionnel
- une erreur avec "tempsprecedent1 = millis - 10000;" >>> "invalid conversion from 'long unsigned int (*)()' to 'long unsigned int' [-fpermissive]" alors que OK quand televersé dans l'arduino
Le voici ci joint
un peu occupé en ce moment m'ai hier soir j'ai pondu un bout de code pour vous montrer comment échanger des données entre le MEGA et le D1 mini par serial pour séparer la partie web de la partie contrôle. je suis en vadrouille donc je n'ai pas testé le code, dès que je peux valider que ce que j'ai écrit tourne je vous le posterai.
dans: tempsprecedent1 = millis - 10000; --> il ne vous manque pas des parenthèses pour millis() (écrit comme cela, millis et l'adresse mémoire de la fonction donc le compilateur n'est pas content)
Pour la perf d'affichage malheureusement on reste dans des perf de petits ordinateurs - faudrait optimiser la page web, utiliser potentiellement de la compression et le SPIFFS pour les partie statique etc...
ensuite il y a à tester des trucs un peu plus avancés comme un client.setNoDelay(1); (une fois que vous avez fait WiFiClient client = server.available(); par exemple - et si vous avez des données en fichier SPIFFS, un client.write(webFile); devrait booster un max
faudra que je creuse un peu
un peu occupé en ce moment m'ai hier soir j'ai pondu un bout de code pour vous montrer comment échanger des données entre le MEGA et le D1 mini par serial pour séparer la partie web de la partie contrôle. je suis en vadrouille donc je n'ai pas testé le code, dès que je peux valider que ce que j'ai écrit tourne je vous le posterai.
(http://idata.over-blog.com/4/18/49/01/GIFS/genial.gif)
dans: tempsprecedent1 = millis - 10000; --> il ne vous manque pas des parenthèses pour millis() (écrit comme cela, millis et l'adresse mémoire de la fonction donc le compilateur n'est pas content)
Pfff effectivement, toujours ces petit detail ...
Mais pourquoi il n'y avait pas d'erreur de compilation quand envoyer dans l'arduino ?
Pour la perf d'affichage malheureusement on reste dans des perf de petits ordinateurs - faudrait optimiser la page web, utiliser potentiellement de la compression pour les partie statique etc...
J'adhererai a cette théorie si l'esp-01 relié a l'arduino était aussi long ou plus long mais il met ~2 seconde a m'afficher cette page, le d1 en "direct" devrait être plus performant et non l'inverse.
A moins qu'il me manque une explication :smiley-confuse:
Pour mon petit .doc, si vous l'avez vu, tout est assez claire ? faut il plus d'info ?
Pour la compression mon html a l'air pas mal puisque les outils de compression indique
Taux de compression: 99%
8 985 octets avant
8 865 octets apres
SPIFFS
Oula ... ca a l'air une usine a gaz :o
Pour la compression mon html a l'air pas mal puisque les outils de compression indique
Taux de compression: 99%
8 985 octets avant
8 865 octets apres
Euh je ne suis pas sûr de quels outils vous parlez là... si vous gzip-ez votre HTML vous verrez qu'il devient bcp bcp plus petit - le texte se comprimant bien
Ensuite il faudrait essayer de pousser des lignes plus longues dans le client.print (ce qui ne pose pas de soucis de RAM grâce au fait qu'on lit directement en progmem) - à cause des ACK mais faut que je regarde plus précisément
Euh je ne suis pas sûr de quels outils vous parlez là... si vous gzip-ez votre HTML vous verrez qu'il devient bcp bcp plus petit - le texte se comprimant bien
j'ai chercher sur le net a compresser ma page html.
je suis tombé sur different logitiel dont Absolute HTML Compressor
Je pense comprendre que vous parliez de ça (https://www.alsacreations.com/article/lire/914-compression-pages-html-css-gzip-deflate.html) mais pour mettre tout ça dans le code ... il faut que j'devienne insomniaque lol
Bonjour - finalement je n'ai pas pu m'approcher des mes arduino dans la semaine, je teste dans l'après midi le bout de code Arduino <<-- SERIAL -->> Wemos D1 et vous poste l'exemple d'échange de données
le gzip c'est pratique avec des pages statiques mais comme vous avez beaucoup de dynamique ça ne va pas aider bcp. il faudra optimiser les ACK de la communication wifi je ferai 2 ou 3 tests
Pour l'ACK,
Au vu de la description trouvé ici (http://searchnetworking.techtarget.com/definition/ACK)
cela rejoindrais l'idée que j'avais sur ce qui se passe dans le serial lorsque je demande la page :
WiFi connected
Server started
192.168.1.54
conected
conected
conected
conected
conected
conected
conected
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
conected
available
n
Donc je mettait dit que cela devait être long a cause de ca.
c'est bien lié a l'ACK ?
En creusant un peu ce sera difficile de contourner simplement l'ACK
Il faudra donc passer à l'étape suivante - un petit coup d'AJAX sans doute - faut que je trouve le temps de faire un petit tuto
un petit coup d'AJAX sans doute - faut que je trouve le temps de faire un petit tuto
Laisse tombé, y a ma femme pour ça !
(https://img.coopathome.ch/produkte/880_880/RGB/3181694_001.jpg?_=1473689089446)
(https://st2.depositphotos.com/1007989/5894/i/950/depositphotos_58949549-stock-photo-smiley-using-a-duster.jpg)
plus sérieusement, si je comprend bien l'ajax permettrais de mettre a jour les variable sur la page web sans avoir besoin de la recharger ?
Pas très galant tout cela ...
Oui avec AJAX la page statique pourrait venir du SPIFFS (un système de fichier simple sur l'ESP) et cette page mettrait à jour des sous parties par une approche AJAX. comme on envoie moins de données c'est plus efficace malgré les ACK et c'est plus joli à l'oeil Car on ne recharge pas toute la page
petit début de tuto (http://forum.arduino.cc/index.php?topic=511147.0)posté pour la partie SPIFFS
le reste quand j'ai un peu plus de temps....
Comme conseiller j'ai installer la bibliothèque FSBrowserNG et tout celle associées.
Cloner manuellement l'esp8266
Et j'ai fini par réussir a compiler sans erreur.
J'ai donc voulu televerser le code exemple Userconfigexemple.
Et j'arrive toujours a ce message:
Archiving built core (caching) in: C:\Users\djbouns\AppData\Local\Temp\arduino_cache_384035\core\core_esp8266com_esp8266_d1_mini_lite_CpuFrequency_80,FlashSize_1M0,LwIPVariant_open,Debug_Disabled,DebugLevel_None____,UploadSpeed_115200_59f27b87b1649ba33697614d4ec8334b.a
Le croquis utilise 367911 octets (35%) de l'espace de stockage de programmes. Le maximum est de 1023984 octets.
Les variables globales utilisent 40820 octets (49%) de mémoire dynamique, ce qui laisse 41100 octets pour les variables locales. Le maximum est de 81920 octets.
java.io.IOException: Cannot run program "C:\Program Files (x86)\Arduino\hardware\esp8266com\esp8266/tools/esptool/esptool.exe": CreateProcess error=2, Le fichier spécifié est introuvable
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
at processing.app.helpers.ProcessUtils.exec(ProcessUtils.java:26)
at cc.arduino.packages.Uploader.executeUploadCommand(Uploader.java:129)
at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:207)
at cc.arduino.UploaderUtils.upload(UploaderUtils.java:78)
at processing.app.SketchController.upload(SketchController.java:713)
at processing.app.SketchController.exportApplet(SketchController.java:686)
at processing.app.Editor$DefaultExportHandler.run(Editor.java:2168)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException: CreateProcess error=2, Le fichier spécifié est introuvable
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init>(ProcessImpl.java:386)
at java.lang.ProcessImpl.start(ProcessImpl.java:137)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
... 8 more
Une erreur est survenue lors du transfert du croquis
C:\Program Files (x86)\Arduino\hardware\esp8266com\esp8266/tools/esptool/esptool.exe
Le fichier spécifié est introuvable
esptool.exe est bien a cette emplacement.
Une idée ?
J'ai intégré la parti web a mon code principal.
Pas de problème a la compilation.
Mais une fois televersé, rien ne se lance.
J'ai placé des "Serial.print" a différent endroit pour voir ou était le problème et même pas uns ligne sur le moniteur ... donc j'en déduit un bug total :(
Depuis ce jour la , impossible de refaire fonctionné mon code, même en retirant toute la partie web ...
Ça va passer par la fenêtre ...
il faut peut-être revenir à la version précédente de la librairie pour votre code?
il faut peut-être revenir à la version précédente de la librairie pour votre code?
Je n'ai pas changer de librairie ...
Depuis ce jour la , impossible de refaire fonctionné mon code, même en retirant toute la partie web ...
Ça va passer par la fenêtre ...
Avec vos #include de code source c'est quasiment impossible de comprendre la structure de la loop, le chevauchement possible de variables et l'allocation mémoire....
Peut être débordez vous la mémoire au run time ?
Il faut que que cree tout ces fonction en VOID et que je les laisses en une page ?
l'idéal serait de définir "proprement" ce que doit faire la loop() sous forme sans doute d'une machine à état pour chacun des sous systèmes.
Chaque appel dans la loop() doit être non bloquant et rapide.
l'idéal serait de définir "proprement" ce que doit faire la loop() sous forme sans doute d'une machine à état pour chacun des sous systèmes.
Chaque appel dans la loop() doit être non bloquant et rapide.
Je ne comprend pas du tout comment mettre cela en forme de cette façon.
A tu un exemple sous cette forme que je vois stp ?
cf mon tuto éventuellement (http://forum.arduino.cc/index.php?topic=470879.0)
La loop() écoute les différents événements possibles et agit en conséquence. Il y a une variable globale (ou plusieurs si plusieurs sous systèmes) qui vont aider à savoir là où on en est dans les différents traitements.
c'est pour cela que je vous demandais le cahier des charges. il y a un bon début de boulot dans ce que vous avez partagé, mais il manque la "logique" (ce qu'il faut faire) pour chaque sous système . Quelles décisions doivent être prises par l'Arduino et en fonction de quoi
Comme très souvent je comprend le principe ... Ya qu'a ... lol
la plus part des fonctions sont generé en fonction de l'heure.
exemple, de 10h a 12h lever soleil
de 12h a 20h soleil zenith
20h a 22h coucher soleil
de 22h a 10h eteint
Le code donne ca:
if ((time >= LEVERbleu1) && (time < finLEVERbleu1)){
pwmbleu1 = map(time, LEVERbleu1, finLEVERbleu1, 0, puissancebleu);
analogWrite (bleu1, pwmbleu1);
}
else if ((time >= COUCHERbleu1) && (time < finCOUCHERbleu1)){
pwmbleu1 = map(time, COUCHERbleu1, finCOUCHERbleu1, puissancebleu, 0);
analogWrite (bleu1, pwmbleu1);
}
else if ((time >= finLEVERbleu1 ) && (time < COUCHERbleu1)) {
pwmbleu1 = puissancebleu;
analogWrite(bleu1, puissancebleu);
}
else {
pwmbleu1 = 0;
analogWrite(bleu1, 0);
Dans le loop il faut donc mettre la lecture de l'heure.
Dans le setup, associé chaque créneau horaire a une fonction
Déclarer chaque fonction en VOID
c'est ca ?
J'ai vu d'autre exemple (mais pas d'exemple avec des condition comme moi) sur le net et si je les adaptes j'arrive a :
void debutleverblanc1 {
if ((Time >= debut_LEVER_blanc1) && (Time < fin_LEVER_blanc1)){
pwm_blanc1 = map(Time, debut_LEVER_blanc1, fin_LEVER_blanc1, 0, puissance_blanc);
analogWrite (pinOUT_blanc1, pwm_blanc1);
}
}
void debutcoucherblanc1{
if ((Time >= debut_COUCHER_blanc1) && (Time < fin_COUCHER_blanc1)){
pwm_blanc1 = map(Time, debut_COUCHER_blanc1, fin_COUCHER_blanc1, puissance_blanc, 0);
analogWrite (pinOUT_blanc1, pwm_blanc1);
}
}
void finleverblanc1 {
if ((Time >= fin_LEVER_blanc1 ) && (Time < debut_COUCHER_blanc1)) {
pwm_blanc1 = puissance_blanc;
analogWrite(pinOUT_blanc1, puissance_blanc);
}
}
Mais pour le else ?
else {
pwm_blanc1 = 0;
analogWrite(pinOUT_blanc1, 0);
}
Si je le met dans un void il ne feras pas le lien avec les if dans les void précédent ?
Donc dans le serial le sms est bien lu mais la réponse recu par sms est toujours la même quelque soit le sms envoyer commencant pas ssid
La config est la suivante : SSID = d
Je me suis re penché sur la partie SMS.
Je suis arrivé a avoir une réponse correcte a mes demandes.
Voyer vous des incohérences ? si non, je l'inclus dans mon code principale
#define gsm Serial1
char * _SSID ;
char * _MDP ;
const byte ctrlZ = 0x1A;
const char * ctrlZString = "\x1A";
const char * escapeString = "\x1B";
const char * ATString = "AT";
const char * OKShortString = "OK";
const char * OKLongString = "\r\nOK\r\n";
const byte max_SSID = 50;// prévoir la bonne longueur
char copie_SSID[max_SSID+1];
const byte max_MDP = 50;// prévoir la bonne longueur
char copie_MDP[max_MDP+1];
const uint32_t oneSecond = 1000ul;
const byte maxMessageSize = 200;
char GSM_MessageLine[maxMessageSize + 1]; // +1 pour le '\0' à la fin de la c-string
// --------------------------------------
// gsmPrintlnAndWaitATCommand executes an AT commmand by adding at the end a CR LF
// then it checks if endMarker string is receivedon the GSM Serial port
// for max duration ms returns a boolean stating if the marker was found
// with the verbose option, the output from the GSM is also printed to Serial
// --------------------------------------
boolean gsmPrintlnAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean gsmPrintAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.print(command);
return waitForString(endMarker, duration, verbose);
}
// --------------------------------------
// gsmPrintATCommand or gsmpWriteATCommand is used when you don't want to send the CR LF
// at the end of the commmand line; use it to build up a multi part command
// same syntax as print as they are Variadic Macros using print or write
// --------------------------------------
#define gsmPrintATCommand(...) gsm.print(__VA_ARGS__)
#define gsmPrintlnATCommand(...) gsm.println(__VA_ARGS__)
#define gsmWriteATCommand(...) gsm.write(__VA_ARGS__)
// --------------------------------------
// read a line from gsm, ignore '\r' and returns true if '\n' is found
// --------------------------------------
boolean getGSMLine()
{
static byte indexMessage = 0;
boolean incomingMessage = true;
while (gsm.available() && incomingMessage) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
GSM_MessageLine[indexMessage] = '\0'; // trailing NULL char for a correct c-string
indexMessage = 0; // get ready for next time
incomingMessage = false;
break;
case '\r': // don't care about this one
break;
default:
if (indexMessage <= maxMessageSize - 1) GSM_MessageLine[indexMessage++] = (char) c; // else ignore it..
break;
}
}
}
return !incomingMessage;
}
// --------------------------------------
// waitForString wait max for duration ms whilst checking if endMarker string is received
// on the GSM Serial port returns a boolean stating if the marker was found
// --------------------------------------
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // clear buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
void setup() {
Serial.begin(115200);
gsm.begin(9600); // suivant votre config, essayez 9600 19200 38400 57600 74880 115200
// prévoir éventuellement AT+CPIN="1234" pour rentrer le PIN
if (gsmPrintlnAndWaitATCommand(ATString, OKLongString, 5 * oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("AT+GSV", OKLongString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("ATE0", OKLongString, oneSecond, false)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("ATV1", OKLongString, oneSecond, false)) {
Serial.print(F("Erreur Message en Verbal"));
dieHere();
}
// on passe en les SMS en mode texte
if (!gsmPrintlnAndWaitATCommand("AT+CMGF=1", OKLongString, oneSecond, true)) {
Serial.print(F("Error AT+CMGF"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
// on passe en mode routage des SMS vers le terminal
if (!gsmPrintlnAndWaitATCommand("AT+CNMI=1,2,0,0,0", OKLongString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
Serial.println("Initialisation terminé");
}
boolean sendSMS(const char * noDeTel, const char * messageAEnvoyer)
{
boolean SMSenvoye = false;
gsmPrintATCommand("AT+CMGS=\"");
gsmPrintATCommand(noDeTel);
if (gsmPrintlnAndWaitATCommand("\"", ">", oneSecond, false)) {
gsmPrintlnATCommand(messageAEnvoyer);
SMSenvoye = gsmPrintlnAndWaitATCommand(ctrlZString, OKShortString, 5 * oneSecond, false);
} else {
gsmPrintlnATCommand(escapeString);
}
return SMSenvoye;
}
void loop()
{
if (getGSMLine()) {
// ********************************************
// TEST RECEPTION D'UN SMS
// +CMT: "+33620000000",,"01/03/22,16:33:31+00"<CRLF>
// texte bla bla bla<CRLF>
// ********************************************
if (!strncmp(GSM_MessageLine, "+CMT:", 5)) {// 5 parce que "+CMT:" c'est 5 caractères, on compare le début de la chaîne
const byte maxDigitsPhoneNb = 30;
char phoneNb[maxDigitsPhoneNb + 1];
char *ptr = strtok(GSM_MessageLine, "\"");
if (ptr) {
ptr = strtok(NULL, "\"");
strncpy(phoneNb, ptr, maxDigitsPhoneNb);
phoneNb[maxDigitsPhoneNb] = '\0';
// *** DEBUG *** POUR VOIR CE QU'IL SE PASSE
Serial.print(F("SMS de = ")); Serial.println(phoneNb);
// ***
while (!getGSMLine()); // on attend le texte du SMS
// *** DEBUG *** POUR VOIR CE QU'IL SE PASSE
Serial.print(F("Message = [")); Serial.print(GSM_MessageLine); Serial.println(F("]"));
// ***
if (!strncmp(GSM_MessageLine, "CONFIG", 6)) { // 6 parce que 6 caractères dans le mot "CONFIG"
// on a rerçu un SMS nous demandant la config, on construit la réponse
strcpy(GSM_MessageLine, "La config est la suivante:\r\nSSID = ");
strcat(GSM_MessageLine, _SSID);
strcat(GSM_MessageLine, "\r\nMot de Passe = ");
strcat(GSM_MessageLine, _MDP);
sendSMS(phoneNb, GSM_MessageLine);
}
else if (!strncmp(GSM_MessageLine, "Ssid", 4)) { // 6 parce que 6 caractères dans le mot "CONFIG"
_SSID = GSM_MessageLine + 5; // Notez que c'est aussi simple d'écrire _SSID = GSM_MessageLine+6;...
strncpy(copie_SSID, _SSID,max_SSID);
copie_SSID[max_SSID] = '\0';
_SSID = copie_SSID;
strcpy(GSM_MessageLine, "nouveau SSID recu = ");
strcat(GSM_MessageLine, _SSID);
sendSMS(phoneNb, GSM_MessageLine);
}
else if (!strncmp(GSM_MessageLine, "Mdp", 3)) { // 6 parce que 6 caractères dans le mot "CONFIG"
_MDP = GSM_MessageLine + 4; // Notez que c'est aussi simple d'écrire _SSID = GSM_MessageLine+6;...
strncpy(copie_MDP, _MDP,max_MDP);
copie_MDP[max_MDP] = '\0';
_MDP = copie_MDP;
strcpy(GSM_MessageLine, "nouveau MDP recu = ");
strcat(GSM_MessageLine, _MDP);
sendSMS(phoneNb, GSM_MessageLine);
}
else {
sendSMS(phoneNb, "Pas compris");
}
}
}
}
}
* A mon avis ici c'est pas _SSID qu'il faut mais copie_SSID et idem pour _MDP il faut prendre copie_MDP à condition bien sûr de les avoir extraits auparavant strcat(GSM_MessageLine, _SSID);
...
strcat(GSM_MessageLine, _MDP);
* Le code suppose que votre SMS ne contienne pas plusieurs lignes - mais ça peut être une convention.
* C'est mieux quand les commentaires correspondent au code :)else if (!strncmp(GSM_MessageLine, "Ssid", 4)) { // 6 parce que 6 caractères dans le mot "CONFIG".
..
else if (!strncmp(GSM_MessageLine, "Mdp", 3)) { // 6 parce que 6 caractères dans le mot "CONFIG"
passez aussi éventuellement tous les commentaires en français, ce sera utile pour les non anglophones
* A mon avis ici c'est pas _SSID qu'il faut mais copie_SSID et idem pour _MDP il faut prendre copie_MDP à condition bien sûr de les avoir extraits auparavant
strcat(GSM_MessageLine, _SSID);
...
strcat(GSM_MessageLine, _MDP);
Au début le code était comme vous dite mais du coup le contenu n'était pas enregistré dans _SSID
C'est pour cela qua j'ai fait
_SSID = copie_SSID;
* Le code suppose que votre SMS ne contienne pas plusieurs lignes - mais ça peut être une convention.
Les MDP reseau peuvent être assez long.
j'ai testé des sms avec "MDP 20caracterre espace 5 caractères espace 20 caractères" soit 51 caractères et tout ces bien passé. Quand j'ai ensuite envoyer CONFIG, j'ai bien reçu tout le MDP soit 47 caractères.
Donc pour moi c'est OK (a voir dans le code principal + wifi)
* C'est mieux quand les commentaires correspondent au code :)
else if (!strncmp(GSM_MessageLine, "Ssid", 4)) { // 6 parce que 6 caractères dans le mot "CONFIG".
..
else if (!strncmp(GSM_MessageLine, "Mdp", 3)) { // 6 parce que 6 caractères dans le mot "CONFIG"
passez aussi éventuellement tous les commentaires en français, ce sera utile pour les non anglophones
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#define gsm Serial1
char * _SSID ;
char * _MDP ;
const byte ctrlZ = 0x1A;
const char * ctrlZString = "\x1A";
const char * escapeString = "\x1B";
const char * ATString = "AT";
const char * OKShortString = "OK";
const char * OKLongString = "\r\nOK\r\n";
const byte max_SSID = 50;// prévoir la bonne longueur
char copie_SSID[max_SSID+1];
const byte max_MDP = 50;// prévoir la bonne longueur
char copie_MDP[max_MDP+1];
const uint32_t oneSecond = 1000ul;
const byte maxMessageSize = 200;
char GSM_MessageLine[maxMessageSize + 1]; // +1 pour le '\0' à la fin de la c-string
// --------------------------------------
//GsmPrintlnAndWaitATCommand exécute une commmand AT en ajoutant à la fin CR LF
//alors, il vérifie si endMarker est recu sur le Port série GSM
//pour la durée Max , boolean si le marqueur a été trouvé
//avec l'option verbeuse, les info du GSM sont imprimées dans le serial
// --------------------------------------
boolean gsmPrintlnAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.println(command);
return waitForString(endMarker, duration, verbose);
}
boolean gsmPrintAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose)
{
gsm.print(command);
return waitForString(endMarker, duration, verbose);
}
// --------------------------------------
//GsmPrintATCommand ou gsmpWriteATCommand est utilisé quand vous ne voulez pas envoyer à CR LF
//à la fin de la ligne de commmand; utilisez il développer une partie multi commande
//la même syntaxe est imprimé comme ils sont des Macros Variadic utilisant imprimé ou écrivent
// --------------------------------------
#define gsmPrintATCommand(...) gsm.print(__VA_ARGS__)
#define gsmPrintlnATCommand(...) gsm.println(__VA_ARGS__)
#define gsmWriteATCommand(...) gsm.write(__VA_ARGS__)
// --------------------------------------
// Lise une ligne de gsm, ignorez '\r ' et des retours vrais si '\n ' est trouvé
// --------------------------------------
boolean getGSMLine()
{
static byte indexMessage = 0;
boolean incomingMessage = true;
while (gsm.available() && incomingMessage) {
int c = gsm.read();
if (c != -1) {
switch (c) {
case '\n':
GSM_MessageLine[indexMessage] = '\0'; // a la fin character NULL pour c-string correct
indexMessage = 0; // pret pour la suite
incomingMessage = false;
break;
case '\r': // n'est pas pris en compte
break;
default:
if (indexMessage <= maxMessageSize - 1) GSM_MessageLine[indexMessage++] = (char) c; // sinon ignoré
break;
}
}
}
return !incomingMessage;
}
// --------------------------------------
// WaitForString attend la duree Max ms en vérifiant si endMarker est reçue
// Sur le Port série GSM rend une déclaration boolean si le marqueur a été trouvé
// --------------------------------------
boolean waitForString(const char * endMarker, unsigned long duration, boolean verbose)
{
int localBufferSize = strlen(endMarker);
char localBuffer[localBufferSize];
int index = 0;
boolean endMarkerFound = false;
unsigned long currentTime;
memset(localBuffer, '\0', localBufferSize); // effacer buffer
currentTime = millis();
while (millis() - currentTime <= duration) {
if (gsm.available() > 0) {
if (index == localBufferSize) index = 0;
localBuffer[index] = (uint8_t) gsm.read();
if (verbose) Serial.print((char) localBuffer[index]);
endMarkerFound = true;
for (int i = 0; i < localBufferSize; i++) {
if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
endMarkerFound = false;
break;
}
}
index++;
}
if (endMarkerFound) break;
}
return endMarkerFound;
}
void dieHere()
{
while (true);
}
void setup() {
Serial.begin(115200);
gsm.begin(9600); // suivant votre config, essayez 9600 19200 38400 57600 74880 115200
// prévoir éventuellement AT+CPIN="1234" pour rentrer le PIN
if (gsmPrintlnAndWaitATCommand(ATString, OKLongString, 5 * oneSecond, false)) {
Serial.println(F("GPRS Modem OK"));
} else {
Serial.println(F("Erreur GPRS Modem"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("AT+GSV", OKLongString, oneSecond, true)) {
Serial.print(F("Erreur Version"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("ATE0", OKLongString, oneSecond, false)) {
Serial.print(F("Erreur Echo off"));
dieHere();
}
if (!gsmPrintlnAndWaitATCommand("ATV1", OKLongString, oneSecond, false)) {
Serial.print(F("Erreur Message en Verbal"));
dieHere();
}
// on passe en les SMS en mode texte
if (!gsmPrintlnAndWaitATCommand("AT+CMGF=1", OKLongString, oneSecond, true)) {
Serial.print(F("Error AT+CMGF"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
// on passe en mode routage des SMS vers le terminal
if (!gsmPrintlnAndWaitATCommand("AT+CNMI=1,2,0,0,0", OKLongString, oneSecond, true)) {
Serial.print(F("Error AT+CNMI"));
// dieHere(); // on ne meurt pas ici mais AT+CNMI non configurgé
}
Serial.println("Initialisation terminé");
}
boolean sendSMS(const char * noDeTel, const char * messageAEnvoyer)
{
boolean SMSenvoye = false;
gsmPrintATCommand("AT+CMGS=\"");
gsmPrintATCommand(noDeTel);
if (gsmPrintlnAndWaitATCommand("\"", ">", oneSecond, false)) {
gsmPrintlnATCommand(messageAEnvoyer);
SMSenvoye = gsmPrintlnAndWaitATCommand(ctrlZString, OKShortString, 5 * oneSecond, false);
} else {
gsmPrintlnATCommand(escapeString);
}
return SMSenvoye;
}
void loop()
{
if (getGSMLine()) {
// ********************************************
// TEST RECEPTION D'UN SMS
// +CMT: "+33620000000",,"01/03/22,16:33:31+00"<CRLF>
// texte bla bla bla<CRLF>
// ********************************************
if (!strncmp(GSM_MessageLine, "+CMT:", 5)) {// 5 parce que "+CMT:" c'est 5 caractères, on compare le début de la chaîne
const byte maxDigitsPhoneNb = 30;
char phoneNb[maxDigitsPhoneNb + 1];
char *ptr = strtok(GSM_MessageLine, "\"");
if (ptr) {
ptr = strtok(NULL, "\"");
strncpy(phoneNb, ptr, maxDigitsPhoneNb);
phoneNb[maxDigitsPhoneNb] = '\0';
// *** DEBUG *** POUR VOIR CE QU'IL SE PASSE
Serial.print(F("SMS de = ")); Serial.println(phoneNb);
// ***
while (!getGSMLine()); // on attend le texte du SMS
// *** DEBUG *** POUR VOIR CE QU'IL SE PASSE
Serial.print(F("Message = [")); Serial.print(GSM_MessageLine); Serial.println(F("]"));
// ***
if (!strncmp(GSM_MessageLine, "CONFIG", 6)) { // 6 parce que 6 caractères dans le mot "CONFIG"
// on a reçu un SMS nous demandant la config, on construit la réponse
strcpy(GSM_MessageLine, "La config est la suivante:\r\nSSID = ");
strcat(GSM_MessageLine, _SSID);
strcat(GSM_MessageLine, "\r\nMot de Passe = ");
strcat(GSM_MessageLine, _MDP);
sendSMS(phoneNb, GSM_MessageLine);
}
else if (!strncmp(GSM_MessageLine, "Ssid", 4)) { // 4 parce que 4 caractères dans le mot "Ssid"
// on a reçu un SMS nous indiquant un nouveau Ssid, on construit la réponse
_SSID = GSM_MessageLine + 5; // on extrait le Ssid du message
strncpy(copie_SSID, _SSID,max_SSID);
copie_SSID[max_SSID] = '\0';
_SSID = copie_SSID;
strcpy(GSM_MessageLine, "nouveau SSID recu = ");
strcat(GSM_MessageLine, _SSID);
sendSMS(phoneNb, GSM_MessageLine);
}
else if (!strncmp(GSM_MessageLine, "Mdp", 3)) { // 3 parce que 3 caractères dans le mot "Mdp"
// on a reçu un SMS nous indiquant un nouveau Mdp, on construit la réponse
_MDP = GSM_MessageLine + 4; // on extrait le Mdp du message
strncpy(copie_MDP, _MDP,max_MDP);
copie_MDP[max_MDP] = '\0';
_MDP = copie_MDP;
strcpy(GSM_MessageLine, "nouveau MDP recu = ");
strcat(GSM_MessageLine, _MDP);
sendSMS(phoneNb, GSM_MessageLine);
}
else {
sendSMS(phoneNb, "Instruction non comprise");
}
}
}
}
}
:)
J'ai fait au mieux pour la traduction
Ah oui j'avais raté le _SSID = copie_SSID; donc ça va (vous auriez pu directement utiliser copie_SSID)
Ah oui j'avais raté le _SSID = copie_SSID; donc ça va (vous auriez pu directement utiliser copie_SSID)
Dans la partir wifi le ssid et mdp sont déclare en char *
J'ai donc fait comme ca pour que la "fusion" des code ne pause pas de problème.
Comme très souvent je comprend le principe ... Ya qu'a ... lol
la plus part des fonctions sont generé en fonction de l'heure.
exemple, de 10h a 12h lever soleil
de 12h a 20h soleil zenith
20h a 22h coucher soleil
de 22h a 10h eteint
Le code donne ca:
if ((time >= LEVERbleu1) && (time < finLEVERbleu1)){
pwmbleu1 = map(time, LEVERbleu1, finLEVERbleu1, 0, puissancebleu);
analogWrite (bleu1, pwmbleu1);
}
else if ((time >= COUCHERbleu1) && (time < finCOUCHERbleu1)){
pwmbleu1 = map(time, COUCHERbleu1, finCOUCHERbleu1, puissancebleu, 0);
analogWrite (bleu1, pwmbleu1);
}
else if ((time >= finLEVERbleu1 ) && (time < COUCHERbleu1)) {
pwmbleu1 = puissancebleu;
analogWrite(bleu1, puissancebleu);
}
else {
pwmbleu1 = 0;
analogWrite(bleu1, 0);
Dans le loop il faut donc mettre la lecture de l'heure.
Dans le setup, associé chaque créneau horaire a une fonction
Déclarer chaque fonction en VOID
c'est ca ?
Et pour organiser mon code ?
J'ai cree des void pour chaque fonction "complète"
sa compile en fonctionne et j'ai remarqué une difference :
avant :
Le croquis utilise 13600 octets (5%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 1288 octets (15%) de mémoire dynamique, ce qui laisse 6904 octets pour les variables locales. Le maximum est de 8192 octets.
apres:
Le croquis utilise 13464 octets (5%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 746 octets (9%) de mémoire dynamique, ce qui laisse 7446 octets pour les variables locales. Le maximum est de 8192 octets.
Cela fait partie du but recherché ?
Non le but est vraiment de structurer correctement la boucle. si la mémoire allouée baisse, vérifier exactement ce que vous avez fait au niveau des variables globales - votre #include précédent mettait tout dans la boucle principale, en vrac
Petit point (amical) sur le vocabulaire un void ne veut rien dire dans votre contexte, on appelle cela une fonction. void c'est juste le type retourné par la fonction - c'est à dire "rien" :)
Ok pour void les fonctions :)
Voila le code modifié,
C'est plus clair ou il y a encore des choses a modifier ?
Je suis depuis quelle jours sur l'écriture et lecture de la carte SD
Dans un premier temps pour y stocker mon Ssid et Mdp mais par la suite pouvoir stocker mes mesure et y accéder via internet (bref, on en est pas la :) )
Je suis donc arrivé au code ci dessous
#include <SPI.h>
#include <SD.h>
File myFile;
char * ssid;
char lecture[50];
int i=0 ;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
if (!SD.begin(53)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("ssid.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
Serial.print("Writing to ssid.txt...");
myFile.seek(0);
myFile.println("t'en a pas marre d'ecrire des conneries ?");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening ssid.txt");
}
// re-open the file for reading:
myFile = SD.open("ssid.txt");
if (myFile) {
Serial.println("ssid.txt:");
// read from the file until there's nothing else in it:
while (myFile.available()>0){
lecture[i] = myFile.read();
i++;
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
ssid = lecture;
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Serial.print(ssid);
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
void loop() {
// nothing happens after setup
}
et je pense avoir enfin de compris (je crois) le fonctionnement de "i"
Dans le cas présent on lis octet par octet le texte du fichier sd.
Le premier caractère est stocker dans "lecture [1]" le second dans "lecture[2]" ...
et donc au final lecture = caractère 1+2 ...
Si c'est ça, j'ai mis le temps ... :) mieux vos tard que jamais :)
Y a t'il des erreur/amélioration a faire sur ce code ?
et je pense avoir enfin de compris (je crois) le fonctionnement de "i"
Dans le cas présent on lis octet par octet le texte du fichier sd.
Le premier caractère est stocker dans "lecture [1]" le second dans "lecture[2]" ...
et donc au final lecture = caractère 1+2 ...
Si c'est ça, j'ai mis le temps ... :) mieux vos tard que jamais :)
Bravo :)
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
Je vous recommande d'utiliser SDFat (https://github.com/greiman/SdFat) au lieu de SD. C'est du même auteur que celle qui est dans l'IDE mais elle est plus avancée, plus robuste.
Pour
Serial.begin(9600);
prenez l'habitude plutôt de travailler à 115200 ou 74880 (pour les ESP souvent leur configuration par défaut)
Pour
if (!SD.begin(53)) {
éviter de coder en dur 53. ce que vous passez en paramètre c'est la broche matérielle SS (broche 10 sur la plupart des cartes Arduino et 53 sur la Mega) et donc puisque vous utilisez la vraie, autant utiliser son petit nom
if (!SD.begin(SS)) {
ce qui fait que le code est plus portable (va fonctionner aussi sur UNO).
Quand vous faites cela
// read from the file until there's nothing else in it:
while (myFile.available() > 0) {
lecture[i] = myFile.read();
i++;
}
il serait bon de tester que vous ne dépassez pas la taille du tableau
lecture sinon ça va mettre la pagaille dans la mémoire.
sinon ça devrait être bon (j'ai pas testé - juste lu le code)
Ok pour void les fonctions :)
Voila le code modifié,
C'est plus clair ou il y a encore des choses a modifier ?
je tâcherai de trouver un peu plus de temps pour regarder cela - mais c'est bcp de code donc....
j'ai déjà noté un bug
int duree_nourissage = 20; // durée du nourissage minute/ pompe de brassage au minimum
...
if (millis() - temps_precedent_nourissage <= (duree_nourissage * 60000)) {
comme
duree_nourissage est un entier, l'expression
duree_nourissage * 60000 va être effectuée en entier et ça va faire un overflow il faut écrire
duree_nourissage * 60000ulJe vous conseille de déclarer tout ce qui touche au temps en
unsigned long et de ne pas avoir de valeurs qui trainent dans le code comme 60000 mais de les déclarer avec un joli nom au début du code (ou dans un .h que vous importez) du genre
const unsigned long uneMinute = 60000ul; // 60 secondes en millisecondes
le code de votre test devient ensuite
if (millis() - temps_precedent_nourissage <= (duree_nourissage * uneMinute)) {
ce qui est déjà un peu plus lisible, mais j'irai un cran plus loin et je définirais
duree_nourissage directement en ms comme ça le code se lit partout plus simplement (et petite prime la durée du nourissage peut être maintenant gérée plus finement comme 1 min 30 par exemple)
unsigned long duree_nourissage = 20 * uneMinute; // durée du nourissage minute/ pompe de brassage au minimum
...
if (millis() - temps_precedent_nourissage <= duree_nourissage) {
--> à changer partout, par exemple ici aussi vous avez le même soucis
else if (millis() - temps_precedent_tempette <= (duree_tempette * 60000)) {
Pour la gestion du temps vous avez déclaré en variables globales
unsigned long temps_precedent_oscillo, temps_precedent_nourissage, temps_precedent_tempette;
, comme je pense qu'elles ne servent pas ailleurs que dans les fonctions associées, pour simplicité de lecture il vaut mieux les déclarer (bien sûr en
static pour que la valeur ne soit pas perdue en sortie de fonction) chacune directement dans la fonction adéquate
else if (brassage1_ON == 1) { // si brassage1 ON
...
else if (brassage1_ON == 2) { // si brassage1 PWM
au lieu d'utiliser 1, 2 qui sont des valeurs qu'on ne comprend pas, créez un
enum pour avoir des petits noms sympas
enum modeBrassage_t : byte {brassageArret=0, brassageActif=1, brassagePWM=2}; // valeurs données pour l'exemple, ce seraient les valeurs par défaut car un enum compte depuis 0
modeBrassage_t brassage1_ON = brassagePWM, brassage2_ON = brassagePWM, brassage3_ON = brassagePWM;
...
else if (brassage1_ON == brassageActif) { // si brassage1 ON
...
else if (brassage1_ON == brassagePWM { // si brassage1 PWM
C'est quand même plus lisible, non?
Pourquoi aller lentement sur la console Série?
Serial.begin(9600);
Les boutons ne sont pas en INPUT_PULLUP? vous avez une résistance externe?
pinMode(pinIN_bouton_alimentation, INPUT);
pinMode(pinIN_bouton_tempete, INPUT);
Je vois que vous utilisez toujours un DS1307, mon conseil serait de passer sur un DS3231 pour la stabilité
appuyez sur ctrl-T dans l'IDE, vous verrez que votre indentation deviendra toute suite plus jolie
un bon début en tout cas :)
Bravo :)
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
:) :) :) ca fait 3 ;D ;D ;D
Quand vous faites cela
// read from the file until there's nothing else in it:
while (myFile.available() > 0) {
lecture[i] = myFile.read();
i++;
}
il serait bon de tester que vous ne dépassez pas la taille du tableau lecture sinon ça va mettre la pagaille dans la mémoire.
Je m'était posé la question justement ... sans trop savoir comment je pouvais verifier ca ... :smiley-confuse:
Avec
lectureMAX strlen(myFile.read())
Puis
if (lectureMAX>50){
lecture[i] = myFile.read();
i++;
}
?
Pour la réponse sur mon code principal, j'ai pris ma journée demain pour bosser la dessus :D :D
à quel index du tableau sauvez vous le caractère lu? comment peut on savoir si on a débordé du tableau (combien de cases a-t-on?)
à quel index du tableau sauvez vous le caractère lu? comment peut on savoir si on a débordé du tableau (combien de cases a-t-on?)
au caractère 0.
Lecture a été déclaré char lecture [50], d'ou if (lectureMAX>50)
mais peut être que if (lectureMAX>lecture) serait mieux
J'essaye à nouveau:lecture[i] = myFile.read();
à quel index écrit-on le caractère lu?
lecture ::)
lecture ::)
je vais enlever le bon point... :) que vous aviez eu quand vous aviez expliqué
Le premier caractère est stocké dans "lecture [1]" le second dans "lecture[2]" ...
L'index c'est le N° de la case (l'adresse de l'octet en mémoire depuis le début du tableau pour faire simple dans notre cas avec des caractères) dans laquelle on va stocker le caractère.... donc c'est la variable
i qui indique cela...
lecture c'est le nom du tableau (et accessoirement un pointeur sur la première case du tableau). Quand vous avez déclaré
char lecture[50];
vous créez un tableau qui peut contenir 50 caractères. Les indices commencent à 0 et donc le dernier endroit de stockage autorisé dans ce tableau est à l'index 49.
Je reviens donc à ma question: que devez vous tester quand vous écrivez
lecture[i] = myFile.read();
pour vous assurer de ne pas écrire dans une case non autorisée ?
J'ai bien compris le "i"
Me retire pas mon point :(
8)
C'est la tournure de la question :P
strcmp (à quel index écrit-on le caractère lu?, que devez vous testez quand vous écrivez)
:smiley-twist:
Il ne faut pas que "i" dépasse 49.
J'ai bien compris le "i"
Me retire pas mon point :(
8)
donné c'est donné, reprendre c'est voler :)
C'est la tournure de la question :P
strcmp (à quel index écrit-on le caractère lu?, que devez vous testez quand vous écrivez)
:smiley-twist:
je n'ai jamais parlé de strcmp()... c'est vous... non il n'y a pas besoin de strcmp() et surtout pas si vous n'avez pas mis un caractère nul à la fin de la chaîne...
Il ne faut pas que "i" dépasse 49.
ben voilà - donc quel est le bout de code en C qui dirait
si i est inférieur ou égal à 49 alors ajouter l'élément lu dans le tableau puis augmenter i ??
const byte lectureMAX = 49;
char lecture (lectureMAX+1);
while (myFile.available() > 0) {
if (i=<lectureMAX){
lecture[i] = myFile.read();
i++;
}
}
char lecture (lectureMAX+1); euh.... :)
c du chipotage la :P
const byte lectureMAX = 49;
char lecture [lectureMAX+1];
while (myFile.available() > 0) {
if (i=<lectureMAX){
lecture[i] = myFile.read();
i++;
}
}
=< .. euh... :)
ouai, c'est comme droite et gauche ... j'inverse tout le temps :smiley-confuse:
const byte lectureMAX = 49;
char lecture [lectureMAX+1];
while (myFile.available() > 0) {
if (i<=lectureMAX){
lecture[i] = myFile.read();
i++;
}
}
ouai, c'est comme droite et gauche ... j'inverse tout le temps :smiley-confuse:
c'est comme en français on dit "inférieur ou égal" donc on écrit < puis =
sinon une fois que le tableau est plein, que pensez vous que votre while va faire puisqu'on n'a pas lu le caractère suivant? (indice - penser à l'instruction break)
il va tourné en rond ...
Longtemps ...
const byte lectureMAX = 49;
char lecture [lectureMAX+1];
while (myFile.available() > 0) {
if (i<=lectureMAX){
lecture[i] = myFile.read();
i++;
}
break
}
euh... :)
(regardez bien où vous l'avez placé)
Petite question sur la fonction millis().
Que se passe t il quand millis() devient un numérique trop grand (plus grand que unsigned long ?) ?
euh... :)
(regardez bien où vous l'avez placé)
Je l'ai modifier 3 fois ... falais bien que ce ne soit pas au bonne endroit.
J'hesitait entre le mettre dans le if ou dans while ...
Donc dans le if
Petite question sur la fonction millis().
Que se passe t il quand millis() devient un numérique trop grand (plus grand que unsigned long ?) ?
il retourne à 0 (il faut quand même plus de 54 jours)
mais si vous avez bien écrit (comme vous le faites) des
if (millis() - lastMoment >= duree) {...} alors c'est tout à fait fonctionnel même si millis() est revenu à zéro car au contraire de l'addition, la soustraction en unsigned va faire que cela va fonctionner
Je l'ai modifier 3 fois ... falais bien que ce ne soit pas au bonne endroit.
J'hesitait entre le mettre dans le if ou dans while ...
Donc dans le if
Non :)
s'il y a de la place vous voulez continuez à lire, pas vous arrêter de lire. c'est quand il n'y a plus de place que vous voulez interrompre le while... donc il faut un .... au
if
const byte lectureMAX = 49;
char lecture [lectureMAX+1];
while (myFile.available() > 0) {
if (i<=lectureMAX){
lecture[i] = myFile.read();
i++;
}
else {
break;
}
}
Arf ... avoir un code qui fonctionne (a première vu) et toute ces petite chose qui finiront pas générer des BUG ...
il retourne à 0 (il faut quand même plus de 54 jours)
Ta été calculer ça ... :-X :D
modeBrassage_t brassage1_ON = brassagePWM, brassage2_ON = brassagePWM, brassage3_ON = brassagePWM;
Cette ligne ne serre qu'a passer en pwm au démarrage ?
Si oui, elle fonctionnerais egalement comme ca :
modeBrassage_t brassage1_ON = 2, brassage2_ON = 2, brassage3_ON = 2;
? c'est pour le stockage dans l'eeprom que je pence a ça.
Mais vu que chaque pompe peut etre reglé individuelement, il faut que je fasse :
enum modeBrassage1 : byte {brassage1Arret, brassage1Actif, brassage1PWM};
enum modeBrassage2 : byte {brassage2Arret, brassage2Actif, brassage2PWM};
enum modeBrassage3 : byte {brassage3Arret, brassage3Actif, brassage3PWM};
Sinon quand je vais modifier la valeur de modeBrassage toute les pompes seront affecté ?
Ta été calculer ça ... :-X :D
je m'en souviens - il suffit de regarder ce que font 4 294 967 295 millisecondes :)
je m'en souviens - il suffit de regarder ce que font 4 294 967 295 millisecondes :)
49,71026961805556 jours
Oula ... il sont ou les 54 jours ?
ça c'est pour définir des constantes - c'est mieux que vos 1, 2 ...
enum modeBrassage : byte {brassageArret, brassageActif, brassagePWM};
ensuite vous pouvez dire
modeBrassage1 = brassageArret; // cette pompe est à l'arrêt
modeBrassage2 = brassageActif; // cette pompe est à fond
modeBrassage3 = brassagePWM; // cette pompe est en PWM
49,71026961805556 jours
Oula ... il sont ou les 54 jours ?
euh - oui c'est p't'être bien 49 et quelques et pas 54 d'ailleurs... comme quoi la mémoire :)
modeBrassage1 = brassageArret; // cette pompe est à l'arrêt
modeBrassage2 = brassageActif; // cette pompe est à fond
modeBrassage3 = brassagePWM; // cette pompe est en PWM[/code]
a et enfaite pour modifier le principe reste le même, si j'envoie 2 a brassage2 il interprete 2 comme brassagePWM
ah et en fait pour modifier le principe reste le même, si j'envoie 2 a brassage2 il interprete 2 comme brassagePWM
oui (à condition qu'ils soient définis dans le bon ordre - le premier dans la liste vaut 0, le second vaut 1, etc - sauf si vous leur donnez des valeurs) mais l'idée est vraiment d'arrêter d'utiliser des nombres magiques que personne ne comprend et plutôt des constantes avec un joli nom que tout le monde peut comprendre (ou vous dans 1 an quand vous reviendrez relire le code)
mais je pense a la page web, les bouton incrémente +1 a modeBrassage
pour ensuite faire brassage1 = modebrassage ?
Et la je ne comprend pas comment brassage2 et 3 ne vont pas passer dans le meme mode
il faut donc bien que je fasse
enum modeBrassage1 : byte {brassage1Arret, brassage1Actif, brassage1PWM};
enum modeBrassage2 : byte {brassage2Arret, brassage2Actif, brassage2PWM};
enum modeBrassage3 : byte {brassage3Arret, brassage3Actif, brassage3PWM};
Non. enum modeBrassage1 : byte {brassage1Arret, brassage1Actif, brassage1PWM};
enum modeBrassage2 : byte {brassage2Arret, brassage2Actif, brassage2PWM};
enum modeBrassage3 : byte {brassage3Arret, brassage3Actif, brassage3PWM};
vous n'avez pas défini 3 fois la valeur '1' ou '2'.... vous définissez juste un petit nom générique. ensuite vos variables prennent une de ces valeurs.
faire cela:
modeBrassage1 = brassageArret; // cette pompe est à l'arrêt
modeBrassage2 = brassageActif; // cette pompe est à fond
modeBrassage3 = brassagePWM; // cette pompe est en PWM
c'est comme faire
modeBrassage1 = 0; // cette pompe est à l'arrêt
modeBrassage2 = 1; // cette pompe est à fond
modeBrassage3 = 2; // cette pompe est en PWM
mais c'est plus lisible.
(si j'ai bien compris il y a 3 pompes de brassage hein?)
oui ok mais a quoi j'incrémente mon +1 des bouton web ?
je doit faire
enum modeBrassage : byte {brassageArret, brassageActif, brassagePWM};
int brassage1_ON = brassagePWM, brassage2_ON = brassagePWM, brassage3_ON = brassagePWM;
Et par exemple le bouton incrémente -1 a brassage1_ON qui devient a lors brassage1_ON = brassage actif ?
else if (millis() - temps_precedent_tempette <= (duree_tempette * 60000)) {
Ok, c'est vrai que c'est mieux :)
mais j'irai un cran plus loin et je définirais duree_nourissage directement en ms comme ça le code se lit partout plus simplement
Puisque ce temps est paramétrable via web, je serait obliger de faire la division donc un calcul dans les 2 cas. Il me parait plus simple ( a lire) de déclarer duree_nourissage en minute et que l'Arduino face le calcule plutôt que déclarer en millis et faire le calcul.
Pour la gestion du temps vous avez déclaré en variables globales
unsigned long temps_precedent_oscillo, temps_precedent_nourissage, temps_precedent_tempette;
, comme je pense qu'elles ne servent pas ailleurs que dans les fonctions associées
Pour temps_precedent_oscillo, Ok, utilisé dans une seule fonction.
J'ai fait :
void OSCILLO() {
static unsigned long temps_precedent_oscillo;
Pour les 2 autres, elles sont utilisées dans plusieurs fonction (dans bouton pour les faire =millis() puis dans leur fonction respective, if (temps_precedent *** <= ****)
Pourquoi aller lentement sur la console Série?
Serial.begin(9600);
Hey man … (https://www.youtube.com/watch?v=dCW4EJUS5is&list=RDdCW4EJUS5is&t=10).on n'est pas pressé …:) :) :)
Ça change quoi d'aller plus vite ?
Les boutons ne sont pas en INPUT_PULLUP? vous avez une résistance externe?
pinMode(pinIN_bouton_alimentation, INPUT);
pinMode(pinIN_bouton_tempete, INPUT);
Oui j'ai mis une résistance mais si je peut la retirer …
Je vois que vous utilisez toujours un DS1307, mon conseil serait de passer sur un DS3231 pour la stabilité
Non, non, je suis sur un ds3231 mais la bibliothèque fonctionne également.
Appuyez sur ctrl-T dans l'IDE, vous verrez que votre indentation deviendra toute suite plus jolie
Magie …
Un bon début en tout cas :)
Heu … Hin … je … je … heu …
Comment ça « début » … ???
J'y suis depuis juin :o
oui ok mais a quoi j'incrémente mon +1 des bouton web ?
je doit faire
enum modeBrassage : byte {brassageArret, brassageActif, brassagePWM};
int brassage1_ON = brassagePWM, brassage2_ON = brassagePWM, brassage3_ON = brassagePWM;
Et par exemple le bouton incrémente -1 a brassage1_ON qui devient a lors brassage1_ON = brassage actif ?
d'une part définir les variables sous le type byte pour être cohérent et économiser 3 octets :-)
Quand vous faites
enum modeBrassage : byte {brassageArret, brassageActif, brassagePWM};
modeBrassage devient le nom d'un "type" comme "byte" ou "int" donc ensuite il faut dire
modeBrassage brassage1_ON = brassagePWM, brassage2_ON = brassagePWM, brassage3_ON = brassagePWM;
pour ce qui est d'ajouter ou enlever 1, ça fonctionne toujours puisque les "petits noms" ont pris des valeurs séquentielles en comptant depuis 0.
Pour être propre généralement on rajoutera à la fin du enum une valeur "finale" qui marque la fin de la liste des modes possibles
enum modeBrassage : byte {brassageArret, brassageActif, brassagePWM, finDeListe};
et ensuite quand on clique pour passer au mode suivant vous testez si le mode courant est == à
finDeListe si oui vous passez à 0 ou
brassageArret et pour les autres c'est OK d'ajouter 1.
Non, non, je suis sur un ds3231 mais la bibliothèque fonctionne également.
--> utiliser l'objet approprié alors le 3231 pas le 1307 (il y en a plusieurs dans la librairie)
Oui j'ai mis une résistance mais si je peut la retirer …
les INPUT_PULLUP activent une résistance interne sur la pin... donc vous connectez
Pin Arduino <--> bouton <--> GND
et la pin est HIGH quand le bouton n'est pas actif et devient LOW quand active
Ça change quoi d'aller plus vite ?
votre loop() fait plein de choses et sera un peu lente et vous utilisez plein de port séries. Un buffer série ne contient que 64 octets, si les données arrivent plus vite que vous ne les lisez vous allez perdre des données. Si vous écrivez en sortie Série et que le buffer est plein (ie vous envoyez bcp de données) alors le Serial.print() ou le Serial.write() deviennent bloquant - un peu comme si vous aviez un delay(), donc plus vite les octets quittent votre arduino, plus vite votre arduino va passer à la suite du code
--> utiliser l'objet approprié alors le 3231 pas le 1307 (il y en a plusieurs dans la librairie)
Il n'y a que le commentaire que je n'ai pas changer car j'ai bien
// **************************************** horloge
RTC_DS3231 rtc;
unsigned long Time, H;
unsigned int M, S;
les INPUT_PULLUP activent une résistance interne sur la pin... donc vous connectez
Pin Arduino <--> bouton <--> GND
et la pin est HIGH quand le bouton n'est pas actif et devient LOW quand active
Je met ca de coté car la majorité des retour de futur utilisateur que j'ai eu veut un écran tactil ...
votre loop() fait plein de choses et sera un peu lente et vous utilisez plein de port séries. Un buffer série ne contient que 64 octets, si les données arrivent plus vite que vous ne les lisez vous allez perdre des données. Si vous écrivez en sortie Série et que le buffer est plein (ie vous envoyez bcp de données) alors le Serial.print() ou le Serial.write() deviennent bloquant - un peu comme si vous aviez un delay(), donc plus vite les octets quittent votre arduino, plus vite votre arduino va passer à la suite du code
A oui, je comprend ...
Mais du coup cela explique peut être le problème avec le code GSM une fois qu'il est inclue dans le code principal ...
Le gsm est en 9600 et 1 fois sur deux les sms ne sont pas reçu ou plutôt traité entièrement (le numéro de tel mais pas le message lui même) et du coup rien n'est pris en compte ...
C'est plausible ?
bonne ap
Puisque ce temps est paramétrable via web, je serait obliger de faire la division donc un calcul dans les 2 cas. Il me parait plus simple ( a lire) de déclarer duree_nourissage en minute et que l'Arduino face le calcule plutôt que déclarer en millis et faire le calcul.
ce que vous présentez à l'utilisateur n'est pas forcément la valeur interne... une multiplication c'est pas la fin du monde :)
Mais du coup cela explique peut être le problème avec le code GSM une fois qu'il est inclue dans le code principal ...
Le gsm est en 9600 et 1 fois sur deux les sms ne sont pas reçu ou plutôt traité entièrement (le numéro de tel mais pas le message lui même) et du coup rien n'est pris en compte ...
c'est possible - le GSM envoie plus de 64 octets entre l'en tête et le contenu du SMS - si vous ne le lisez pas assez vite alors vos allez perdre des données du début
c'est aussi pour cela que je disais qu'il faut bien structurer la loop() et pas tout balancer à la suite de manière à ce que les fonctions stratégiques ne soient pas privées longtemps de travailler.
Il faut peut être mieux ne pas faire de multiplication a répétition dans le loop et faire une division 1 fois lorsque l'on demande la page internet ...
Dit comme sa, sa change tout :)
ca donne ca avec toute les petite modif
J'ai instaler sdfat
#include <SPI.h>
#include <SDFat.h>
File myFile;
char * ssid;
char lecture[50];
int i=0 ;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
if (!SD.begin(53)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("ssid.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
Serial.print("Writing to ssid.txt...");
myFile.seek(0);
myFile.println("t'en a pas marre d'ecrire des conneries ?");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening ssid.txt");
}
// re-open the file for reading:
myFile = SD.open("ssid.txt");
if (myFile) {
Serial.println("ssid.txt:");
// read from the file until there's nothing else in it:
while (myFile.available()>0){
lecture.indexOf(i, myFile.read());
i++;
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
ssid = lecture;
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Serial.print(ssid);
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
void loop() {
// nothing happens after setup
}
Sa fonctionne mais des que le texte ecrit dans le fichier txt est plus court il reste le précédent ...
Il faut ecrire du "vide" jusque a arriver a 49 caractère ? ou y a une autre méthode ?
Il faut peut être mieux ne pas faire de multiplication a répétition dans le loop et faire une division 1 fois lorsque l'on demande la page internet ...
Dit comme sa, sa change tout :)
Sur le même principe cela veut dire qu'il faut que je fasse pareil pour tout mes % passer en pwm avec * 2.55 ?
Mais ca va pas etre galère a modifier après car pour modifier de 1 il faudra ajouté un décimal 2.55
vous êtes sûr qu'il compile votre code ?
c'est quoi ce délire? :)
lecture.indexOf(i, myFile.read());
encore la vitesse de tortue :) Serial.begin(9600);
ça ne sert à rien sur un Mega de faire cela: while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Il faut ecrire du "vide" jusque a arriver a 49 caractère ? ou y a une autre méthode ?
Pour faire une chaine de caractère avec un tableau de caractère (ce qu'on appelle une c-string, on en a déjà parlé) il doit se termine par un caractère nul '\0''
ensuite je ne sais pas où vous voyez des choses qui trainent? dans le ficher? sur un LCD?
vous êtes sûr qu'il compile votre code ?
c'est quoi ce délire? :)
lecture.indexOf(i, myFile.read());
Arf laisse tomber ... c'est ca davoir plusieurs fenetre ouverte avec des vieux nanare .... pffff
encore la vitesse de tortue :)
Serial.begin(9600);
Rien ne sert de courire (mais je voudrait pas dire, tout les exmple de code fournie avec les biblioteque son comme ca)
ça ne sert à rien sur un Mega de faire cela:
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Msieu Msieu, c'est pas moi ... c'est dans l'exemple ...
Bon le bon code :
#include <SPI.h>
#include "SdFat.h"
SdFat SD;
#define SD_CS_PIN SS
File myFile;
char * ssid;
const byte lectureMAX = 49;
char lecture [lectureMAX+1];
int i=0 ;
void setup() {
Serial.begin(9600);
Serial.print("Initializing SD card...");
if (!SD.begin(SD_CS_PIN)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("ssid.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
Serial.print("Writing to ssid.txt...");
myFile.seek(0);
myFile.println("bon ... tu va fonctionner !!!?");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening ssid.txt");
}
// re-open the file for reading:
myFile = SD.open("ssid.txt");
if (myFile) {
Serial.println("ssid.txt:");
// read from the file until there's nothing else in it:
while (myFile.available() > 0) {
if (i<=lectureMAX){
lecture[i] = myFile.read();
i++;
}
else {
break;
}
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
ssid = lecture;
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Serial.print(ssid);
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
void loop() {
// nothing happens after setup
}
tout les exmple de code fournie avec les biblioteque son comme ca
oui parce qu'il datent d'un temps où l'arduino de base ne savait pas faire mieux sans se mélanger les pinceaux...
sinon une fois lu le contenu du fichier, si vous mettiez un '\0' à la fin du tableau ce serait mieux ... (attention de ne pas déborder)
Pour faire une chaine de caractère avec un tableau de caractère (ce qu'on appelle une c-string, on en a déjà parlé) il doit se termine par un caractère nul '\0''
ensuite je ne sais pas où vous voyez des choses qui trainent? dans le ficher? sur un LCD?
Oui sur le fichier de la sd
au debut j'ai ecrit du text du genre "je raconte n'importe quoi"
et ensuite "je suis bo"
et je me suis retrouvé avec (en lecture) "je suis bo n'importe quoi"
c'est un exemple :smiley-grin: :smiley-grin: :smiley-grin:
et je me suis retrouvé avec (en lecture) "je suis bo n'importe quoi"
la vérité sort de la bouche des arduinos :)
deux soucis
1/ vous vous positionnez dans le fichier au début (avec le
myFile.seek(0);) et donc les caractères que vous écrivez viennent remplacer les caractères à cet endroit. Mais si le texte est plus court que l'ancien, votre fichier va conserver les anciens caractères. --> si vous voulez effacer le contenu alors le plus simple c'est de détruire le fichier s'il existe déjà et d'en créer un nouveau
2/ quand vous relisez le contenu du fichier vous ne mettez pas de caractère nul en fin de tableau. il faut le faire sinon le prochain print va imprimer jusqu'à ce qu'il trouve ce 0... si ça se trouve bcp plus loin en mêmoire que vous 50 octets même
pour le caractere 0
c'est bon ?
myFile.println("bon ... tu va fonctionner !!!?");
myFile.println("\0")
puis
else {
lecture[i] = "\0";
break;
}
Et j'ai ajouté avant d'ecrire
SD.remove ("ssid.txt");
mais du coup j'ai un saut de ligne maintenant
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
bon ... tu va fonctionner !!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
avant
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
bon ... tu va fonctionner !!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
la vérité sort de la bouche des arduinos :)
c'est pas gentil gentil
chui beau goss !
(http://scrat.hellocoton.fr/img/classic/la-tele-des-inconnus-emission-inedite-ce-soir-sur-d8-17164240.jpg)
Non ce n'est pas bon...
myFile.println("\0")
des guillemets c'est pour une chaîne de caractère, de apostrophes c'est pour un caractère. c'est pas pour rien que j'ai écrit '\0', pas "\0". (même si ici ça ne change rien mais une bonne habitude à prendre, ne pas confondre 1 caractère et une chaine d'un seul caractère)
votre bug c'est le ln
effectivement ca marche :/
a ouai ...
J'ai déjà du mal avec des truc qui saute au yeux mais si maintenant faut voir la différence ente " et ' ...
je vais devoir ouvrir grand les :o
effectivement ca marche :/
surprise :)
pour les " ou ' c'est important de bien faire la différence. un c'est un char *, l'autre un char
const char toto = 'x'; // c'est bon
const char toto = "x"; // c'est pas bon, une chaîne se terminant par un \0 il faudrait 2 caractères
const char * toto = "x"; // c'est bon
ba du coup cette partie est Ok, y plus qu'a attendre de l'intégrer avec le gsm
Je me suis basé au code du gsm pour copier le ssid une fois extrait du fichier txt.
J'ai donc fait
strncpy(ssid, lecture,lectureMAX);
et bien sur sa marche pas, rien ne se passe...
a oui, c'est pas l'histoire que ssid fix un pointeur dans lecture et que lecture varie du cout sa pointe plus sur ce que l'on veut ?
c'etait ca ... c'est bon
Bon en plus de tout ca, j'ai fait pas mal de petite modif dans le code principal dont de nouvelle fonction :
l'osmolation, qui n'était pas prévu au programme, mais vu la demande ...
void OSMOLATION() {
boolean compteur_osmolation = true;
unsigned long temps_precedent_osmolation;
unsigned long temps_attente_osmolation;
if (pinIN_floteur_osmolation == LOW) { // si floteur en position basse
if (compteur_osmolation == true) {
temps_precedent_osmolation = millis(); // activation de la duree d'osmolation
compteur_osmolation = !compteur_osmolation;
}
if (osmolation_ON <= compteur_MAX_osmolation) { //si compteur MAX pas atteind
if (millis() - temps_precedent_osmolation >= duree_osmolation_millis) { // osmolation si duree d'osmolation non depassé
digitalWrite(pinOUT_relais_osmolation, HIGH); // relais osmolateur ON
temps_attente_osmolation = millis(); // activation de la duree d'attente apres osmolation
}
else if (millis() - temps_attente_osmolation >= duree_osmolation_millis) { // attente si duree attente non depassé
digitalWrite(pinOUT_relais_osmolation, LOW); // relais osmolateur OFF
}
else { // quand duree osmolation + duree atteinte
compteur_MAX_osmolation++; // +1 au compteur d'osmolation
compteur_osmolation = !compteur_osmolation;
}
}
else { // quand compteur MAX atteind
digitalWrite(pinOUT_relais_osmolation, LOW);
// alerte
}
}
else { // quand floteur en position haute
osmolation_ON = 0;
}
}
Et un mode tempête aléatoire :
void TEMPETTE_ALEATOIR(){
if (tempette_du_jour == true){
H_tempette_aleatoir = random(fin_LEVER_blanc2, debut_COUCHER_blanc1);
duree_tempette_aleatoir = random((millis()+30000), (millis()+duree_tempette_millis));
tempette_du_jour = !tempette_du_jour;
}
if (H_tempette_aleatoir == Time){
temps_precedent_tempette_aleatoir = millis();
}
}
************
if (millis() - temps_precedent_tempette_aleatoir <= duree_tempette_aleatoir) {
pwm_jebao1,pwm_jebao2,pwm_jebao3 = puissance_tempette;
analogWrite(pinOUT_jebao1, pwm_jebao1);
analogWrite(pinOUT_jebao2, pwm_jebao2);
analogWrite(pinOUT_jebao3, pwm_jebao3);
}
Sans doute un bug ici et une petite simplification
if (digitalRead(pinIN_floteur_osmolation) == LOW) { // si floteur en position basse
if (compteur_osmolation == true) {
temps_precedent_osmolation = millis(); // activation de la duree d'osmolation
compteur_osmolation = false;
}
(Et un booléen c'est rarement un compteur :) autant appeler compteur_osmolation avec un autre nom )
Ps: on écrit tempête et aléatoire :)
a oui, c'est pas l'histoire que ssid fix un pointeur dans lecture et que lecture varie du cout sa pointe plus sur ce que l'on veut ?
Non c'est qu'il n'y a pas de cases mémoires associées à SSID ! Vous ne pouvez pas faire un strcpy() dedans... (attention quand on fait un strncpy() il faut mettre à la main un zéro dans la dernière case au cas où)
Les bouton et flotteur son relier au pin analogique.
Avant j'aurais écrit "autant écrire compteur_osmolation = false;" mais ça, c'était avant ...
Y a t'il une raison a passer par l'un plutot que l'autre ? peut être pars-qu'avant j'ai utilisé "compteur_osmolation == true"
Je ne vois pas comment l'appeler ce boolean ...
Non c'est qu'il n'y a pas de cases mémoires associées à SSID ! Vous ne pouvez pas faire un strcpy() dedans... (attention quand on fait un strncpy() il faut mettre à la main un zéro dans la dernière case au cas où)
Non c'est qu'il n'y a pas de cases mémoires associées à SSID ! ??? je ne comprend pas, je n'est rien vu de différent par rapport au code gsm.
A part que char * ssid est vide avant d'être dans strcpy. c'est ça ?
Pour le '\0' je l'avais bien mis. :)
Pour le strncpy(ssid, lecture,lectureMAX);
char * ssid; ça veut dire réserver de la place pour un pointeur (2 octets, une adresse) vers quelque part sur la mémoire, qui s'appelle ssid. mais ça ne réserve aucun espace mémoire.
char lecture[50]; ça veut dire réserver 50 cases mémoires pour y mettre des caractères, l'adresse de la première case étant connue sous le nom de lecture
Quand vous faites le strncpy(ssid, lecture,lectureMAX); vous prenez au plus lectureMAX caractères qui sont rangés consécutivement à partir de l'adresse mémoire lecture (en s'arretant au premier 0 rencontré) et vous les dupliquez (copiez) les uns derrières les autres à partir de l'adresse pointée par ssid. Comme vous n'avez pas réservé de cases pour cette copie vous allez faire n'importe quoi (et si ssid pointe au milieu de lecture alors la fonction strcpy() est interdite)
OK?
Les bouton et flotteur son relier au pin analogique.
Avant j'aurais écrit "autant écrire compteur_osmolation = false;" mais ça, c'était avant ...
Y a t'il une raison a passer par l'un plutot que l'autre ? peut être pars-qu'avant j'ai utilisé "compteur_osmolation == true"
Pas compris la question ou l'explication.
Pour le nom du booléen, un truc du genre
declencherOsmolation par exemple
OK?
Ok mais quel est l'intérêt dans mon cas d'avoir ce pointeur autent directement avoir char ssid [50] ?
C'est votre idée :)
Dans le code gsm on avait d'ailleurs fait un tableau pour l'extraire et le copier...
Un pointeur c'est juste si vous n'avez pas besoin de le conserver
et donc j'aurais :
strncpy(_SSID, GSM_MessageLine + 5,max_SSID);
_SSID[max_SSID] = '\0';
au lieu de
SSID = GSM_MessageLine + 5; // on extrait le Ssid du message
strncpy(copie_SSID, _SSID,max_SSID);
copie_SSID[max_SSID] = '\0';
_SSID = copie_SSID;
C'est votre idée :)
Oula ...
Faut pas hésiter a sortir le
smiley fouet quand c'est comme ca ...
Jcrois que c'est bon après et jcontinu tete baissé ...
et puis 3 semaine apres je comprend plus :smiley-roll-sweat:
Pfff effectivement c'est tellement plus simple !!!
Bref, c'est corrigé ...
strncpy(mdp, lecture, maxMdp);
mdp[maxMdp] = '\0'
ta vu le ptit '\0'
:)
Bon Zou ...
O dodo
Un grand merci Jean Marc pour ton aide encore aujourd'hui ... :smiley-wink:
ça avance bien :)
Pfff effectivement c'est tellement plus simple !!!
Bref, c'est corrigé ...
strncpy(mdp, lecture, maxMdp);
mdp[maxMdp] = '\0'
ta vu le ptit '\0'
:)
J'ai vu :)
Attention il faut que mdp ait été déclaré avec maxMdp+1 cases réservées
Bonne nuit!
Attention il faut que mdp ait été déclaré avec maxMdp+1 cases réservées
// **************************************** wifi
const byte maxSsid = 40;// prévoir la bonne longueur
char ssid[maxSsid + 1];
const byte maxMdp = 40;// prévoir la bonne longueur
char mdp[maxMdp + 1];
:smiley-cool: :smiley-cool: :smiley-cool: :smiley-grin:
Cool
Pour revenir sur le problème de lecture de sms.
Quelle solution faut-il envisager ?
Car même si le code est organiser la quantité d'information a traiter dans la boucle est la même ? (et va continuer a s'agrandir)
Ya t'il un code pour prioriser certaine fonction ?
Ou peut être faudra t'il que la fonction GSM soit répété a différent moment dans la boucle ?
la première chose à faire c'est de faire un timing des fonctions, quand elles tournent. Si vous voyez des fonctions trop longues il faut les découper en petits bouts pour que seulement une étape soit fait à chaque tour de boucle et pour les très longues (genre le web) il faut soit envisager de l'accélérer (cf mon tuto,sur SPIFFS) et les déporter sur un second arduino éventuellement (j'ai toujours le code dont je vous avais parlé mais pas eu le temps de le nettoyer)
Comme vous utilisez millis() pour décider quand elles doivent s'executer - assurez vous que les tempos soient telles que elles ne se déclenchent pas toute dans la même loop()
La plupart du temps ça devrait aller sans trop d'effort, mais si vous avez une fonction critique alors envisager de l'appeler entre 2 autres fonctions c'est possible aussi - du genrefonction1();
testSMS();
fonction2();
testSMS();
fonction3();
testSMS();
fonction4();
testSMS();
si vraiment c'est nécessaire
Pour le timing,
il s'agit de faire en sorte que la boucle génère t'elle fonction, puis au tour suivant une autre et ainsi de suite (et les fonction importante en continu)
Ou alors de dire pendant 500millis t'elle fonction puis la suivante ( et toujours les fonction importante en continu ?
Pour le tuto spiffs j'ai commencer mais pas encore mis a fond dessus (je me met a jour sur le "simple" de façon a n'avoir qu'a l'ajouter ensuite)
Ou alors de dire pendant 500millis t'elle fonction puis la suivante ( et toujours les fonction importante en continu ?
non 500ms c'est bcp trop pour une seule fonction
idéalement (si vous voulez que des boutons répondent par exemple) pas plus de quelques ms (moins de 5/10). En 10ms votre méga fait 160,000 instructions de base... c'est quand même pas mal de calculs
non 500ms c'est bcp trop pour une seule fonction
idéalement (si vous voulez que des boutons répondent par exemple) pas plus de quelques ms (moins de 5/10). En 10ms votre méga fait 160,000 instructions de base... c'est quand même pas mal de calculs
Petit test réalisé ... En 10 seconde, avec le code actuel (qui est loin d'être complet) :
arduino mega chinois (72 boucles soit 7.2 a la seconde soit 138.8 millis la boucle)
Arduino mega wemos d1 (102 boucles soit 10.2 a la seconde soit 98 millis la boucle)
Apres, je pourrait analysé le temps de boucle de chaque fonction ...
Mais y a t'il plus d'interet a se quelle boucle pendant un certain temps (et donc faire surement plusieurs loop) plutôt que de leur donner un ordre (1 loop puis suivante ) ?
et par exemple faire un truc du genre :
loop :
// fonction continu
wifi
gsm
affichage
bouton
// fonction tour a tour
fonction=1
eclairage
fonction=2
brassage
fonction=3
oscillo
fonction++
if fonction=4
fonction=1
C'est juste pour montrer l'exemple, me dit pas qu'il manque des parenthèse et des point virgules :)
Les petit test continu ...
Avec la mesure fonction par fonction du temps de boucle :
TIME(); // max 5 millis
OSMOLATION();// max 4 millis
ECLAIRAGE();//max 6 millis
NOURISSAGE();// max 5 millis
VENTILATION();// max 5 millis
BRASSAGE();// max 6 millis
BOUTON();// max 5 millis
TEMPERATURE();//100 millis
ALARME(); // max 5 millis
PH(); // 5 millis
Si je tiens pas le problème la ...
La fonction en question :
void TEMPERATURE() {
// **************************************** recuperation temperature ds18b20
sensors.requestTemperatures();
temp_bac = sensors.getTempCByIndex(wire_bac);
temp_rampe = sensors.getTempCByIndex(wire_rampe);
}
Vu le peut d'écriture qu'elle contient ... je ne vois pas ou j'ai pu me gourer :smiley-confuse:
Apres quelque recherche sur le net ce temps est plutôt bon :/
il est annoncé selon la résolution entre 100ms et 750ms !
a contrario, je suis surpris que toutes les autre fonctions ne prenne que 5/6ms max.
Donc pour le moment, la fonction qui seras sortie de chaque loop est la température.
Je vais remettre la fonction SMS dans le code principale en désactivant la température pour voir si le problème est réglé.
La température il faut la lire en asynchrone. Cf mon petit projet de gestion de T° et humidité dans les projets finis
Vous lancez une mesure puis vous revenez plus tard lire le résultat
visiblement cela consiste a ajouter
sensors.setWaitForConversion(false); // on travaillera en asynchrone, on n'attend pas les lectures
sensors.requestTemperatures(); // on lance une demande de lecture qui sera prête plus tard
Si c'est bien le cas, j'en arrive a un maximum de 29millis par loop sur la fonction.
boucle éffectuée : 68 fois soit : 29 millis par boucle
23.00
23.50
boucle éffectuée : 69 fois soit : 29 millis par boucle
23.00
23.50
boucle éffectuée : 70 fois soit : 28 millis par boucle
23.00
23.50
boucle éffectuée : 71 fois soit : 29 millis par boucle
23.00
23.50
boucle éffectuée : 72 fois soit : 29 millis par boucle
23.00
23.50
boucle éffectuée : 73 fois soit : 27 millis par boucle
23.00
23.50
boucle éffectuée : 74 fois soit : 29 millis par boucle
23.00
23.50
boucle éffectuée : 75 fois soit : 29 millis par boucle
23.00
23.50
boucle éffectuée : 76 fois soit : 28 millis par boucle
23.00
23.50
boucle éffectuée : 77 fois soit : 29 millis par boucle
et donc, toujours si j'ai bien compris, le serial.print de la température afficher juste après la demande correspond surement au température relever il y a quelle loop (puisque pas encore mis a jour)?
ps : il ne fait pas chaud chez moi, cela a du a la résistance 4k7 ... il m'on envoyer des 4k6 >:(
Pour pouvoir envoyer mes températures lors des alertes SMS j'i fait ca :
strcpy(GSM_Message, "!!! ALERT TEMPERATURE !!!");
dtostrf (temp_bac,5,1, buffer);
strcat(GSM_Message, buffer);
envoyerSMS(numero2tel, GSM_Message);
avec buffer[maxMessageSize] = '\0';
ça éviteras de se faire tirer les oreille :smiley-confuse:
if (compteur_osmolation == true) {
Le fait de ne mettre aucune info veut forcement dire qu'on parle de lui en "VRAI" ?
Ps: on écrit tempête et aléatoire :)
C'est fait :smiley-confuse:
Le fait de ne mettre aucune info veut forcement dire qu'on parle de lui en "VRAI" ?
Oui quand on écrit
if (expression) {...} le compilateur calcule la valeur logique (vrai ou faux) de l'expression et si c'est vrai exécute ce qui suit
L' expression ne peut pas être
void, et la règle en C ou C++ c'est que si l'espression N'est pas de type logique alors une valeur nulle veut dire faux et toute valeur non nulle veut dire vrai
(par exemple
File monFichier = SD.open("Toto.txt");
if (monFichier) {...}
ne veut pas dire si
monFichier est vrai, car son type est File pas boolean ça veut dire si
monFichier nest pas le pointeur NULL,
if (monFichier != NULL) {...} )
Dans notre cas votre
compteur_osmolation est un booléen donc déjà directement une valeur logique - pas la peine de lui dire
si (vrai == vrai) alors {...} on peut juste dire
si (vrai) {...}
avec buffer[maxMessageSize] = '\0';
ça éviteras de se faire tirer les oreille :smiley-confuse:
Mais avec dtostrf() il n'y a pas besoin, il met toujours le '\0' pour vous dans tous les cas - donc ça ne sert absolument à rien, autant l'enlever :)
Mais avec dtostrf() il n'y a pas besoin, il met toujours le '\0' pour vous dans tous les cas - donc ça ne sert absolument à rien, autant l'enlever :)
Pfff,
J'ai chercher sur le net avant parce que j'ai vu que certaine fonction l'ajoutait automatiquement et d'autre non, pour celle ci, j'ai pas trouvé donc par Default je me suis dit ... plutôt que de me faire taper sur les doigts ... et comme par hasard ... :smiley-confuse:
y a t'il une solution pour envoyer par SMS le signe dégrée ?
J'ai testé pas mal de truc (°, 0xB0, 248) sans succès, certain dise que ce n'est pas possible donc il mette un C après la température
Qu'est ce que tu pense de l'utilisation d'un arduino DUE ?
Je me suis encore mis dans une belle galère ... :smiley-confuse:
J'ai reçu mon écran ER-TFTM070-5
Selon se que j'avais lu et compris, il suffisant de brancher l'écran avec 5 fils :
RST, SCLK, SDI, SDO, SCS
et le vdd et gnd
Visiblement non ...
Et ce que j'ai du mal a comprendre c'est que quand je branche juste le vcc et gnd l'écran ne s'allume pas.
Normal ?
https://www.buydisplay.com/download/manual/ER-TFTM070-5_Datasheet.pdf
Boun's si cela peut aider.
Merci Breisleach mais je l'ai déjà lus et relus
J'ai ouvert un post sur le sujet
https://forum.arduino.cc/index.php?topic=514417.new#new
Bonjour,
J'ai lu les 25 pages avec intérêt et je précise que je suis plus que débutant mais j'apprends (ou j'essaie).
Serait il possible d'avoir le programme en entier pour que je puisses essayer de l'adapter à un aquarium d'eau douce (2000L).
En tout cas très beau travail.
Merci d'avance
Bonsoir Patoune,
Il ne me parait pas judicieux de partager un code en cour de développement, qui n'as même pas été testé.
Par contre, tu peut partager tes besoins afin de voir si il peuvent être inclue dans le projet.
Bonsoir,
Pour l'eau douce le besoin est beaucoup plus simple : éclairage progressif, 2 pompes de brassage, nourrissage, 1 sonde de température (+ chauffage), 1 capteur de niveau - 1 pompe et une électrovanne pour le changement d'eau.
C'est très simple quand on connait mais quand on est novice c'est une autre histoire.
Pour l'instant je recherche à faire fonctionner sur un écran tactile de 3,2", pour ce qui est de l'interface web et les sms je verrais après, je préfère y aller étape par étape.
J'ai essayé Sebduino et Akduino mais il y a des erreurs lors de la compilation et comme je ne m'y connait pas assez je n'arrive pas à les faire fonctionner.
Si le début de ton programme est déjà fait je peux essayer de l'adapter et le tester. Ce qui ferait 2 versions (eau de mer et eau douce).
Pour info je n'ai jamais trouvé d'automate diy pour l'eau douce.
comme je te l'ai dit même si le début est écrit il n'as jamais été testé, il comporte a coup sur des erreur d'écriture et donc des bug.
Patience ...
ps1 :l'ackduino est a téléverser a partir de codeblocks et non pas de l'ide arduino. compile et téléversement fonctionne correctement dans codeblocks :smiley-wink:
ps2 : l'ackduino est le dernier nom du projet "sebduino" et l'ackduino 1.25 "idefixes" est la derniere version de l'ackduino améliorés par idefixes, elle prend en charge les pompes jebao pour le brassage.
ps3: regarde mon post complet sur le montage de l'ackduino (http://www.recifal-france.fr/montage-d-un-ackduino-de-a-a-z-t23888.html?hilit=montage%20ackduino)et aussi le post pour toute les question sur l'ackduino (http://www.recifal-france.fr/question-sur-le-montage-d-un-ackduino-de-a-a-z-t23890.html?hilit=montage%20ackduino) et n'hesite pas a poster tes question la bas en cas de soucis
Merci,
ça y est j'ai réussi à faire fonctionner les sondes de températures et les relais, je vais passer à l'éclairage
Je ne comprends pas le fonctionnement des alarmes, je verrais plus tard.
Je vais essayer de l'adapter à mon usage (eau douce).
Petit question bête :smiley-confuse:
quand l'arduino n'est pas connecté a l'ordi, fait il tout de même les opération Serial.print ou il les zap ?
Je pe pose la question car pour debugger c'est pratique mais si une fois le code en route l'arduino travaille pour rien c'est bête ...
J'ai commencer a regarder comment faire un graphique pour avoir la température sur 24h.
J'ai fini par faire fonctionné une page :
<!DOCTYPE html>
<html>
<head>
<style>#graphe{border:1px solid;background-color:#FFFFFF}</style>
<script>
// fonction pour dessiner
function draw(f,f1) {
// Largeur et hauteur en pixels
var W=1000, H=W/2.4
var sc=W/24;
var canvas = document.getElementById("graphe");
canvas.width=W; canvas.height=H;
var ctx = canvas.getContext("2d");
// nb de pixels pour une unité
ctx.strokeStyle = "green";
ctx.moveTo(0,H/2);
ctx.lineTo(W,H/2);
ctx.stroke();
// tracé du quadrillage
ctx.strokeStyle = "#666";
ctx.beginPath();
ctx.lineWidth=0.5;
for(var i=0; i<=H/sc; i++) { // lignes horizontales
ctx.moveTo(0, H-sc*i)
ctx.lineTo(W, H-sc*i)
}
for(var i=0; i<=W/sc; i++) { // lignes verticales
ctx.moveTo(sc*i,H-0)
ctx.lineTo(sc*i, H-H)
}
ctx.stroke();
// tracé de la fonction
var x;
with(Math) {
ctx.strokeStyle = "#ff0000";
ctx.lineWidth=1.5;
ctx.beginPath();
for(x=-W/(2*sc); x<=W/(2*sc); x+=1/sc) {
ctx.moveTo(0, H/2)
ctx.lineTo(sc, 50)
ctx.lineTo(sc*2, 30)
ctx.lineTo(sc*3, 100)
ctx.lineTo(sc*4, 100)
ctx.lineTo(sc*5, 50)
ctx.lineTo(sc*6, 30)
ctx.lineTo(sc*7, 100)
ctx.lineTo(sc*8, 100)
ctx.lineTo(sc*9, 30)
ctx.lineTo(sc*10, 100)
ctx.lineTo(sc*11, 100)
ctx.lineTo(sc*12, 50)
ctx.lineTo(sc*13, 30)
ctx.lineTo(sc*14, 100)
ctx.lineTo(sc*15, 100)
ctx.lineTo(sc*16, 50)
ctx.lineTo(sc*17, 30)
ctx.lineTo(sc*18, 100)
ctx.lineTo(sc*19, 100)
ctx.lineTo(sc*20, 30)
ctx.lineTo(sc*21, 100)
ctx.lineTo(sc*22, 100)
ctx.lineTo(sc*23, 50)
ctx.lineTo(sc*24, 30)
}
}
ctx.stroke();
}
function tracer()
{
draw(0,0);
}
</script>
</head>
<body onload="tracer();afficher()">
<p><canvas id="graphe"></canvas></p>
</body>
</html>
Je l'ai donc mis dans un code arduino :
client.print(F("<style>#graphe{border:1px solid;background-color:#FFFFFF}</style><script>\r\n"));
// fonction pour dessiner
client.print(F("function draw(f, f1){ var W = 1000, H = W / 2.4;var sc = W / 24; var canvas = document.getElementById(graphe);canvas.width = W; canvas.height = H; var ctx = canvas.getContext(2d)\r\n"));
// nb de pixels pour une unité
client.print(F("ctx.strokeStyle = green; ctx.moveTo(0, H / 2);ctx.lineTo(W, H / 2);ctx.stroke()\r\n"));
// tracé du quadrillage
client.print(F("ctx.strokeStyle = #666;ctx.beginPath();ctx.lineWidth = 0.5;for (var i = 0; i <= H / sc; i++){ctx.moveTo(0, H - sc * i) ctx.lineTo(W, H - sc * i)}\r\n"));
client.print(F(" for (var i = 0; i <= W / sc; i++) {ctx.moveTo(sc * i, H - 0)ctx.lineTo(sc * i, H - H)}ctx.stroke(); \r\n"));
// tracé de la fonction
client.print(F(" var x;with(Math) {ctx.strokeStyle = #ff0000;ctx.lineWidth = 1.5;ctx.beginPath(); \r\n"));
client.print(F("for (x = -W / (2 * sc); x <= W / (2 * sc); x += 1 / sc) {ctx.moveTo(0, H / 2) \r\n"));
client.print(F("ctx.lineTo(sc, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 2, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 3, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 4, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 5, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 6, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 7, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 8, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 9, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 10, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 11, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 12, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 13, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 14, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 15, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 16, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 17, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 18, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 19, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 20, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 21, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 22, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 23, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 24, 30)}}\r\n"));
client.print(F("ctx.stroke()}function tracer(){draw(0, 0)}< / script >< / head > \r\n"));
client.print(F(" < body onload = tracer(); afficher() ><p><canvas id = graphe> < / canvas > < / p >< / body > \r\n"));
Et la page reste blanche.
Petit question bête :smiley-confuse:
quand l'arduino n'est pas connecté a l'ordi, fait il tout de même les opération Serial.print ou il les zap ?
Je pe pose la question car pour debugger c'est pratique mais si une fois le code en route l'arduino travaille pour rien c'est bête ...
il les exécute effectivement.
On peut créer des macros pour les traces debug qui gèrent les impressions sous différentes formes et on recompile en mode non débug pour la version finale
par exemple un truc du genre
#define DEBUG // à commenter pour enlever les traces
#ifdef DEBUG
#define DPRINTF(s) {Serial.print(F(s));}
#define DPRINTFV(s,v) {Serial.print(F(s)); Serial.print(v);}
#define DPRINTFX(s,v) {Serial.print(F(s)); Serial.print(F("0x")); Serial.print(v, HEX);}
#define DPRINT(x) {Serial.print(x);}
#define DPRINTLN(x) {Serial.println(x);}
#else
#define DPRINTF(s)
#define DPRINTFV(s,v)
#define DPRINTFX(s,v)
#define DPRINT(x)
#define DPRINTLN(x)
#endif
et dans le code vous n'utilisez plus
Serial.print(F("problème memoire")); mais vous faites un
DPRINTS("problème memoire"); par exemple. Quand vous recompilez en élevant la ligne déclarant DEBUG pour la version finale alors tous les DPRINT ne génère plus de code et le compilateur ne génère rien - donc ça libère de la mémoire
J'ai commencer a regarder comment faire un graphique pour avoir la température sur 24h.
J'ai fini par faire fonctionné une page :
...
Je l'ai donc mis dans un code arduino :
.....
Et la page reste blanche.
--> vous avez traduit ça
<body onload="tracer();afficher()">
<p><canvas id="graphe"></canvas></p>comme cela:
client.print(F(" < body onload = tracer(); afficher() ><p><canvas id = graphe> < / canvas > < / p >< / body > \r\n"));manquerait pas des trucs? (attention aux espaces aussi genre < / canvas > ou < / p >)
vous n'avez pas étudié mon début de tuto sur SPIFFS et AJAX? l'interface serait drôlement plus rapide et pas de réaffichage
vous n'avez pas étudié mon début de tuto sur SPIFFS et AJAX? l'interface serait drôlement plus rapide et pas de réaffichage
Non pas mi dessus car pas trop de temps ces dernier jour (1h par ci par la) et sa a l'air d'être une gros morceaux, j'attend d'être bien poser pour revenir sur la partie ESP.
(du coup je bidouille un peu, je met mon code au propre, j'essaie de comprendre cette écran de malheur ...)
Mais effectivement, si cette partie grignote sur l'écriture de la page, attend mettre ça de coté.
J'ai retiré les " car il y a erreur de compilation sinon et lorsque on fait \" c'est page blanche egalement.
les guillemets ne sont pas là pour décorer et les tags doivent être bien écrits </canvas> pas avec des espaces
Si vous prenez votre code HTML certes il affiche quelque chose en le chargeant en tant que fichier, mais si vous allez dans la console web (Chrome ou Safari on cette fonction) je vois une erreur ReferenceError: Can't find variable: afficher
onload -- toto.html:77
donc votre fichier d'origine n'est pas correct - même si quelque chose s'affiche
Depuis votre arduino - connectez vous, puis dans le menu du navigateur passez en mode développement, regardez le code HTML reçu et comparez le à celui que vous avez écrit à la main. Sont-ils identiques à 100% ? --> si ce n'est pas le cas, modifiez la partie Arduino pour en tenir compte
passez en mode développement
8)
Ça m'as pris 2h mais c'est pratique,
Maintenant ca fonctionne et plus d'erreur en prime :)
client.print(F("<style>#graphe{border:1px solid;background-color:#FFFFFF}</style><script>\r\n"));
// fonction pour dessiner
client.print(F("function draw(f, f1){var W = 1000, H = W/2.4;var sc = W/24; var canvas = document.getElementById(\"graphe\");\r\n"));
client.print(F("canvas.width = W; canvas.height = H; var ctx = canvas.getContext(\"2d\");\r\n"));
// nb de pixels pour une unité
client.print(F("ctx.strokeStyle = \"green\"; ctx.moveTo(0, H/2);ctx.lineTo(W, H/2); ctx.stroke();\r\n"));
// tracé du quadrillage
client.print(F("ctx.strokeStyle = \"#666\";ctx.beginPath();ctx.lineWidth = 0.5;for (var i = 0; i <= (H/sc); i++){ctx.moveTo(0, H - sc * i);ctx.lineTo(W, H - sc * i)}\r\n"));
client.print(F(" for (var i = 0; i <= (W/sc); i++) {ctx.moveTo(sc * i, H - 0);ctx.lineTo(sc * i, H - H)}ctx.stroke(); \r\n"));
// tracé de la fonction
client.print(F(" var x; with(Math){ctx.strokeStyle = \"#ff0000\" ;ctx.lineWidth = 1.5;ctx.beginPath(); \r\n"));
client.print(F("for (x = -W / (2 * sc); x <= W / (2 * sc); x += 1 / sc) {ctx.moveTo(0, H / 2) \r\n"));
client.print(F("ctx.lineTo(sc, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 2, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 3, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 4, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 5, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 6, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 7, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 8, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 9, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 10, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 11, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 12, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 13, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 14, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 15, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 16, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 17, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 18, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 19, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 20, 30)\r\n"));
client.print(F("ctx.lineTo(sc * 21, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 22, 100)\r\n"));
client.print(F("ctx.lineTo(sc * 23, 50)\r\n"));
client.print(F("ctx.lineTo(sc * 24, 30)}}\r\n"));
client.print(F("ctx.stroke();}function tracer(){draw(0, 0);}</script></head>\r\n"));
client.print(F("<body onload =\"tracer()\"><p><canvas id =\"graphe\"></canvas></p></body>\r\n"));
Cool
oui sauf que (a quand ce jour ou il n'y aurais plus de, sauf, mais ...)
Sur internet exploreur, cela n'est pas pris en charge (d'après se que j'ai pu lire) et que su chrome il y a toujours se problème de lenteur déjà rencontré avec la page "normal".
Je met ca de coté jusqu'à pouvoir me pencher sur la partie wifi.
Peut être vendredi si j'ai ma journée
La lenteur ne sera traitée que par le passage en SPIFFS et AJAX...
J'ai trouvé un graphique plus adapté, plus jolie, regroupant tout les relevé de paramètre en même temps et fonctionnant sous IE :)
<!DOCTYPE HTML>
<html lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>[...]</title>
<style type="text/css">
</style>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Q(m3/h)');
data.addColumn('number', 'T du BAC');
data.addColumn('number', 'T de la rampe');
data.addColumn('number', 'PH BAC');
data.addColumn('number', 'PH RAC');
data.addRow([ "0", 9.0, 11.5, 13.5, 14.5]);
data.addRow([ "1", 8.5, 11.0, 13.0, 14.5]);
data.addRow([ "2", 7.8, 9.5, 12.5, 14.0]);
data.addRow([ "3", 6.5, 9.0, 11.5, 13.5]);
data.addRow([ "4", 5.2, 8.0, 11.0, 12.5]);
data.addRow([ "5", null, 6.0, 9.0, 11.0]);
data.addRow([ "6", null, 4.0, 7.5, 9.0]);
data.addRow([ "7", null, null, 5.75, 7.5]);
data.addRow([ "8", null, null, null, 6.0]);
data.addRow([ "9", null, null, null, null]);
data.addRow([ "10", 9.0, 11.5, 13.5, 14.5]);
data.addRow([ "11", 8.5, 11.0, 13.0, 14.5]);
data.addRow([ "12", 7.8, 9.5, 12.5, 14.0]);
data.addRow([ "13", 6.5, 9.0, 11.5, 13.5]);
data.addRow([ "14", 5.2, 8.0, 11.0, 12.5]);
data.addRow(["15", 5, 6.0, 9.0, 11.0]);
data.addRow(["16", 6, 4.0, 7.5, 9.0]);
data.addRow(["17", 8, null, 5.75, 7.5]);
data.addRow(["18", 12, null, null, 6.0]);
data.addRow(["19", 15, null, null, null]);
data.addRow(["20", 7, 6.0, 9.0, 11.0]);
data.addRow(["21", 9, 4.0, 7.5, 9.0]);
data.addRow(["22", 22, null, 5.75, 7.5]);
data.addRow(["23", 15, null, null, 6.0]);
data.addRow(["24", 19, null, null, null]);
var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
chart.draw(data, { width: 1920, height: 1080, title: 'Relever les paramètres',
hAxis: {title: 'Heure', minValue: 0, maxValue: 24, textStyle:{ fontSize : 12}},
vAxis: {title: 'Temperature', minValue: 0, maxValue: 45, textStyle:{ fontSize : 12}, gridlines:{ color: '#aaa', count: 50}},
});
}
</script>
</head>
<body>
<div id="chart_div"> </div>
</body>
</html>
la page s'affiche.
Dans chrome j'ai une "erreur orange" mais je ne comprend pas ce que je doit faire :
jsapi: 22 Un script inter-bloqueur, intersite (c'est-à-dire différent eTLD + 1), https://www.google.com/uds/?file=visualization&v=1&packages=corechart, est appelé via document.write. La demande de réseau pour ce script PEUT être bloquée par le navigateur dans ce chargement de page ou dans un futur chargement de page en raison d'une mauvaise connectivité réseau. S'il est bloqué dans cette page, il sera confirmé dans un message de console ultérieur. Voir https://www.chromestatus.com/feature/5718547946799104 pour plus de détails.
google.loader.f @ jsapi: 22
Coté arduino,
je cherche a "transformer" les lignes
data.addRow([ "13", 6.5, 9.0, 11.5, 13.5]);
le but étant de na pas avoir a tout numéroter :
exemple,
si i=1:
colonne 1h les valeur temp_bac"i", temp_rampe"i",ph_BAC"i" et ph_RAC"i" seront lu.
puis i++
donc i=2:
colonne 2h les valeur temp_bac"i", temp_rampe"i",ph_BAC"i" et ph_RAC"i" seront lu.
puis i++
ect ...
J'ai testé pas mal de truc mais je me demande si cela est possible car dans les code que j'ai déjà vu, cette méthode sert pour "affecter" les caractere dans une chaîne char (dsl si ces mal exprimé)
:D :D :D
J'ai trouvé 8)
j'ai fait le test sur 24 secondes (plutôt qu'attendre 24h :smiley-razz: )
j'ai fait
float temp_bac_graph[25],temp_rampe_graph[25],ph_BAC_graph[25],ph_RAC_graph[25];
////////////////////////////////////////////////////////////////////////////////////////////
void GRAPHIQUE(){
i=S;
if ((i<25)&&(testgraph==0)){
temp_bac_graph[i]=temp_bac;
temp_rampe_graph[i] = temp_rampe;
ph_BAC_graph[i] = ph_BAC;
ph_RAC_graph[i] =ph_RAC;
Serial.println(i);
}
}
//////////////////////////////////////////////////////////////////////////////////
i=0;
while (i<25){
client.print(F("data.addRow([\""));client.print(i);client.print(F("\",\r\n"));client.print(temp_bac_graph[i]);client.print(F(","));client.print(temp_rampe_graph[i]);
client.print(F(","));client.print(ph_BAC_graph[i]);client.print(F(","));client.print(ph_RAC_graph[i]);client.print(F("]);\r\n"));
i++;
}
j'ai mis mon doigt sur la sonde "température bac" pour faire varier la T
TinDinnnnn: (https://img15.hostingpics.net/pics/456139Sanstitre.jpg)
Ps: j'ai pris 5% de mémoire dynamique avec cette fonction.
Du au float ? (25 float de chaque variable (4) de 4 octets = 400 octets ?)
Les variables globales utilisent 5332 octets (65%) de mémoire dynamique, ce qui laisse 2860 octets pour les variables locales. Le maximum est de 8192 octets.
400 fait bien 4.88% de 8192 donc je pense que ces ça...
Donc, il faut trouver une solution pour stocker sur SD je suppose ..
pas envie de coder ce soir alors je lit "juste" le tuto sur l'esp.
la première partie pour mettre en mémoire la page html ma l'air accessible.
après ça se complique avec ajax
xml je patauge
et quand je vois que le nombre de page que tu a encore réservé :smiley-confuse: :smiley-confuse: :smiley-confuse:
Tu a du voir de quoi jetait capable jusque a présent.
Je me demande vraiment si j'ai le niveau pour me lancer la dedans et ne pas finir bloqué ...
Il faut également penser que d'autre utilisateur devrons faire certaine de ces manipulations pour pouvoir utiliser l'aquabouns et certains on pas/peu de connaissance dans l'arduino.
Si mon projet ne peut être monté et utilisé par peu de monde, il perd tout intérêt pour la majorité des aquariophiles.
Qu'en pence tu ? dois je revoir mes ambitions a la baisse ou foncer ? jusque ou aller pour rester accessible a la majorité ?
Oui c'est un bon point - le soucis étant que la performance ne va pas être bonne en HTML, peut être il faut se concentrer sur la solution avec l'ecran TFT
l'ecran TFT
J'ai aussi réfléchit a cette solution et n'avoir que la visualisation des paramètre sur la page web, via le graphique (quand on est en vacance par exemple) sans avoir la page de paramétrage, qui est très long des que l'on touche au bouton.
reste le problème de l'écran ...
Oui c'est un bon point
J'en est gagné un de plus ? :smiley-wink: ;D
Si ce n'est pas indiscret, tu travaille dans quoi ?
J'en est gagné un de plus ? :smiley-wink: ;D
Pour l'humour ? Non ça ne compte pas :)
Boun's, J-M-L bonjour.
Boun's tu n'as peu l'image du ' Bon Point'
Et je vous suis toujours en apprenant tranquillement.
Pascal.
Pour l'humour ?
Celui ci te reviens :)
a tu un ecran sur arduino a me conseiller ? un capacitif avec une connection a peu de fils (l'idéal est de pourvoir le deporter via un RJ45) ?
Le seul que j'ai c'est le 2.8 TFT Touch Shield for Arduino w/Capacitive Touch (https://www.adafruit.com/product/1947) de Adafruit mais il est assez petit avec 240x320 pixels (devrait suffire cependant pour vos besoins si vous bossez un l'interface graphique (je ne pense pas que vous ayez vraiment besoin de 7 pouces!)
Le seul que j'ai c'est le 2.8 TFT Touch Shield for Arduino w/Capacitive Touch (https://www.adafruit.com/product/1947) de Adafruit mais il est assez petit avec 240x320 pixels (devrait suffire cependant pour vos besoins si vous bossez un l'interface graphique (je ne pense pas que vous ayez vraiment besoin de 7 pouces!)
Les futures utilisateur souhaite un écran plus grand, pas forcement 7" mais l'avantage est que les 5" et 7" avait la même puce et même résolution donc chacun pouvais mettre l'écran de la taille souhaité.
5" me semblais être un bon compromis taille/prix
mes recherche mon fait tomber plusieurs fois sur des écran "nextion" que j'ai zaper a chaque fois.
Mais faute de trouver réellement se que je cherche, connection avec peu de fils et sans shield, tactil capacitif ... j'ai fini par y regarder de plus pret.
Il communique avec l'arduino via le tx / rx (donc, que 4 fils :) )
si j'ai bien compris, toute la video est geré par l'ecran lui meme en telechargent directement dans l'ecran les page que l'on cree avec leur logitiel (https://nextion.itead.cc/editor_guide/), pratique puisque plus besoin de téléverser a chaque modif pour voir le rendu ...
Donc l'arduino est un peu libéré ( et également de la bibliothèque assez lourde associé)
J'ai trouvé 2 video ou l'on voit la réactivité de l'ecran.
video 1 (https://www.youtube.com/watch?v=eDn7LFyoEm8)
video 2 (https://www.youtube.com/watch?v=hKt1MmYD0-Q)
Autre point simpa :
en résolution 800*480 il est dispo en 5" et 7" (https://nextion.itead.cc/shop-nextion/)
et pourquoi pas faire aussi des page en 480 * 320 pour ceux qui voudrait un écran plus petit (3.5") ?
dispo en version avec boitier (https://www.itead.cc/nextion-nx8048k070-011r-011c.html) ce qui est vraiment un plus pour l'intégrer a nos aquariums
Bref ... trop beau sur le papier pour être vrai ...
Qu'en penser vous ?
et j'ai meme trouvé un exemple pour un aquarium (https://www.itead.cc/blog/nextion-tutorial-based-on-nextion-arduino-library) :)
en tout cas je trouve l'interface graphique vraiment sympa (incomparable avec se que j'avais pu faire cette été sur mon 480*320)
Oui nextion c'est cool et performant - mais c'est assez cher aussi et ils n'ont qu'un soft pour PC.. moi je suis Mac ou Linux alors je ne les aime pas :)
Ils ont une librairie arduino qui facilite la communication
Oui nextion c'est cool et performant - mais c'est assez cher aussi et ils n'ont qu'un soft pour PC.. moi je suis Mac ou Linux alors je ne les aime pas :)
C'est vrai que c'est un peu plus chère, en comparaison, les 7" sont au alentour de 50€ et la, le moins chère que j'ai trouvé c'est 80€ ...
Pour les 5" 45€ contre 70€ ...
Pour les 3.5# ~15€ contre 20€ donc sur petite taille peu de différence
oui, embêtant pour leur application ...
D'un autre coté, une fois l'interface graphique réalisé et mis sur SD cela n'as plus d'importance.
d'un point de vu global,
branchement (moins de 8 fils pour fonctionner)
performance
Facilité de programmation/échange avec l'arduino
qualité/prix
Je doit oublier cette piste (pour la quelle ?)?
ou cela est un bon compromis
C'est un bon compromis - avec des jolis graphiques et c'est un peu comme AJAX et la page web, vous ne balancez que les données il se charge de peindre l'ecran Comme il faut donc c'est rapide
Juste avis perso - je boycotte les sociétés qui n'ont pas compris que de plus en plus de monde utilise un Mac ou Linux (ou le cloud); c'est pour moi un signe de manque de vision et d'adaptation au changement. Donc des has-been... ce qui m'intéresse c'est le progrès, l'innovation.. ils ne vivent pas avec leur temps et donc leurs clients un jour vont se retrouver dépassés aussi
C'est un bon compromis - avec des jolis graphiques et c'est un peu comme AJAX et la page web, vous ne balancez que les données il se charge de peindre l'ecran Comme il faut donc c'est rapide
Juste avis perso - je boycotte les sociétés qui n'ont pas compris que de plus en plus de monde utilise un Mac ou Linux (ou le cloud); c'est pour moi un signe de manque de vision et d'adaptation au changement. Donc des has-been... ce qui m'intéresse c'est le progrès, l'innovation.. ils ne vivent pas avec leur temps
oula ... faut que je passe sous mac ... j'ai pas envie que mon fils pence que je suis un vieux des années 80 ... ;D ;D ;D
Du coup, ca veut dire que tu ne pouras pas m'aider sur cette partie la ... :smiley-roll-blue: :smiley-roll-sweat:
Non pas vraiment pour aider - cels dit comme je suis curieux j'ai regardé comment ça marche et lu quelques tutos et lu le code des librairies en diagonale - c'est assez simple à comprendre
Sinon pour le « faut que je passe sous mac » --> pas forcément (même si ça vous changerait la vie en mieux à mon avis même s'il vous faudrait une période d'adaptation :) )
Mais mon commentaire C'est plus pour les fabricants de solutions - je suis pour que les individus eux restent libres de leur choix bien sûr
Non pas vraiment pour aider - cels dit comme je suis curieux j'ai regardé comment ça marche et lu quelques tutos et lu le code des librairies en diagonale - c'est assez simple à comprendre
Je me lens dans le vide alors ...
Je part sur le 3.5" dans un premier temps histoire de dompter la bete.
Si tout est Ok je prendrais un 7" comme ça je pourrait proposer 2 définition et 3 taille d'écran
ça vous changerait la vie en mieux
Je suis sur de ca mais se qui me bloque c'est la partie hardware qui ne peut pas être monter maison, pas evolutif, cout plus élevé ...
Je me trompe peut être ...
Bonsoir,
j'avance peu en se moment, faute de temps.
Je continu a mettre en forme.
J'en arrive a un code assez long, difficile de si retrouver ... (dans toute les fonctions, mais le loop est beaucoup plus claire)
J'ai pour le moment inclue le wifi pour les test.
Le gsm est opérationnel.
il y a peut être autre chose a faire pour rendre tout cela plus claire et lisible ?
dans l'absolu il faudrait un fichier .cpp et un fichier .h par grands modules (les fonctions dans la loop()).
Dans le .h vous déclarez la fonction principale qui est appelée dans la loop(), tout le reste qui lui est spécifique est dans le .cpp qui inclus le .h
le .ino inclurait aussi tous ces .h
j'ai suivi ce tuto (http://eskimon.fr/1803-arduino-mini-tuto-organisez-votre-code-en-fichiers) que plusieurs personne recommande sur les forums
J'ai donc eclairage.h :
ECLAIRAGE();
et eclairage.cpp:
void ECLAIRAGE(){
// **************************************** bleu 1
if (eclairageBleu1 == eclairageArret) {
pwm_bleu1 = 0;
analogWrite(pinOUT_bleu1, 0);
}
else if (eclairageBleu1 == eclairageActif) {
pwm_bleu1 = puissance_bleu;
analogWrite(pinOUT_bleu1, puissance_bleu);
}
else if (eclairageBleu1 == eclairagePWM) {
if ((Time >= debut_LEVER_bleu1) && (Time < fin_LEVER_bleu1)) {
pwm_bleu1 = map(Time, debut_LEVER_bleu1, fin_LEVER_bleu1, 0, puissance_bleu);
analogWrite (pinOUT_bleu1, pwm_bleu1);
}
else if ((Time >= debut_COUCHER_bleu1) && (Time < fin_COUCHER_bleu1)) {
pwm_bleu1 = map(Time, debut_COUCHER_bleu1, fin_COUCHER_bleu1, puissance_bleu, 0);
analogWrite (pinOUT_bleu1, pwm_bleu1);
}
else if ((Time >= fin_LEVER_bleu1 ) && (Time < debut_COUCHER_bleu1)) {
pwm_bleu1 = puissance_bleu;
analogWrite(pinOUT_bleu1, puissance_bleu);
}
else {
pwm_bleu1 = 0;
analogWrite(pinOUT_bleu1, 0);
}
}
// **************************************** bleu 2
if (eclairageBleu2 == eclairageArret) {
pwm_bleu2 = 0;
analogWrite(pinOUT_bleu2, 0);
}
else if (eclairageBleu2 == eclairageActif) {
pwm_bleu2 = puissance_bleu;
analogWrite(pinOUT_bleu2, puissance_bleu);
}
else if (eclairageBleu2 == eclairagePWM) {
if ((Time >= debut_LEVER_bleu2) && (Time < fin_LEVER_bleu2)) {
pwm_bleu2 = map(Time, debut_LEVER_bleu2, fin_LEVER_bleu2, 0, puissance_bleu);
analogWrite (pinOUT_bleu2, pwm_bleu2);
}
else if ((Time >= debut_COUCHER_bleu2) && (Time < fin_COUCHER_bleu2)) {
pwm_bleu2 = map(Time, debut_COUCHER_bleu2, fin_COUCHER_bleu2, puissance_bleu, 0);
analogWrite (pinOUT_bleu2, pwm_bleu2);
}
else if ((Time >= fin_LEVER_bleu2 ) && (Time < debut_COUCHER_bleu2)) {
pwm_bleu2 = puissance_bleu;
analogWrite(pinOUT_bleu2, puissance_bleu);
}
else {
pwm_bleu2 = 0;
analogWrite(pinOUT_bleu2, 0);
}
}
/////////////////////////////////////// *** blanc *** ///////////////////////////////////////
// **************************************** blanc 1
if (eclairageBlanc1 == eclairageArret) {
pwm_blanc1 = 0;
analogWrite(pinOUT_blanc1, 0);
}
else if (eclairageBlanc1 == eclairageActif) {
pwm_blanc1 = puissance_blanc;
analogWrite(pinOUT_blanc1, puissance_blanc);
}
else if (eclairageBlanc1 == eclairagePWM) {
if ((Time >= debut_LEVER_blanc1) && (Time < fin_LEVER_blanc1)) {
pwm_blanc1 = map(Time, debut_LEVER_blanc1, fin_LEVER_blanc1, 0, puissance_blanc);
analogWrite (pinOUT_blanc1, pwm_blanc1);
}
else if ((Time >= debut_COUCHER_blanc1) && (Time < fin_COUCHER_blanc1)) {
pwm_blanc1 = map(Time, debut_COUCHER_blanc1, fin_COUCHER_blanc1, puissance_blanc, 0);
analogWrite (pinOUT_blanc1, pwm_blanc1);
}
else if ((Time >= fin_LEVER_blanc1 ) && (Time < debut_COUCHER_blanc1)) {
pwm_blanc1 = puissance_blanc;
analogWrite(pinOUT_blanc1, puissance_blanc);
}
else {
pwm_blanc1 = 0;
analogWrite(pinOUT_blanc1, 0);
}
}
// **************************************** blanc 2
if (eclairageBlanc2 == eclairageArret) {
pwm_blanc2 = 0;
analogWrite(pinOUT_blanc2, 0);
}
else if (eclairageBlanc2 == eclairageActif) {
pwm_blanc2 = puissance_blanc;
analogWrite(pinOUT_blanc2, puissance_blanc);
}
else if (eclairageBlanc2 == eclairagePWM) {
if ((Time >= debut_LEVER_blanc2) && (Time < fin_LEVER_blanc2)) {
pwm_blanc2 = map(Time, debut_LEVER_blanc2, fin_LEVER_blanc2, 0, puissance_blanc);
analogWrite (pinOUT_blanc2, pwm_blanc2);
}
else if ((Time >= debut_COUCHER_blanc2) && (Time < fin_COUCHER_blanc2)) {
pwm_blanc2 = map(Time, debut_COUCHER_blanc2, fin_COUCHER_blanc2, puissance_blanc, 0);
analogWrite (pinOUT_blanc2, pwm_blanc2);
}
else if ((Time >= fin_LEVER_blanc2 ) && (Time < debut_COUCHER_blanc2)) {
pwm_blanc2 = puissance_blanc;
analogWrite(pinOUT_blanc2, puissance_blanc);
}
else {
pwm_blanc2 = 0;
analogWrite(pinOUT_blanc2, 0);
}
}
}
et ajouter #include "eclairage.h" dans "l'en tete" de mon fichier .ino
et a la compile j'ai
exit status 1
expected constructor, destructor, or type conversion before ';' token
sur mon fichier eclairage.h
Pourtant j'ai bien suivi le tuto, et je l'ai refait 3 fois ...
Dans le .h mettez
#ifndef _eclairage_h_
#define _eclairage_h_
extern void ECLAIRAGE();
#endif
ça dit qu'il va exister quelque part une fonction du nom ECLAIRAGE qui ne prend aucun param et qui ne retourne rien. le .cpp va lui importer le .h et définir cette fonction. Votre .ino lui va aussi prendre le .h
Pour éviter de déclarer 2 fois des mêmes choses on utilise la ruse du #ifndef
les declaration t'elle que
int puissance_bleu = 255 ;
qui sont utilisé pour cette fonction mais egalement dans d'autre doivent etre declaré dans le .cpp ou dans le ino ?
Quand je les met dans le ino j'ai erreur dans .cpp et quand elle sont dans le .cpp elle sont non déclarer dans le ino ...
Y a encore une ruse...
Ce sont des éléments globaux - donc un .h genre varglobales.h qui les déclare comme étant extern que vous importez partout où au moins une des variable, constante, type, enum, ... doit être connu
et vous les définissez (allouez la mémoire et la valeur de départ) dans un des fichiers .cpp, le plus pertinent
Mettre aussi la « ruse » du ifndef dans ce. h aussi
#ifndef _varglobales_h_
#define _varglobales_h_
extern int puissance_bleu;
// ....
#endif
Ce sont des éléments globaux - donc un .h genre varglobales.h qui les déclare comme étant extern que vous importez partout où au moins une des variable, constante, type, enum, ... doit être connu
et vous les définissez (allouez la mémoire et la valeur de départ) dans un des fichiers .cpp, le plus pertinent
si je comprend bien, actuelement j'ai
unsigned long debut_LEVER_bleu1 = LEVER;
Je fait un header (j'ai revisé lol) avec
unsigned long debut_LEVER_bleu1;
et dans le Cplusplus le plus concerné je fait
debut_LEVER_bleu1 = LEVER;
Pour le #ifndef, extern, #endif je regarde pour trouver un post qui m'explique le pourquoi et ce que c'est.
Oui pour le mot header, non pour la forme car il faut utiliser extern
Une variable est définie par plusieurs choses: son petit nom pour l'utiliser dans le code, son type (le nombre d'octets alloués), son adresse mémoire (l'adresse du premier octet) et le contenu de ces adresses mémoire.
En gros On parlera de déclaration quand il s'agit du nom et du type et de définition quand il s'agit du tout - du type, du nom, de l'adresse mémoire (et du contenu éventuellement).
En faisant int toto=8;
on déclare et définit en même temps la variable,c'est à dire qu'on fait tout d'un coup: on dit que le type est int, que le petit nom c'est toto, et en l'écrivant comme cela quand le compilateur voit ça il réserve de la mémoire (ici 2 octets) à un endroit particulier. Et accessoirement il met dans les bits de 2 octets la représentation en binaire de la valeur 8.
Quand on fabrique un projet qui comporte plusieurs fichiers il y a plusieurs étapes. La première consiste à compiler chaque fichier indépendamment puis ensuite à tout mettre ensemble et résoudre à ce moment là les dernières choses inconnues - on appelle cela la phase de « link » en anglais.
Pour compiler un bout de code où on parlerait de toto, il suffit de savoir que toto est un entier sur 2 octets. C'est Seulement à la phase de link qu'il faudra enfin savoir où il est exactement en mémoire.
Le mot clé extern permet de séparer les deux opérations.extern int toto;
dit au compilateur "il va y avoir quelque part une variable de type int qui s'appelera toto. Ne te soucie pas pour le moment d'où elle est en mémoire, je te le dirais plus tard, à l'extérieur de ce fichier (d'où le extern) ».bien sûr il faudra s'assurer que quelque part ailleurs vous ayez bien la définition
Donc quand vous voulez utiliser une variable dans différents fichiers, il y en a un qui contient la vraie définition (et donc effectue l'allocation de la mémoire) et tous les autres ne doivent avoir que la déclaration (juste qu'est-ce que c'est que ce nom représente).
Si vous n' avez qu'une variable vous pouvez mettre extern int toto;
dans tous les fichiers. C'est simple. Mais si vous en avez beaucoup ça fait lourd de mettre toutes ces déclarations dans chaque fichier et Donc souvent on fait un .h qui ne fait que déclarer toutes les variables partagées avec extern et ensuite tous les fichiers de code (.cpp ou .ino) qui ont besoin de les connaître, importent ce .h ce qui permet au compilateur de connaître le type des variables et leurs noms. Un des fichiers - peu importe lequel mais souvent celui qui est pertinent pour cette variable ou le corps principal - va avoir la vraie définition et lors de la phase de link tout au bout du processus de fabrication du programme exécutable, le linker retrouve ses petits et remplace les adresses inconnues par la vraie adresse mémoire à lire ou écrire
OK?
J'ai bien compris le principe, l'explication est très clair :smiley-wink:
effectivement ça compile :)
Mais du coup, ça fait un paquet de ligne en plus ...
bonsoir,
Discret en se moment ... beaucoup de taf avec les fete qui approche donc bon ... je boss beaucoup moins sur le projet que j'en est envie mais bon ...
J'ai reçu mon écran nextion 3.5.
Ca a l'air vraiment super, tu branche, et quasi instantanément tu a un menu demo.
tres belle affichage.
J'ai donc commencer a utilise le logitiel NEXTION EDITOR pour faire des tests.
Et comme d'habitude ... sa a merdé ...
J'ai donc chercher a comprendre ...
Et j'ai fini par trouver ...
Ces écran son commercialiser sous 2 référence différente pour chaque ecran.
exemple pour le 3.5
NX4832T035_011R et TJC4832T035_011R
La version NX (nextion) est faite pour le marché international tandis que la version TJC est réservé au marché chinois. (et je pense que l'on peut dire que ce sont des copie tout simplement!)
Certain vendeur n'hesite pas a vendre ces ecran TJC en indiquant NEXTION ...
Vous vous douter bien quelle version des deux j'ai reçu ... :smiley-confuse: :smiley-confuse:
Si cette "difference" ne posait pas probleme ...
MAIS
Le logiciel NEXTION EDITOR a décider de ne prendre en charge que les ecran NX ...
Pour les ecran TJC il faut utiliser USART HMI ... qui est en chinois !!!
Bien sur, impossible d'importer un fichier fait dans nextion editor dans usart hmi ...
Décidément ... compliqué avec les écrans ...
Les joies des chinoiseries...courage !
0110001001101111011011100110101001101111011101010111001000101100000010100110001001101111011011100110111001100101001000000110011001100101011101000110010100100000011001000110010100100000011011100110111101100101011011000010000001100001001000000111010001101111011101010111001100101110000010100110000100101011
si besoin ... (http://www.supportduweb.com/convertisseur-textes-binaire-code-ascii-numerique-ordinateur-secret-encoder-text-binary-chiffrer.html)
:smiley-wink:
bonsoir,
Apres l'électronique et le codage ... il faut se mettre au graphisme ...Tout aussi compliqué sans maîtrise des logiciel mais sa commence a être sympa
(https://img15.hostingpics.net/pics/665438Sanstitre.png)
Y manque que les variable :)
Un fois l'affichage terminer je passerait au codage écran puis arduino.
Wow c'est Disney land :)
Bravo et bonnes fêtes
(http://cleopatre22.e-monsite.com/medias/ecard/bonne-fete-6-1.gif)
En retard de 4 jours ...
Mais il est quand même passé ...
Le papa noel lol
Et visiblement, il y avait beaucoup de lutin en CP ou maladie ...
:)
(http://i64.tinypic.com/68dk4n.jpg)
Ça va être bien pratique pour l'aquaboun's ... boitier, support sonde ect ...
Et 6h15 plus tard
(http://i65.tinypic.com/29eo04.jpg)
Y a plus qu'a tout réglé et lancer la première impression :) :) :)
Il y en a qui sont gâtés :)
Boun's bonjour.
Super page d'accueil et joli graphisme, donc en un mot ' beau boulot'.
Je rejoins J-M-L ' y en as qui sont vernis'.
Allez zou bonne fête et à l'année prochaine.
Pascal.
djbouns dis moi on tu as acheter ton imprimante 3D? quel prix?
merci
Je l'ai commander sur gearbest.
pour 170€
Par contre il faut prendre en compte 3 points importants :
1 mois 1/2 de délais de livraison
Risque de dédouanement ( donc 20% + 25€ de frais)
Arrive démonté ( j'ai passé 6h15 non stop ! et c'est juste pour le montage ... je suis depuis plusieurs heure sur le réglage et test ...) je ne suis pas un pro mais je ne crois pas être novice non plus donc ATTENTION ne vous lancer pas a l'aveugle.
PS: et ne pas oublier qu'il a fallu être sage toute l'année lol
La page de paramétrage
(https://img15.hostingpics.net/pics/911337Sanstitre.png)
Je vois que vous maîtriser la chose !
C'est peut être juste mon goût perso mais je trouve que c'est chargé et difficile à lire. Le dessin du fond devrait être plus en filigrane pour améliorer les contrastes, les champs texte un fond blanc et texte noir c'est plus lisible et visible etc...
En ergonomie des interfaces utilisateurs, moins c'est plus ! Faites simple, une seule police de caractères, les couleurs doivent être porteuse de sens, ...
Je vois que vous maîtriser la chose !
lol oula ... se n'est que le visuel ... je vais m'amuser avec le codage je pense ...
Pour le fond blanc sous le texte sa fait dégueu (je trouve)
j'ai donc mis le texte en blanc et suis revenu sur le fond original (donc assez sombre)
J'ai changer la couleur des boutons en faisant un degradé d'un coté pour le - et de l'autre pour le + (pas fait encore l'écriture dans les boutons)
(https://img15.hostingpics.net/pics/592540Sanstitre.png)
Pour info, si vous ouvrez l'image dans une nouvelle page ou a partir du lien (https://img15.hostingpics.net/pics/592540Sanstitre.png), l'image sera taille "pixel" réel (800*480), donc plus pratique de se rendre compte
Qu'en pense tu ?
et vous , hazerty565, Breisleach (en temps que futur utilisateur ? lol)
Les labels en blanc c'est déjà plus lisible - ensuite je ne comprends pas bien ce qui va aller dans toutes ces cases où vous avez des 5 et comment l'utilisateur va interagir
ensuite je ne comprends pas bien ce qui va aller dans toutes ces cases où vous avez des 5 et comment l'utilisateur va interagir
Les "5" sont provisoire, je n'ai pas encore rentrée les valeur de chaque bouton.
Ces boutons, par un appuie tactile modifierons la variable qui seras entre les 2 boutons, même implantation que sur la page web (et modifieras en même temps la variable dans l'arduino)
Vous ne disposez pas de caractères accentués pour votre afficheur ?
Dernière info en bas à droite : Mouvement de 1° des oscillateurs toutes les :
Bonnes fêtes de fin d'année à vous ... et à toutes et à tous.
Pierre
ah OK
Pourquoi ne pas dessiner des beaux boutons avec d'un côté un "-" et de l'autre un "+" et la valeur entre les 2?
chaque bouton ne modifie pas la variable du même nombre
certain 0.1 d'autre 1 ou encore 5 ...
J'ai mis les valeur dans les boutons :
(https://img15.hostingpics.net/pics/933665Sanstitre.png)
Petite question sur les ssid et mdp wifi (je suis sur le clavier tactile)
Est il impératif d'avoir minuscule/majuscule ou les ssid et mdp sont bien reconnu quand même ?
Y a t'il des caractere spéciaux dedans ? sinon, cela m'évite un paquet de touche a faire.
Tous les caractères sont reconnus...
Vous ne disposez pas de caractères accentués pour votre afficheur ?
Dernière info en bas à droite : Mouvement de 1° des oscillateurs toutes les :
Bonnes fêtes de fin d'année à vous ... et à toutes et à tous.
Pierre
Les % fonctionnes
Les ° non ...
L'orthographe c pas mon truc :smiley-confuse:
Tous les caractères sont reconnus...
a ...
arff ...
:smiley-sweat:
... L'orthographe c pas mon truc :smiley-confuse:
Moi non plus, mais j'essaie d'y faire attention.
Au-delà de ça, vous réalisez un matériel dont plusieurs personnes pourraient s'inspirer ou le reproduire. C'est quand même plus sympa si l'IHM est correcte et n'a pas l'air de venir d'une traduction douteuse d'un truc venant d'on ne sait où.
Cordialement.
Pierre
#djbouns - de mémoire je crois que j'avias Lu que Nextion supportait de nombreux ISO8859 et pour le français c'est le 1 (il manque de mémoire le E dans l'O = œ) ou le 15 (ajoute le symbole € et le œ) qui va bien.
regardez la doc comment sélectionner le Charest et ensuite il suffit d'envoyer ces caractères, ils tiennent sur 1 octet, la partie ASCII est la même et il y a d'autres caractères dans la partie haute y compris toutes les lettres accentuées
#djbouns
Ludovic ;)
je suppose que tu parle de ça :
(https://img15.hostingpics.net/pics/980681Sanstitre.png)
Je suis rester sur celui par défaut, ascii, car je ne comprend pas se qu'il me veut lol
Donc il suffit que je sélectionne la deuxième ligne pour avoir les caractere spéciaux.
Je testerais sa en 2018 car je doit y allée ... :smiley-razz:
Alors avec un peu d'avance ...
(https://t3.ftcdn.net/jpg/01/81/59/40/240_F_181594048_s1q9Tw1OFLiTTdsY4rZfhWLmqpd8lkLU.jpg)
Passer un bon réveillons, et profité bien, mais pas trop lol :smiley-mr-green:
Et un grand Merci pour toutes l'aide que vous m'apportez.
Et ...
a l'année prochaine lol (https://www.youtube.com/watch?v=EsCZ4ciTZEk)
;)
Bonne fête de fin d'année, joyeux réveillon, ne mangez pas les poissons de l'aquarium :)
ne mangez pas les poissons de l'aquarium :)
:smiley-confuse: :smiley-confuse: :smiley-confuse: trop tard :smiley-confuse: :smiley-confuse: :smiley-confuse:
(https://img15.hostingpics.net/pics/694232Sanstitre.png)
:smiley-lol: :smiley-lol: :smiley-lol:
J'ai pris le 8859-1 et les ° se "transforme" en ?
Et les ? reste bien en ?
mais je vient de voir que ça le fait directement dans le logiciel, avant même d'être affecté a un type de police :/
un petit aperçu (https://www.youtube.com/watch?v=tDnYKmtVGOA&feature=youtu.be) des pages réalisé et de la navigation
Le clavier est presque fini
normalement le lien fonctionne maintenant
qu'en pensez vous ?
Boun's bonjour et tous mes vœux pour toi et ta familles.
Joli et simple de compréhension je trouve... Beau travail et hâte de continuer à apprendre en vous lisant.
¨
Pascal.
Salut
Bravo pour le boulot - surtout le clavier qui a du demander pas mal de temps. Une petite modification peut-être pour la partie Wifi: généralement en interface utilisateur on choisit d'abord le champ à modifier puis on tape le texte à côté plutôt que le contraire (ou taper dans le vide et affecter le résultat)
Bravo pour le boulot
Arfff ... en 2018 juste le droit a un bravo ... meme plus de bon point :smiley-confuse:
;D ;D ;D
le clavier qui a du demander pas mal de temps
au que oui ...
généralement on choisit d'abord le champ à modifier puis on tape le texte à côté
par ici (https://youtu.be/whxgeA2ulRg)
Je n'ai fait que la première ligne du clavier, je ferait le reste cette nuit.
C'est bon comme ca ou autre chose a modifier avant de programmer toutes les touches ?
J'ai également ajouter "un petit détail" lorsque l'on clique sur enregistrer car le retour a la page précédente me paraissait trop violent et pouvait laisser penser qu'il y avait un bug et/ou que la sauvegarde n'avait pas ete faite. j'ai donc ajouter un petit barre de progression ne laissant plus planer de doute.
Boun's bonjour et tous mes vœux pour toi et ta familles.
Meilleur voeux a vous aussi
La santé et plein de bonne chose
Arfff ... en 2018 juste le droit a un bravo ... meme plus de bon point :smiley-confuse:
;D ;D ;D
au que oui ...
par ici (https://youtu.be/whxgeA2ulRg)
Je n'ai fait que la première ligne du clavier, je ferait le reste cette nuit.
C'est bon comme ca ou autre chose a modifier avant de programmer toutes les touches ?
J'ai également ajouter "un petit détail" lorsque l'on clique sur enregistrer car le retour a la page précédente me paraissait trop violent et pouvait laisser penser qu'il y avait un bug et/ou que la sauvegarde n'avait pas ete faite. j'ai donc ajouter un petit barre de progression ne laissant plus planer de doute.
c'est cool :)
idéalement si vous pouviez avoir une sorte de champs de texte visualisé par un rectangle blanc à côté du SSID ou PWD ce serait l'idéal car on est habitué à reconnaitre ce genre de champs et donc à cliquer dessus pour le remplir
La question est dans la video (https://youtu.be/QBEYbumuhbs)
?
Oui c'est bon :)
Bonsoir,
Petit vidéo video (https://youtu.be/K03WqXwqaAs) des mouvement thermomètre, j'ai également ajouter les champs variable.
C'était assez compliquer puisque le NEXTION ne prend que les chiffres entier, Donc les faire bouger a partir de température en décimal ... (les variable devront être x10 en sortant de l'arduino, le point des centième est incruster sur l'image de fond, meme chose pour les PH) et que les barres d'avancement ne peuvent pas être paramétré avec une valeurs basse et haute, elle son forcement de 0 a 100.
Ce n'est pas parfait mais je trouve les mouvement correcte.
Le thermo de rampe va de 20 a 65
Celui de l'aqua de 22 a 28.
J'ai avancé sur les variable du paramétrage avec un peu de code
///HS-in///
Le codage sur le nextion est assez simple et intuitif puisque il nous propose les possibilité au fur et a mesure de la ligne de code un peu comme la saisie intuitive de google.
Je suis vraiment agréablement surpris par le logiciel nextion, j'espère que le codage pour les échanges arduino/nextion seras aussi "facile"
///HS-out///
Des limite ont été mise pour tout les paramètres dont certains en relation entre eux.
Exemple, la limite PH bas ne peut pas être supérieur a la limite PH haut, ect ...
J'ai passé un peu de temps sur l'affichage OFF des oscillateur et tempête car, tout comme les affichage testé sur arduino, la dernière image par dessus reste affiché si elle n'est pas recouvert. j'ai donc triché en élargissant les champs texte et sur les moment de rafraîchissement de l'écran.
De cette façon, visuellement on ne se rend compte de rien.
https://youtu.be/2MjN8nAFbJs (https://youtu.be/2MjN8nAFbJs)
Et me revoila :) :) :)
Je suis depuis 2 jours sur la partie échange d'information entre l'écran et l'arduino.
J'ai naviguer sur le net, énormément de post en anglais, mais j'ai fini par trouver par ou commencer.
(je suis même tomber sur un le forum un post de juillet ou participe j.m)
Et donc, qui dit reprendre le codage dit rencontrer de nouveau problème :)
J'ai fait un petit code "test" pour comprendre les inter action entre les deux composant
Dans un premier temps, j'envoie des info de l'arduino a l'ecran.
Un champs texte initialement remplie par "abcd" est rempli par "azerty"
Un champs numérique initialement remplie par "1234" est rempli par now.second d'un RTC, ce champs ce mais bien a jour, sans saccade, c'est TOP :)
#include <Wire.h>
#include "RTClib.h"
#include "NexText.h"
#include "NexVariable.h"
RTC_DS3231 rtc;
NexText t0 = NexText(0, 1, "t0");
NexVariable n0 = NexVariable(0, 2, "n0");
void setup () {
Serial1.begin(9600);
Serial.begin(9600);
delay(3000); // wait for console opening
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
}
void loop () {
DateTime now = rtc.now();
t0.setText("yes");
n0.setValue(now.second());
}
Par contre, dans le moniteur serie j'ai
recvRetCommandFinished err
recvRetCommandFinished err
Qui apparaît, un après l'envoie du champs texte et l'autre du numérique
J'ai chercher et ce problème arrive a beaucoup de monde mais sur tout les post, aucune solution( et en plus toujours en anglais donc je comprend pas tout des fois)
Il serait du au fait que l'écran ne renvoie pas un retour "OK" en quelque sort.
Mais pourquoi ...
ps : l'arduino arrive bien a lire les variable pressentes sur l'écran
Personne ?
En plus je vient de voir que le forum nextion est fermé ... : /
Je trouve des code d'exemple mais pas de tuto adapté a se que j'ai besoin.
Désolé mais peux pas vous aider sur ce coup là - je n'ai pas ce genre d'écran
:smiley-wink: je le savais, on en avait discuté :)
la poisse il on fermé le forum le 15 novembre :smiley-confuse:
J'ai 3h devant moi ... il faut que je comprenne les exemple fourni avec la biblioteque
J'ai compris le principe de l'exemple "bouton"
Sur l'ecran, un bouton avec du texte dessus "1"
Dans l'arduino :
void b0PopCallback(void *ptr)
{
uint16_t len;
uint16_t number;
NexButton *btn = (NexButton *)ptr;
dbSerialPrintln("b0PopCallback");
dbSerialPrint("ptr=");
dbSerialPrintln((uint32_t)ptr);
memset(buffer, 0, sizeof(buffer));
/* Get the text value of button component [the value is string type]. */
btn->getText(buffer, sizeof(buffer));
number = atoi(buffer);
number += 1;
memset(buffer, 0, sizeof(buffer));
itoa(number, buffer, 10);
/* Set the text value of button component [the value is string type]. */
btn->setText(buffer);
}
Chaque click augment de +1 la valeur le texte present sur le bouton.
Si je décode se qui se passe :
Le click sur l'ecran envoie un message "65 00 01 00 FF FF FF " a l'arduino (le "65 00 01 00 FF FF FF " étant surement décodé par la bibliothèque et associé au bon bouton, ce qui déclenche b0PopCallback)
L'arduino lit la valeur "texte" du bouton en la stock dans un String
"atoi" converti se String en numerique
On ajout +1 a se numerique
"itoa" converti le numerique en String
L'arduino renvoie cette valeur au bouton qui l'affiche.
Je ne croie pas mettre trompé donc cela m'amène a une question ...
En procédant de cette manière, l'arduino est sollicité (j'irais dire, pour rien puisque l'écran lui même est capable de faire cette opération de +1 ou -1)
J'étais parti du principe que :
Quand la page s'ouvre, les valeur son demander a l'arduino.
On modifie tout se que l'on veut
On enregistre ( toute les valeur son envoyer a l'arduino OU, il me parait plus simple que l'arduino les lises)
Je continu sur ma lancer ou adopte leur exemple ?
Oui le principe du callback de l'écran est bien celui la et si vous pouvez bien interroger chaque élément lors du click sur la sauvegarde des préférences alors les 2 approches sont possibles
mais la quelle te parait la mieux ?
vos mieux laisser l'arduino faire autre chose ?
Les interfaces modernes sont souvent en modification directe (ce que vous voyez affiché est la réalité en vigueur tout le temps) sauf dans certains cas où plusieurs paramètre doivent être modifiés d'un coup pour faire quelque chose de cohérent
Je pense que dans votre cas vous êtes plutôt dans la seconde catégorie et qu'il ne sert à rien d'avoir l'arduino Qui doit traiter toutes les valeurs intermédiaires si vous voulez passer un paramètre de 10 à 20, surtout si pour que ce soit cohérent il faut en baisser un autre de 20 à 10 par exemple.
Donc si vous voulez frimer un peu :) - vous allez dire que vous prenez la design pattern MVC (https://fr.m.wikipedia.org/wiki/Modèle-vue-contrôleur) - vous avez une vue et un contrôleur embarqué dans l'ecran (ça reste hors du champs du processeur central) et un seul bouton (sauvegarde des préférences) sert à mettre à jour le modèle.
je frimerais peut être quand tout sera opérationnel lol
Je vais donc continuer sur le principe que j'avais.
Avant de continuer a bosser sur le nextion il faut que je mette de l'ordre dans mon code global car c'est un bazars monstre !!!
Je l'avais mis de coté au moment ou je créait de .cpp et .h
j'ai deux chose qui m'interpelle.
Dans un premier temps, j'ai des erreurs a la compile dans le premiere .cpp qu'il croise et il me dit par exemple boolean not declared.
Apres avoir chercher sur le net il faudrait ajouter #include "arduino.h" dans le fichier cpp si j'ai bien compris
https://www.arduino.cc/en/Hacking/LibraryTutorial (~vers 30% du document)
Une fois fait sa compile
Mais au fichier cpp suivant rebelote.
Se qui veut dire qu'il faut ajouter #include "arduino.h" a chaque fichier cpp ?
Ensuite malgré que mes pin soit déclaré dans le ino, il me dit :
'pinOUT_relais_distributeur_nouriture' was not declared in this scope
ya un truc a faire ou il faut également que je fasse
extern const byte pinOUT_relais_distributeur_nouriture;
Se qui veut dire qu'il faut ajouter #include "arduino.h" a chaque fichier cpp ?
oui. arduino.h rajoute tous les mots clés liés au monde arduino
ya un truc a faire ou il faut également que je fasse
extern const byte pinOUT_relais_distributeur_nouriture;
oui idéalement les trucs globaux devraient être dans un .h que vous importez partout, déclarés en
extern et définis dans le .ino par exemple (où là où ça fait du sens)
oui idéalement les trucs globaux devraient être dans un .h
c'est le cas pour tout les pins il sont dans un #include "pin.h" qui est appelé dans le ino juste apres les biblioteque.
que vous importez partout, déclarés en extern et définis dans le .ino par exemple (où là où ça fait du sens)
la je ne comprend pas.
"que vous importez partout" = #include "pin.h"
"déclarés en
extern" ?
Une fois le #include "pin.h" importez dans le fichier.h ca compile car j'ai deja fait
extern unsigned long Time, H;
extern unsigned long temps_precedent_nourissage;
extern boolean tempete_ON;
extern int i;
extern float temp_bac_graph[24], temp_rampe_graph[24], ph_BAC_graph[24], ph_RAC_graph[24],temp_bac,temp_rampe,ph_BAC,ph_RAC;
C'est de ca que tu parlais en "extern" ? si oui, c'est OK
Oui si vous avez besoin de connaître le nom d'une variabke globale dans le fichier toto.cpp alors qu'elle est définie (définir = allouer la mémoire associée) dans le .ino alors l'idéal est d'avoir cette variable en extern dans un global.h et importer ce .h dans le .cpp
global.hextern int v; // déclare que v est de type int, n'alloue pas de mémoire
extern int x; // déclare que x est de type int, n'alloue pas de mémoire
Projet.ino#include "global.h"
int v = 1; // définit v (alloue la mémoire)
....
x = x * v; // le compilateur sait que x existe - ça sera résolu au link
....
Toto.cpp#include "global.h"
int x = 8; // définit x (alloue la mémoire)
....
x = v-1; // le compilateur sait que v existe - ça sera résolu au link
....
Et rajoutez le petit bout de code en début du .h pour éviter d'importer 2 fois de suite le même .h (avec les #ifdef....)
Arfff
j'ai fait l'inverse.
declaration dans global.h
et les extern dans les autre .cpp
:)
:)
Ca veut dire que c'est pas grave ou je doit tout modifier ?
Ca veut dire que c'est pas grave ou je doit tout modifier ?
Oui....(tout,modifier)
(i ne devrait sans doute pas être global)
Suis sur mon mobile pour quelques jours encore donc peut pas vérifier...
sa tombe bien, j'ai pris deux jours pour me reposer la tête ... je vais faire ça lol
j'ai fait mon global.h
#include <SPI.h>
#include "RTClib.h"
#include "SdFat.h"
#include "WiFiEsp.h"
#include <Servo.h>
#include <DallasTemperature.h>
#include <OneWire.h>
/////////////////////////////////////// *** autres *** ///////////////////////////////////////
extern boolean graph_par_H;
extern unsigned long duree_nourissage_millis;
extern unsigned long heure_nourrissage1;
extern unsigned long heure_nourrissage2;
extern float consigne_ph_RAC;
extern float consigne_ph_BAC;
extern float ph_RAC, ph_BAC;
extern unsigned long temps_precedent_nourissage;
extern unsigned long temps_precedent_tempete;
extern const uint32_t verifeeprom;
extern uint32_t lectureverifeeprom;
extern boolean valider;
/////////////////////////////////////// *** brassage *** ///////////////////////////////////////
extern int pwm_jebao1, pwm_jebao2, pwm_jebao3;
extern int puissance_max_jebao1; //
extern int puissance_max_jebao2;
extern int puissance_max_jebao3;
extern int puissance_min_jebao1;
extern int puissance_min_jebao2;
extern int puissance_min_jebao3;
/////////////////////////////////////// *** eclairage *** ///////////////////////////////////////
extern int pwm_blanc1, pwm_blanc2, pwm_bleu1, pwm_bleu2;
extern int puissance_blanc;
extern int puissance_bleu;
extern unsigned long LEVER;
extern unsigned long COUCHER;
extern unsigned int progression;
extern unsigned long debut_LEVER_bleu1;
extern unsigned long debut_LEVER_bleu2;
extern unsigned long debut_LEVER_blanc1;
extern unsigned long debut_LEVER_blanc2;
// **************************************** fin lever
extern unsigned long fin_LEVER_bleu1;
extern unsigned long fin_LEVER_bleu2 ;
extern unsigned long fin_LEVER_blanc1;
extern unsigned long fin_LEVER_blanc2 ;
// **************************************** debut coucher
extern unsigned long debut_COUCHER_bleu2 ;
extern unsigned long debut_COUCHER_bleu1 ;
extern unsigned long debut_COUCHER_blanc2;
extern unsigned long debut_COUCHER_blanc1 ;
// **************************************** fin coucher
extern unsigned long fin_COUCHER_bleu2;
extern unsigned long fin_COUCHER_bleu1;
extern unsigned long fin_COUCHER_blanc2;
extern unsigned long fin_COUCHER_blanc1;
/////////////////////////////////////// *** temperature *** ///////////////////////////////////////
extern float temp_bac_graph[24], temp_rampe_graph[24], ph_BAC_graph[24], ph_RAC_graph[24];
extern float temp_bac, temp_rampe;
extern float temp_declenchement_vent_bac ; // temperature de declaenchement de la ventilation du bac
extern float temp_alerte_bac ; // temperature de declaenchement de la ventilation du bac
extern float temp_declenchement_vent_rampe;
/////////////////////////////////////// *** gsm *** ///////////////////////////////////////
extern boolean alerte;
const byte maxnumero2tel;
extern char numero2tel [];
extern const char * ATString;
extern const char * OKShortString;
extern const char * OKLongString;
const uint32_t oneSecond ;
extern const byte maxMessageSize;
extern char GSM_Message[];
extern char buffer[];
extern boolean envoyerSMS(const char * noDeTel, const char * messageAEnvoyer);
/////////////////////////////////////// *** horloge *** ///////////////////////////////////////
extern unsigned long Time, H;
extern unsigned int M, S;
extern unsigned long temps_precedent_nourissage, temps_precedent_tempete, temps_precedent_tempete_aleatoire;
/////////////////////////////////////// *** oscillo *** ///////////////////////////////////////
extern int oscillo1_angle1;
extern int oscillo1_angle2;
extern int oscillo2_angle1;
extern int oscillo2_angle2;
extern int oscillo3_angle1;
extern int oscillo3_angle2;
extern unsigned int delais_mouvement_oscillo_millis;
extern int position_oscillo1, position_oscillo2, position_oscillo3;
//extern Servo oscillo1, oscillo2, oscillo3;
extern boolean oscillo1_ON, oscillo2_ON, oscillo3_ON;
/////////////////////////////////////// *** tempete *** ///////////////////////////////////////
extern RTC_DS3231 rtc;
extern OneWire oneWire;
extern DallasTemperature sensors;
extern DeviceAddress sonde_bac, sonde_rampe;
extern boolean adresse_sonde_rampe, adresse_sonde_bac;
extern boolean tempete_ON;
extern unsigned long duree_tempete_millis;
extern int puissance_tempete;
extern unsigned long H_tempete_aleatoire;
extern unsigned long duree_tempete_aleatoire;
/////////////////////////////////////// *** sd *** ///////////////////////////////////////
extern SdFat SD;
extern void recup_para_sd();
/////////////////////////////////////// *** oscillo *** ///////////////////////////////////////
extern Servo oscillo1, oscillo2, oscillo3;
extern int position_oscillo1, position_oscillo2 , position_oscillo3;
/////////////////////////////////////// *** wifi *** ///////////////////////////////////////
extern boolean gsmPrintlnAndWaitATCommand(const char * command, const char * endMarker, unsigned long duration, boolean verbose);
const byte maxSsid, maxMdp;
extern char ssid[], mdp[];
extern int status;
extern void rebootwifi();
extern void printHTTPServerInfo();
const uint16_t HTTPPort;
extern WiFiEspServer server(HTTPPort);
/////////////////////////////////////// *** gestionweb *** ///////////////////////////////////////
extern const byte maxURL;
extern char urlRequest[];
extern const unsigned int maxLabelsOfInterest;
extern const char * labelsOfInterest[];
extern const byte maxHTTPLine;
extern char httpLine[];
j'avais des erreur de compil et j'ai perdu pas mal de temps sur les extern const byte ou uint32_t" mais j'ai trouvé la solution
il suffisait de ne pas mettre "extern" et de supprimer "= *"
par contre j'ai toujours une méga erreur concernant "serveur"
sketch\autres.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/4.9.2/../../../../avr/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions
sketch\bassage.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\eclairage.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\gestionweb.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\osmolation.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\sd.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\temperature.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\tempete.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\wifi.cpp.o (symbol from plugin): In function `server':
(.text+0x0): multiple definition of `server'
sketch\AQUABOUNS_V2_gsm.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1
Erreur de compilation pour la carte Arduino/Genuino Mega or Mega 2560
Je pense que cela vient de la :
const uint16_t HTTPPort;
extern WiFiEspServer server(HTTPPort);
J'ai chercher mais rien trouvé sur le net pour solutionner
Suis sur mon mobile pour quelques jours encore donc peut pas vérifier...
j'avais pas vu :/
pour pouvoir avancer, j'ai mis dans le setup //server.begin();
comme sa je peut continuer
Je vais avancer sur le communication nextion / arduino
Votre .h devrait avoir cette structure#ifndef GLOBAL_H_
#define GLOBAL_H_
....
#endif
extern WiFiEspServer server(HTTPPort);
Cette écriture fait plus que définir l'objet server, ça l'instancie (allocation de la mémoire et appel du constructeur)
Essayez juste extern WiFiEspServer server;
Et dans le bon fichier il faudra faire WiFiEspServer server(HTTPPort); pour allouer la mémoire
(https://media.istockphoto.com/vectors/two-thumb-up-emoticon-for-you-design-vector-id464368183?k=6&m=464368183&s=612x612&w=0&h=Ao7Wfu2eeThQVfNMmuFhUIvES_DyuFcA2Tmay_2q3kw=)
Pour envoyer les info au nextion :
fonction de base pour envoyer :
n0.setValue(valeur);
n0 correspond au nom du destinataire dans le nextion
setvalue correspond a l'ordre d'envoier
(valeur) correspond a la valeur envoyer a n0
l'ecran affichera donc dans le chanps n0 (valeur)
J'ai donc voulu faire
void num_send_to_nextion(char *objname, float T) {
unsigned int numtogo;
numtogo = T * 10; // le nextion ne gere pas de float donc tout est *10
(objname).setValue(numtogo);
pour que quand j'appelle :
num_send_to_nextion(n0, temp_bac) // temp bac = 26.6
cela envoie
n0.setValue(266);
Mais le compilateur me répond
sketch\nextion.cpp: In function 'void num_send_to_nextion(char*, float)':
nextion.cpp:46: error: request for member 'setValue' in 'objname', which is of non-class type 'char*'
(objname).setValue(numtogo);
^
exit status 1
request for member 'setValue' in 'objname', which is of non-class type 'char*'
C'est les déclaration dans la bibliothèque nextion qui me bloque ?
Le nom d'un objet est un pointeur en mémoire vers l'objet pas son nom de variable ascii dans votre code... une fois compilé tous les noms disparaissent... donc pas faisable avec une chaîne de caractère comme on peut le faire dans certains langages interprétés
Soit chaque fonction parle spécifiquement à un objet, soit Il faut conserver par exemple un tableau de tous les objets nextion créés et les référencer par leur index dans le tableau
ok ba tempi, je vais pas monter une usine a gaz ...
Vous pourriez faire une macro #define SETx10VALUE(x) setValue((int)((x)*10))
Et au lieu d'appeler n0.setValue(valeur);
fairen0.SETx10VALUE(valeur);
et laisser le préprocesseur bosser pour vous
J'ai tellement de variable a convertir de manière différent que cela ne vos même pas le coup de faire des fonctions.
Ok
j'ai bien avancé aujourd'hui mais énormément de temps passer a résoudre les problèmes lié a avoir le code étalé dans plusieurs fichiers ...
A chaque ligne nouvellement écrit faut en ajouté partout ...
:smiley-confuse:
Je dirai (sans méchanceté) que c'est un signe d'un code mal « architecturé » - normalement on a des unités de code relativement indépendantes et il n'y a pas besoin de tout déclarer partout
Ce sera pour la prochaine version :)
Ce sera pour la prochaine version :)
https://www.youtube.com/watch?v=pbaXL7UwzQ4 (https://www.youtube.com/watch?v=pbaXL7UwzQ4)
:o :smiley-confuse: :D :D :D
normalement on a des unités de code relativement indépendantes et il n'y a pas besoin de tout déclarer partout
Je n'ai tout de même pas tout a appeler partout mais comme il y a un fichier.cpp par catégorie (Eclairage, brassage, ect..) et que, par exemple, l'écran doit accéder (pour lire et modifier) a toutes ces catégorie ... donc :smiley-confuse: :smiley-confuse: :smiley-confuse:
Il n'en reste pas moins qu'il y a surement une meilleur architecture a adopter
Je vais encore passer la mâtiné dessus et je donnerais un aperçu du rendu visuel + programme ( la première page de l'affichage commence a afficher et réagir :) je ressent vraiment que ça se concrétise )
Djbouns and J-M-L bonjour.
Je vous suis toujours quoique suivre n'est pas le mot approprié, je suis même perdu mais j'aime suivre le développement de cet automate.
Quel boulot accomplis.
Pascal.
salut breisleach,
Merci
y en a encore a faire :)
depuis hier je fouille dans la biblioteque nextion pour comprendre et avancé mais y a un truc que je ne trouve pas et comprend pas
le "void*ptr"
void t0PopCallback(void *ptr)
{
dbSerialPrintln("t0PopCallback");
t0.setText("50");
}
Dans certain exemple fourni par nextion il y a dans le setup :
t0.attachPop(t0PopCallback, &t0);
et dans d'autre
t0.attachPop(t0PopCallback);
Cela fonctionne dans les deux cas
J'ai aussi testé de faire
t0.setText("50");
en dehors de la fonction est ca fonctionne aussi
donc je ne comprend pas a quoi sert "void*ptr"
À mon avis (c'est typique dans le design des IHM en MVC) la fonction callback (celle que vous attachez au bouton) prend en paramètre un pointeur sur l'objet clické. Si vous n'avez qu'un seul bouton ou un callback pour chaque bouton ça ne sert pas puisque dans la fonction vous savez quel bouton a été clické, mais si vous attachez le même callback à plusieurs éléments alors ça vous permet de savoir lequel a été clické. Le fait que le pointeur ne soit pas typé (utilisation de void * qui veut dire pointeur en mémoire sur n'importe quoi - ça pourrait être un NexButton * ou un NexText *c'est pour permettre d'avoir le même callback pour plusieurs types d'objets)
Il se peut aussi que ce soit pour passer un paramètre lors du callback, éventuellement si c'est configurable lors de la définition du callback
Pour vérifier Dans votre callback faites void t0PopCallback(void *ptr)
{
if (ptr == (void *) &t0) dbSerialPrintln("OK c'est bien ce que je pensais"); // on vérifie si le pointeur est bien t0
dbSerialPrintln("t0PopCallback");
t0.setText("50");
}
Quand vous clickez ça va vérifier que ptr pointe bien en mémoire sur t0
Et éventuellement tester aussiint toto; // variable globale
...
t0.attachPop(t0PopCallback, (void *) &toto);
...
void t0PopCallback(void *ptr)
{
if (ptr == (void *) &toto) dbSerialPrintln("OK j'ai bien toto en param"); // on vérifie si le pointeur est bien sur notre entier
dbSerialPrintln("t0PopCallback");
*((int*) ptr) = 50; // on met 50 dans toto
t0.setText("50");
}
Si cette approche fonctionne alors ça vous permet de partager le même callback entre plusieurs champs texte par exemple mais chacun piloterait une variable différente en mémoire
sa compile pas
invalid cast from type 'NexText' to type 'void*'
J'avais penser que c'etait le bouton que l'on attachait a la fonction mais je comprenait pas pourquoi si on ne l'indiquais pas sa fonctionnais quand meme.
Vous avez écrit ça comment?
Vous avez écrit ça comment?
void t4PopCallback(void *ptr){
if (ptr == (void *) t4){
dbSerialPrintln("OK c'est bien ce que je pensais"); // on vérifie si le pointeur est bien t0
}
dbSerialPrintln("t0PopCallback");
t4.setText("50");
}
:smiley-confuse:
Ah j'ai merdé évidemment il faut mettre l'adresse de l'objet (void *) &t4 dans le test (je suis sur mon tel donc peux pas tester)
Ah j'ai merdé
(https://i.pinimg.com/736x/51/d4/ef/51d4ef870146758ca24c3abe63cf9dc8--emoji-faces-smiley-faces.jpg)
OK c'est bien ce que je pensais
t0PopCallback
(http://img109.xooimage.com/files/6/d/9/best20smiley-4642d03.gif)
:)
Vous avez essayé avec toto?
Vous avez essayé avec toto?
?
Relisez le message de la page précédente j'étais en train de l'éditer quand vous faisiez le test
sa marche pas avec toto
Ok - dommage :(
Donc c'est juste le caller qui est passé en paramètre sans doute
Ok - dommage :(
si tu le dit :)
enum modeBrassage : byte {brassageArret, brassageActif, brassagePWM, retourBrassageArret};
modeBrassage brassage1_ON = brassagePWM, brassage2_ON = brassagePWM, brassage3_ON = brassagePWM;
Je n'arrive pas a declarer cette enum dans mon global en extern
j'ai besoinde
brassageArret, brassageActif, brassagePWM, retourBrassageArret
et
brassage1_ON
Sur le net il dise que "enum" est un "int" mais j'ai
'brassageArret' redeclared as different kind of symbol
a la compil
Enum par défaut est un int sauf si vous lui préciser d'utiliser un byte...
enum modeBrassage : byte {brassageArret, brassageActif, ....
Mais à mon avis ça ne vient pas du type byte ou int.
dans cette déclaration modeBrassage est un type il faut donc que les variables qui vont prendre ces valeurs nommées soient de type modeBrassage pas int ni byte
Un enum ne se déclare pas en externe, ce ne sont pas des valables donc vous mettez juste l'enum Dans le .h global
(http://www.camping-vacaf.fr/wp-content/uploads/2015/07/Danse-des-pouces.gif)
Les ligne "enum" transféré dans global et "extern modeBrassage" egalement dans le global
Tout est Ok
Il est trop tard pour faire une vidéo de l'écran comme promis :smiley-confuse: mais la page d'accueil est presque fini et fonctionnel (tout les bouton son rattacher dans l'arduino et son couplé a leur fonction et toute les variables son bien envoyer par l'arduino et reçu/affiché par l'écran)
Il faudra, quand tout le code sera fini avec l'écran, vraiment se pencher sur le loop car l'envoie/réception de donné avec l'écran prend un certain temps (~1/3 de seconde juste pour la MAJ des info température, éclairage, brassage ect ...) et les bouton de l'écran ne réagisse plus pendant se temps la ...
J'ai passer une bonne partie de ces deux jour a remettre de l'ordre dans le code.
Tout n'est pas fini, il manque encore des commentaire et de la mise en forme mais j'espère que ces compréhensible.
Je peut pas bosser sur le code se week end mais je garde un oeil sur le forum, a l'affût de vos commentaire.
Je suis en vadrouille donc prut pas voir le code. Vous devriez penser à faire un github :)
Je suis en vadrouille donc prut pas voir le code. Vous devriez penser à faire un github :)
c'est prevu, d'ailleurs sur la page info l'adresse est deja indiqué mais je le ferait quand le code seras ok. Le code reste dispo avec mon message precedent et moi aussi jsui en vadrouille donc jboss pa dessu. Kan tu poura, si yu peut mdire ske ten pense a+
Boun's.
Je n'y connais pas grand chose, bon je fais des nano pour les température et humidité plus quelques petits montage sur Tft, mais avoir vu d'autre prog un peu fouilli, je trouve le tien lisible et compréhensif même pour un néophyte que je suis.
Je viens de demander un devis pour un 550 litres surverse centrale ( mais vraiment central) je vais donc le réceptionner, le mettre à vieillir et voir si tu pourras m'aider à mettre ton prog en place, si tu es d'accord car développer ce que tu as faits me semble être un but que je ne saurais atteindre.
Au fait que des coraux mous il seras.
En attente de te lire et continuer à suivre cette belle aventure.
Bonne soirée à toi.
Pascal.
J-M-L bonsoir idem pour toi une bonne soirée.
je trouve le tien lisible et compréhensif même pour un néophyte que je suis.
Je trouvais mon code précédent compréhensif aussi mais la priorité est de faire les choses dans les règles.
J'espère avoir atteint se but.
J'attends la validation de l'expert ;) quand il pourras regarder
Cc,
J'ai reussi a faire une focntion pour dessiner les courbes.
La dificulté etait dans le fait que le nextion dessin les courbe par pixel, on ne peut pas Ecrire un point au milieu par exemple.
Si le tableau fait 800 pixels de large alors il faut envoyer 400 pixels pour que la courbe arrive a la moitié du tableau ...
Il m'as fallu faire des calcules pour que tout tombe juste en ayant 48 valeur (pour la courbe 24h) ou 84 (pour la courbe 7 jours)
j'ai donc fait ca (pour celle de 24h)
void courbe_24h_T(int T_precedent, int T_suivant) { // dessine la courbe temperature sur 24h (boucle de 15 pixels, a lancer 48 fois pour graph complet ******************
int val_courbe = T_precedent;
int i = 0;
DPRINTLN("envoie 15 pixels au graph temperature sur 24h");
while (i < 15) { // boucle 15 fois
val_courbe = val_courbe + ((T_suivant - T_precedent) / 15); // ajoute a la valeur precedent la diference entre les 2 mesures divisé en 15
int pixel_courbe = map(val_courbe, 2400, 2800, 0, 157);
s0.addValue(0, (pixel_courbe)); // envoie 1 pixels
i++;
il suffit ensuite de lancer cette fonction 48 fois avec les températures relever entre 0h et 23h30
j'en arrive donc au stockage et récupérations des températures.
pour les utiliser il faut qu'elle soit en "int"
J'imaginais utiliser la carte SD mais cela signifie qu'il faudra lire les info TXT puis chercher dedans chaque donnée, l'extraire pour lancer la fonction et faire cela 48 fois ...
c'est l'usine a gaz ...
Y a une autre solution ?
Salut
posé à un endroit avec un mon mac connecté correctement à internet :)
J'ai jeté un oeil au code quelques commentaires / critiques constructives:
appuyez sur ctrl-t dans chacun des fichiers pour qu'ils soent correctement indentés. Virez les lignes vides superflues.
Une convention de programmation est que les noms en majuscules soient plutôt réservés pour les constantes ou les éléments d'un enum (constants aussi) --> Je changerai donc le nom de vos fonctions globales pour suivre le camelCase par exemple
ALERTE_TEMPERATURE() devient
alerteTemperature() et idem pour tout le reste du code.. je sais c'est du boulot.... :)
tous les noms de vos
NexVariable n0 n1 etc sont peu parlants.... si n4 est l'affichage % brassage 3 pourquoi ne pas appeler la variable
brassage3PerCent ou
nexionBrassage3PerCent ou
nxBrassage3PerCent.. idem pour les p, b, t g et j et va0. la longueur d'un nom de variable est viré à la compilation donc ça ne prend pas plus de place et le code ensuite est plus lisible / compréhensible quand on y revient 1 an plus tard ou que quelqu'un se penche dessus.
ça je vous ai déjà dit que ce n'est pas une bonne pratique :)
void setup() {
#include "setup.h"
}
Un .h c'est un en-tête, on y déclare des variables importe d'autres header etc mais on ne s'en sert pas pour injecter du code. c'est pas beau, c'est pas fait pour cela. et surtout ici vous avez même des #include aussi que vous injectez donc au coeur d'une fonction... ça ne se fait pas du tout!! beurk :)
Si vous voulez que le code du setup soit ailleurs dans un autre fichier, créez un setup.h et un setup.cpp et dans le fichier setup.cpp définissez une fonction
void customSetup(). Votre sketch lui va importer setup.h comme un header comme les autres dans lequel la déclaration de
void customSetup(); sera effectuée et votre vrai setup sera alors
void setup() {
customSetup();
}
mais je ne suis pas persuadé que ce soit utile, à mon avis vous pouvez mettre directement le code dans le setup()....
idem pour pageweb.h (et les autres codes HTML) et pour être efficace votre page web devrait n'a pas besoin d'être "jolie" en HTML, virez tous les blancs ou \r\n (mettre juste un espace à la place) en trop et compactez tout ça (par exemple la variable zone_dessin pourrait s'appeler zd uou juste z et graphe pourrait être juste g... on n'écrit pas
< / script > mais
</script> idem pour
< / body >--> ça sert à rien de transmettre des octets qui ne servent à rien.
dans la loop() vous avez
if (millis()-refresh > 10000) { --> le 10000 devrait être un const unsigned long ou un #define dans vos fichiers de param. il faut éviter d'avoir des nombres magiques qui trainent.
idem ailleurs dans le code, j'ai vu un
if (temp_rampe > 65) { // variation du thermo temperature rampe --> 65 devrait être une constante déclarée quelque part et pas au milieu du code
quand vous faites un
if (temp_bac * 10 > 280) { // variation thermo temperature aquarium autant écrire
if (temp_bac > 28) { et bien sûr le 28 devrait aussi être une constante déclarée (comme vous avez fait pour
temp_alerte_bac_haut par exemple )
évitez de dupliquer les calculs
void refresh_nextion_ph() {// maj des mesure ph ******************************************************************************************************************
if (int ph_BAC_nextion = (ph_BAC * 100) < 100) {
n10.setValue(ph_BAC_nextion); // affiche le ph du bac
}
if (int ph_RAC_nextion = (ph_BAC * 100) < 100) {
n11.setValue(ph_RAC * 100); // affiche le ph du rac
}
}
--> dans ce code vous calculez 2 fois
ph_BAC * 100 et vous créez 2 variables différentes BAC et RAC... il y a sans doute quelque chose de louche sur le second.. ça sent le bug
il y a un fichier qui porte mal son nom -->
bassage.cpp manque un r
dans ce fichier il y a la valeur magique 2.55 qui revient hyper souvent.. il faudrait la définir.. et comme vous faites un map juste avant de définir l'analogWrite, vous pourriez même inclure le facteur d'échelle par 2.55 dans la fonction affine map()
const uint16_t HTTPPort = 80; devrait être dans
gestionweb.h et pas dans le .cpp
avoir ça quand on fait un petit bout de code de démo c'est OK, mais pas dans un vrai programme en production.
void dieHere() {
while (true);
}
il me semble que ce n'est pas appelé donc ce n'est pas grave, mais autant le virer
DPRINT (" \" a été envoyer au : ");
--> a été envoyé :)
attention, pas sûr que les accents vont bien sortir..
dans horloge.cpp attention (même si ici ça ne déborde pas car H est déjà un
unsigned long ) aux calculs faits en
int au lieu de
unsigned long quand on a de grosses constantes il vaut mieux écrire
H = (H * 3600ul); pour s'assurer qu'on force le calcul en unsigned long
Sinon c'est quand même déjà bcp mieux que la version 1, ça vaut presqu'un bon point de plus (car il y a des choses que je vous avais déjà dit à corriger :) )
(http://forum.arduino.cc/index.php?action=dlattach;topic=504431.0;attach=242864)
Bonjour jm,
content de te revoir, j'espère que ton absence de réseau est du au fait que tu est sous les tropic et non dans la creuse lol.
Je commence les cartons du déménagement donc sa va être compliqué d'etre sur le code les weekend (mais j'en profite le vendredi :) )
Je commence également a me dire que le système ne seras pas opérationnel a temps (pour être installer a la mise en eau du nouveau bac dans ma nouvelle maison), mais bon, ce n'est pas faute d'y passer le maximum de temps.
La partie graphique me prend énormément de temps et allonge le code de manière phénoménale ...
en pièce jointe le travail de cette semaine et ça ne représente que la page d'accueil ...
Je met cette partie en stand bye pour corriger tout se qui ne va pas.
je commence donc la lecture ... et mise en application :D
appuyez sur ctrl-t dans chacun des fichiers pour qu'ils soent correctement indentés. Virez les lignes vides superflues.
fait :smiley-cool:
Une convention de programmation est que les noms en majuscules soient plutôt réservés pour les constantes ou les éléments d'un enum (constants aussi) --> Je changerai donc le nom de vos fonctions globales pour suivre le camelCase par exemple ALERTE_TEMPERATURE() devient alerteTemperature() et idem pour tout le reste du code.. je sais c'est du boulot.... :)
Ok pour les majuscule.
Dans ton exemple tu a retirer les "_" , ils sont aussi a retirer?
On réserve souvent les _ pour des variables privées / cachées, ou les types genre uint8_t
Maintenant ce ne sont juste que des conventions donc rien d'obligatoire!
Bon courage pour le déménagement
(Dans les montagnes - pas dans les Iles)
merci
tous les noms de vos NexVariable n0 n1 etc sont peu parlants.... si n4 est l'affichage % brassage 3 pourquoi ne pas appeler la variable brassage3PerCent ou nexionBrassage3PerCent ou nxBrassage3PerCent.. idem pour les p, b, t g et j et va0. la longueur d'un nom de variable est viré à la compilation donc ça ne prend pas plus de place et le code ensuite est plus lisible / compréhensible quand on y revient 1 an plus tard ou que quelqu'un se penche dessus.
fait 8)
profite bien
(https://aminus3.s3.amazonaws.com/image/g0008/u00007768/i00773393/3a34e194b44522d578420e70d40a5a9a_large.jpg)
ce ne sont juste que des conventions donc rien d'obligatoire!
Je veut faire ca bien ;) donc c'est partie ! :smiley-roll-sweat:
(https://www.aspentimes.com/wp-content/uploads/2017/03/untucked-atw-030917.jpg)
(https://www.aspentimes.com/wp-content/uploads/2017/03/untucked-atw-030917.jpg)
On peut pas être doué en tout.
:)
vous pourriez même inclure le facteur d'échelle par 2.55 dans la fonction affine map()
Faire comme ça ?
float pourCentEnPwm = 2.55;
pwm_jebao1 = map(Time, debut_LEVER_bleu1, fin_LEVER_blanc2, (puissanceMinBrassage1*pourCentEnPwm), (puissanceMaxBrassage1*pourCentEnPwm));
Le problème en faisant ca c'est qu'ensuite je suis obliger de tout diviser par "pourCentEnPwm" pour pouvoir afficher sur l'écran en %.
Donc si je fait
analogWrite (pinOutBleu2, pwmBleu2 * pourCentEnPwm);
c'est bon ?
OK si c'est utilisé sans le facteur ailleurs alors ça revient plus ou moins au même - éventuellment prendre l'option qui fait l moins de calculs
OK si c'est utilisé sans le facteur ailleurs alors ça revient plus ou moins au même - éventuellment prendre l'option qui fait l moins de calculs
C'est bien celle ci donc ok merci
Je continu a tout mettre a jour
Effectivement c'est beaucoup de boulot.
C'est le deuxième jours ... :)
que penser vous de cette constatation fait en fin de semaine dernière :
J'ai modifier le Nextion pour communiquer avec lui en 115200 au lieu de 9600 par Default.
j'ai fait quelque test et je n'est vu aucune amélioration du temps de réponse ( j'ai envoyer une suite de paramètre dans l'écran qui retournait une valeur dans le serial moniteur)
Normal ?
Une convention de programmation est que les noms en majuscules soient plutôt réservés pour les constantes ou les éléments d'un enum (constants aussi) --> Je changerai donc le nom de vos fonctions globales pour suivre le camelCase par exemple ALERTE_TEMPERATURE() devient alerteTemperature() et idem pour tout le reste du code.. je sais c'est du boulot.... :)
fait
C'était long ... :smiley-sweat: :smiley-sweat: :smiley-sweat:
J'en est profité pour modifier certain nom afin que se soit plus claire
évitez de dupliquer les calculs
void refresh_nextion_ph() {// maj des mesure ph ******************************************************************************************************************
if (int ph_BAC_nextion = (ph_BAC * 100) < 100) {
n10.setValue(ph_BAC_nextion); // affiche le ph du bac
}
if (int ph_RAC_nextion = (ph_BAC * 100) < 100) {
n11.setValue(ph_RAC * 100); // affiche le ph du rac
}
}
--> dans ce code vous calculez 2 fois ph_BAC * 100 et vous créez 2 variables différentes BAC et RAC... il y a sans doute quelque chose de louche sur le second.. ça sent le bug
y voit tout ...
c'est rectifié
// ******************************************************************************************************************************************************************
void rafraichirPhBacNextion() {// maj des mesure ph *********************************************************************************************************************
if (changementPhBac != (phBac * deuxDecimalEnEntier)) {
changementPhBac = (phBac * deuxDecimalEnEntier);
if (changementPhBac > 999) {
changementPhBac = 0;
}
affichagephbac.setValue(changementPhBac); // affiche le ph du bac
DPRINTLN("refresh ph bac");
}
}
void rafraichirPhRacNextion() {// maj des mesure ph *********************************************************************************************************************
if (changementPhRac != (phRac * deuxDecimalEnEntier)) {
changementPhRac = (phRac * deuxDecimalEnEntier);
if (changementPhRac > 999) {
changementPhRac = 0;
}
affichagephrac.setValue(changementPhRac); // affiche le ph du rac
DPRINTLN("refresh ph rac");
}
const uint16_t HTTPPort = 80; devrait être dans gestionweb.h et pas dans le .cpp
Donc toute les variable utilisé uniquement dans un *.cpp doivent être mis dans le*.h du même nom ?
Si c'est bien ça, j' n'ai pas que celle la a déplacer ...
Pour rester sujet "déclaration", pour optimiser le code il faut que je fasse le maximum de déclaration a intérieur d'une fonction (a condition que la valeur de celle ci ne soit pas nécessaire en dehors de la fonction ou elle est déclaré) ?
Si oui, j'ai encore un coup de balais a passer lol
Non pas obligatoirement mais faut garder une cohérence pour le .h
Oui si on n'a besoin d'une variable que dans une seule fonction pas la peine de la mettre globale
Allez pour le boulot fourni :)
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
Non pas obligatoirement mais faut garder une cohérence pour le .h
Je ne comprend pas de quelle cohérence tu parle avec l'exemple "const uint16_t HTTPPort = 80; devrait être dans gestionweb.h et pas dans le .cpp"
Oui si on n'a besoin d'une variable que dans une seule fonction pas la peine de la mettre globale
Allez pour le boulot fourni :)
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
YESsss
Sa fait 3 :)
Comme vous avez mis le reste dans le .h, le port pourrait y être aussi - par cohérence. si ça ne sert à personne d'autre alors on peut aussi tout garder dans le .cpp
L'idée du .h c'est pour partager
Ok, faudras que je regarde tout alors.
Pour info, j'ai passer le week end sur un probleme :
la gestion des heures parametrable dans le nextion.
> quand l'heure était en 2 decimal dans le nextion (heure et minute) il n'est pas capable d'ecrire "00" en minute , il ecrit "0"
Il a donc falu trouver une solution ...
J'ai finni par afficher l'heure en texte (les minutes seulement), le nextion convertie le texte en decimal, fait la modif + ou - et convertit decimal en texte.
Il faut ensuite que l'arduino récupère le texte quand on sauvegarde, le transforme en decimal pour fait le calcul pour l'avoir en seconde
memset(minuteEnTexteIn, 0, sizeof(minuteEnTexteIn));
valeurleverSoleilh.getValue(&numberh);// recupere la valeur dans nextion
valeurleverSoleilm.getText(minuteEnTexteIn, sizeof(minuteEnTexteIn)); // recupere la valeur dans nextion
DPRINTLN(minuteEnTexteIn);
horraireEnSeconde(numberh, minuteEnTexteIn); // calcule h*3600+m*60
leverSoleil = recupHorraireTemporraire;// attribut la valeur nextion dans l'arduino
EEPROM.put(saveleverSoleil, leverSoleil);// sauvegarde dans l'eeprom la valeur
void horraireEnSeconde(unsigned long h, char m [sizeof(minuteEnTexteIn)]) {
DPRINTLN("*********************** fonction horraireEnSeconde *************************");
int M = atoi(m);
recupHorraireTemporraire = ((h * 3600ul) + (M * 60));
}
Et inversement quand on charge la page pour afficher le paramétrage actuel :
secondeEnHorraire(leverSoleil) ;
valeurleverSoleilh.setValue(numberh);
valeurleverSoleilm.setText(minuteEnTexteOut);
void secondeEnHorraire(unsigned long t) {
unsigned long h, m;
h = t / 3600u;
m = (t / 60) % 60;
numberh = h;
if (m == 0) {
minuteEnTexteOut[0] = '0';
minuteEnTexteOut[1] = '0';
minuteEnTexteOut[2] = '\0';
}
else if (m == 5) {
minuteEnTexteOut[0] = '0';
minuteEnTexteOut[1] = '5';
minuteEnTexteOut[2] = '\0';
}
else {
itoa (m, minuteEnTexteOut, 10);
}
}
Ça fonctionne
j'ai eu l'impression d'avoir un beug a un moment mais pas sur et après plusieurs essai répéter je n'ai rien vu d'anormal.
c'était compliqué...
Dans les exemple nextion les "char" sont declarer comme ca :
char exemple [10]= {0};
a quoi sert "= {0}" ?
a quoi sert "= {0}" ?
À initialiser le tableau à 0
Les deux fonctions avec les "numérique" <<<>>> char te semble correcte sinon ?
(j'ai encore du mal avec les "char", peut être subsiste une petite erreur génératrice de bug )
Bouns bonjour.
Trois joli bon point ou faut-il aussi compter celui sans le coin haut droit ?????
En tout cas tu abat un boulot de fou et en attendant le final haut en couleurs c'est sur, j'applaudi d'avance.
J'ai passer commande d'un bac en 180 x60 x60 et aurais besoin de tes lumières pour la décantes mais c'est hors sujet ici.
J-M-L bonjour.
Allez zou bonne journée à vous deux.
Pascal.
Salut Pascal,
Merci ;)
on peut parler de ton bac sur recifal france ou envoie moi un MP
a+
Pour info :
Le croquis utilise 53254 octets (20%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 6703 octets (81%) de mémoire dynamique, ce qui laisse 1489 octets pour les variables locales. Le maximum est de 8192 octets.
La mémoire disponible faible, des problèmes de stabilité pourraient survenir.
Le code commence a être très long :) :) :)
quand je retire les serial.print sa redescend a 60%
J'ai vraiment un problème avec des "const byte" utilisé a plusieurs endroit.(utilisé pour délimité l'écriture d'un "char")
Des que je l'ai mes en extern sur le global.h j'ai une erreur de compil.
Ça fait 2h que je navigue sur le net mais je trouve rien :smiley-confuse:
comment faire ?
Si c'est un truc comme const byte toto = 12;
, toto n'est pas une variable globale. C'est une constante qui disparaît à la compilation.
Le compilateur remplace toto par 12 partout dans le code.
Et tu obtiens exactement le même code compilé que si tu avais mis #define toto 12
Donc pas étonnant que le compilo râle si tu essayes de la déclarer en extern.
content de te revoir bricoleau.
Mais comment faire pour pouvoir utiliser ces constantes dans different *.ccp ?
Ben tu le mets dans un fichier header .h
Cela ne devrait pas poser de problème particulier.
Et bien sûr, afin d'éviter tout problème d'inclusions multiples d'un même header dans divers cpp compilés ensemble, le contenu d'un .h doit toujours être de la forme :
#ifndef monfichierheader_h
#define monfichierheader_h
...
(contenu utile du header)
...
#endif
Ainsi, quand le compilateur rassemble tous les bouts de code en un seul :
- le premier #include "monfichier.h" va importer le contenu utile du header
- les #include suivants ne vont rien importer car monfichierheader_h aura déjà été défini, donc on n'entre pas dans la clause ifndef
C'est la mécanique standard pour éviter les doublons d'inclusion.
arfff, on en revient a se que l'on disait il y a quelque message avec jean marc sur les déclarations a mettre dans le .h
mais du coup j'ai une erreur a la compile
'byte' does not name a type
faut ajouter #include "arduino.h" dans chaque .h, c'est normal ?
Byte est spécifique au monde arduino. Le type à utiliser est uint8_t
Byte est spécifique au monde arduino. Le type à utiliser est uint8_t
ca me donne la meme chose une fois dans le header
'uint8_t' does not name a type
Il nya pas un bug avant ?
le message global In file included from sketch\wifi.cpp:1:0:
wifi.h:3: error: 'uint8_t' does not name a type
const uint8_t maxSsid = 30;// prévoir la bonne longueur
^
wifi.h:4: error: 'uint8_t' does not name a type
const uint8_t maxMdp = 30;// prévoir la bonne longueur
^
wifi.h:5: error: 'uint8_t' does not name a type
const uint8_t maxURL = 40;
^
wifi.cpp:12: error: 'maxURL' was not declared in this scope
char urlRequest[maxURL + 1]; // +1 pour avoir la place du '\0'
^
wifi.cpp:13: error: 'maxSsid' was not declared in this scope
char ssid[maxSsid + 1];
^
wifi.cpp:14: error: 'maxMdp' was not declared in this scope
char mdp[maxMdp + 1];
^
Plusieurs bibliothèque trouvées pour "DallasTemperature.h"
Utilisé : C:\Users\djbouns\Documents\Arduino\libraries\DallasTemperature
Non utilisé : C:\Program Files (x86)\Arduino\libraries\DallasTemperature
exit status 1
'uint8_t' does not name a type
les erreur precedente me semble nomale puisque "uint8_t" n'est pas connu, le nom de la constante est non déclaré dans le .cpp
Bonjour,
Met
#include <Arduino.h>
dans ton fichier .h
Bonjour,
Met
#include <Arduino.h>
dans ton fichier .h
bONJOUR ICARE,
j'avais remarquer que cela fonctionnait en faisant cela mais ca veut dire qu'il faut le mettre dans chaque header ? apres chaque #define monfichierheader_h pour qu'il ne soit pas charger plusieurs fois ?
il n'y a pas de raisons que ça ne fonctionne pas mais il faut respecter les règles C / C++
sketch principal:#include "glob.h"
void setup() {
Serial.begin(115200);
Serial.println(toto);
}
void loop() {}
Header: glob.h#ifndef glob_h
#define glob_h
#include <stdint.h> // http://www.cplusplus.com/reference/cstdint/
extern const uint8_t toto;
#endif
Implémentation: glob.cpp#include "glob.h"
const uint8_t toto = 10;
ça devrait compiler sans warnings et imprimer 10 dans la console.
j'avais remarquer que cela fonctionnait en faisant cela mais ca veut dire qu'il faut le mettre dans chaque header ? apres chaque #define monfichierheader_h pour qu'il ne soit pas charger plusieurs fois ?
le fichier Arduino.h (https://github.com/arduino/Arduino/blob/3d3bb385ad7575596b07ca20bf55d50a0166a215/hardware/arduino/avr/cores/arduino/Arduino.h#L20-L21) commence lui aussi par
#ifndef Arduino_h
#define Arduino_h
donc ne sera pas inclus deux fois quel que soit l'endroit où vous faites l'import
effectivement, en ajoutant #include <stdint.h> dans mon header sa compil sans problème.
J'ai bien la valeur de ma constante dans le moniteur.
Donc c'est ok :) :) :)
une dernière précision svp,
bricoleau me disait de déclarer ces constantes dans mon header. (les extern son dans global.h)
jml, dans l'exemple ci dessus, a mis la declaration dans le .cpp et les extern dans le header du .cpp du même nom.
La quelle des deux dois je appliquer ?
mais il faut respecter les règles C / C++
difficile sans formation :)
D'ailleurs vous pouvez peut être m'aider, j'ai déjà été a deux reprise voir dans des magasin pour trouver un bouquin simple et complet sur l'arduino/c++
a première vu sa ma paru très "pauvre" >>> exemple, l'arduino pour les nul
Ou alors c'était niveau bac ++
Une suggestion sur un ouvrage qui pourrait m'apprendre toute les "réglés" de base sans être nian nian ou hard ?
Header: glob.h
#ifndef glob_h
#define glob_h
#include <stdint.h> // http://www.cplusplus.com/reference/cstdint/
extern const uint8_t toto;
#endif
Implémentation: glob.cpp
#include "glob.h"
const uint8_t toto = 10;
Euh...
Peut-être que coder ainsi est nécessaire avec d'autres compilos que celui de notre IDE Arduino, à moins que ce ne soit une intransigeance des ayatollahs de la syntaxe... :)
Mais je maintiens : dans le cas d'une constante simple, c'est-à-dire pas un tableau, il n'est pas nécessaire de s'embêter ainsi.
toto n'est pas une variable globale pour laquelle un espace ram est réservé, contenant une valeur fixe, qui justifierait l'emploi du mot clé "extern", ainsi que sa définition dans un fichier cpp.
La pré-compil remplace partout toto par sa valeur littérale.
Le code compilé résultant est le même que si on avait mis un #define toto 10, sauf qu'au passage le compilo a pu contrôler que le type de toto est bien conforme à son usage.
Mettre directement la valeur dans un header ne pose aucun souci avec l'IDE arduino
header constantes.h#ifndef constantes_h
#define constantes_h
#include <arduino.h>
const uint8_t toto = 10;
#endif
et pas besoin d'un fichier cpp associé
Me gourre-je ?
@bricoleau --> Mon point était juste de montrer que ça fonctionnait aussi avec des constantes et que l'endroit de définition n'était pas obligatoirement l'endroit de déclaration. ça fonctionne comme le reste,
const ou pas
const. L'important est que la définition (allocation de la mémoire correspondante) ne doit se faire qu'une seule fois.
toto n'est pas une variable globale pour laquelle un espace ram est réservé, contenant une valeur fixe, qui justifierait l'emploi du mot clé "extern", ainsi que sa définition dans un fichier cpp.
La pré-compil remplace partout toto par sa valeur littérale.
Le code compilé résultant est le même que si on avait mis un #define toto 10, sauf qu'au passage le compilo a pu contrôler que le type de toto est bien conforme à son usage.
Si si
toto est bien une variable globale
avec un espace ram réservé, contenant une valeur fixe. Suivant les architectures cette variable peut-être placée en mémoire en lecture seule par exemple (OS avec des pages protégées par exemple).
Le remplacement d'une constante par sa valeur littérale est un effet d'optimisation du
compilateur et non pas du pré-processeur et donc c'est différent d'un
#define (même si pour un entier c'est ce que le compilateur va générer).
Personnellement je déconseille les #define (sauf directives de compilation optionnelle comme dans les fichiers .h) et préfère une bonne définition de
const. Le mot clé
const à l'origine a été pensé pour vérifier la justesse sémantique du code et ensuite seulement utilisé pour des optimisations --> en utilisant un
type donné pour le
const GCC peut effectuer toutes les vérifications d'usage et vous n'aurez pas d'effets de bords potentiellement liés à une promotion indue.
de même j'utilise des
enum plutôt que des
define si j'ai plusieurs valeurs
enfin les const peuvent avoir un scope précis que les #define ne permettent pas
(https://s00.yaplakal.com/pics/pics_original/2/6/9/3918962.jpg)
Bon bon d'accord
C'est le compilateur avec les paramètres de l'IDE Arduino qui optimise.
N'empêche que j'ai quand même un peu de mal à mettre mes const en extern dans un cpp, vu que cela ne change strictement rien dans l'exécutable televerse dans l'arduino, ni dans leur portée, ni dans les contrôles de typage à la compilation.
À moins d'avoir un bout de code multi plateforme écrit pour la postérité, non exclusivement destiné à Arduino, la nécessité ne me saute pas franchement aux yeux :P
Ça dépend du type de const - c'est vrai pour des petits types genre octets ou 16 bits sur un Arduino traditionnel
donc c'est bon si je laisse comme ça :
gsm.h
#ifndef _gsm_h_
#define _gsm_h_
#include <stdint.h> // http://www.cplusplus.com/reference/cstdint/
const uint8_t maxnumero2tel = 15;
#endif
global.h
#ifndef global_h_
#define global_h_
extern const uint8_t maxnumero2tel;
#endif
affichage.cpp
#include "global.h"
*****
strncpy(numero2tel, bufferTexte, maxnumero2tel);
Pourquoi deux .h ?
Pourquoi deux .h ?
eux ...
Il y a mon global.h qui contient toute mes variables en "extern" qui est appeler dans tout les .cpp nécessaire.
et il y a toute les "famile", gsm, wifi, affichage, etc qui on 1 header et 1 cpp.
OK
oui
ouf lol
J'ai peut de temps en se moment entre le taf (interdiction de circuler), mon déménagement qui approche (les carton et le déménagement de l'aqua) mais je bidouille quand même un peu.
La page de paramétrage est Ok donc les 2 plus grosse page (menu + paramètre)
J'ai jouté 2 pages, une pour modifier l'heure de l'horloge et 1 pour tout le paramétrage du brassage + oscillo)
donc sa avance doucement.
D'ailleurs vous pouvez peut être m'aider, j'ai déjà été a deux reprise voir dans des magasin pour trouver un bouquin simple et complet sur l'arduino/c++
a première vu sa ma paru très "pauvre" >>> exemple, l'arduino pour les nul
Ou alors c'était niveau bac ++
Une suggestion sur un ouvrage qui pourrait m'apprendre toute les "réglés" de base sans être nian nian ou hard ?
Bouns bonjour.
Je te rejoint en ce qui concerne les livres traitant de l'arduino et de la programmation, ne lisant pas l'anglais couramment surtout en termes techniques le choix en langue de notre beau pays est des plus restreint, pour la compréhension des écrits.
Sinon réfection faite, tu as raison mon idée de décante en plein milieu du bac bof, je voulais au départ faire deux univers bien distinct.
Courage pour toutes les aventures bénéfiques qui arrivent dans ta vie.
Pascal.
J'ai un probleme avec la sauvegarde dans l'eeprom
J'ai chercher a automatisé l'adresse de sauvegarde.
Apres qelque recherche j'ai fait
uint16_t adresse = 0;
const uint8_t savepuissanceMaxBrassage1 = adresse;
adresse += sizeof(puissanceMaxBrassage1);
const uint8_t savepuissanceMaxBrassage2 = adresse;
adresse += sizeof(puissanceMaxBrassage2);
const uint8_t savepuissanceMaxBrassage3 = adresse;
adresse += sizeof(puissanceMaxBrassage3);
const uint8_t saveaccalemieNocturne = adresse;
adresse += sizeof(accalmieNocturne);
const uint8_t saveleverSoleil = adresse;
adresse += sizeof(leverSoleil);
const uint8_t savecoucherSoleil = adresse;
adresse += sizeof(coucherSoleil);
const uint8_t savedureelevercoucher = adresse;
adresse += sizeof(dureelevercoucher);
const uint8_t savealertetemperaturebasse = adresse;
adresse += sizeof(alertetemperaturebasse);
const uint8_t savealertetemperaturehaute = adresse;
adresse += sizeof(alertetemperaturehaute);
const uint8_t saveventilationaquarium = adresse;
adresse += sizeof(ventilationaquarium);
const uint8_t saveventilationrampe = adresse;
adresse += sizeof(ventilationrampe);
const uint8_t saveheureNourrissage1 = adresse;
adresse += sizeof(heureNourrissage1);
const uint8_t saveheureNourrissage2 = adresse;
adresse += sizeof(heureNourrissage2);
const uint8_t savedureeNourissageMillis = adresse;
adresse += sizeof(dureeNourissageMillis);
const uint8_t saveconsignePhRac = adresse;
adresse += sizeof(consignePhRac);
const uint8_t savealertephbacbas = adresse;
adresse += sizeof(alertephbacbas);
const uint8_t savealertephbachaut = adresse;
adresse += sizeof(alertephbachaut);
const uint8_t savepuissanceMaxBlanc = adresse;
adresse += sizeof(puissanceMaxBlanc);
const uint8_t savepuissanceMaxBleu = adresse;
adresse += sizeof(puissanceMaxBleu);
const uint8_t saveoscillo1Angle1 = adresse;
adresse += sizeof(oscillo1Angle1);
const uint8_t saveoscillo2Angle1 = adresse;
adresse += sizeof(oscillo2Angle1);
const uint8_t saveoscillo3Angle1 = adresse;
adresse += sizeof(oscillo3Angle1);
const uint8_t saveoscillo1Angle2 = adresse;
adresse += sizeof(oscillo1Angle2);
const uint8_t saveoscillo2Angle2 = adresse;
adresse += sizeof(oscillo2Angle2);
const uint8_t saveoscillo3Angle2 = adresse;
adresse += sizeof(oscillo3Angle2);
const uint8_t savessid = adresse;
qui est mis au debut du setup
puis j'ai une fonction
void enregistrerBrassage() {
DPRINTLN("******************************************************************************************************");
DPRINTLN("*************************************** sauvegarde brassage ******************************************");
DPRINTLN("******************************************************************************************************");
valeurbrassage1.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage1 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(savepuissanceMaxBrassage1, puissanceMaxBrassage1); // sauvegarde dans l'eeprom la valeur
valeurbrassage2.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage2 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(savepuissanceMaxBrassage2, puissanceMaxBrassage2); // sauvegarde dans l'eeprom la valeur
valeurbrassage3.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage3 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(savepuissanceMaxBrassage3, puissanceMaxBrassage3); // sauvegarde dans l'eeprom la valeur
valeuroscillo1min.getValue(&number); // recupere la valeur dans nextion
oscillo1Angle1 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(saveoscillo1Angle1, oscillo1Angle1); // sauvegarde dans l'eeprom la valeur
valeuroscillo2min.getValue(&number); // recupere la valeur dans nextion
oscillo2Angle1 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(saveoscillo2Angle1, oscillo2Angle1); // sauvegarde dans l'eeprom la valeur
valeuroscillo3min.getValue(&number); // recupere la valeur dans nextion
oscillo3Angle1 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(saveoscillo3Angle1, oscillo3Angle1); // sauvegarde dans l'eeprom la valeur
valeuroscillo1max.getValue(&number); // recupere la valeur dans nextion
oscillo1Angle2 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(saveoscillo1Angle2, oscillo1Angle2); // sauvegarde dans l'eeprom la valeur
valeuroscillo2max.getValue(&number); // recupere la valeur dans nextion
oscillo2Angle2 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(saveoscillo2Angle2, oscillo2Angle2); // sauvegarde dans l'eeprom la valeur
valeuroscillo3max.getValue(&number); // recupere la valeur dans nextion
oscillo3Angle2 = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(saveoscillo3Angle2, oscillo3Angle2); // sauvegarde dans l'eeprom la valeur
valeurdureetempette.getValue(&number); // recupere la valeur dans nextion
dureeTempeteMillis = (number * minuteEnMillis); // attribut la valeur nextion dans l'arduino
EEPROM.put(savedureeTempeteMillis, dureeTempeteMillis); // sauvegarde dans l'eeprom la valeur
valeurtempette.getValue(&number); // recupere la valeur dans nextion
puissanceTempete = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(savepuissanceTempete, puissanceTempete); // sauvegarde dans l'eeprom la valeur
valeurmouvementoscillo.getValue(&number); // recupere la valeur dans nextion
delaisMouvementOscilloMillis = (number * secondeEnMillis); // attribut la valeur nextion dans l'arduino
EEPROM.put(savedelaisMouvementOscilloMillis, delaisMouvementOscilloMillis); // sauvegarde dans l'eeprom la valeur
valeuraccalemienocturne.getValue(&number); // recupere la valeur dans nextion
accalmieNocturne = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(saveaccalmieNocturne, accalmieNocturne); // sauvegarde dans l'eeprom la valeur
qui est dans un *cpp avec d'autre fonction exactement identique a celle ci (pour page parametre, wifi ...)
J'au une erreur de compile que je n'arrive pas a comprendre
C:\Users\djbouns\AppData\Local\Temp\ccZqpNL8.ltrans8.ltrans.o: In function `EERef::update(unsigned char)':
ccZqpNL8.ltrans8.o:(.text+0xb74): undefined reference to `savepuissanceMaxBrassage1'
ccZqpNL8.ltrans8.o:(.text+0xb92): undefined reference to `savepuissanceMaxBrassage2'
C:\Users\djbouns\AppData\Local\Temp\ccZqpNL8.ltrans8.ltrans.o: In function `enregistrerBrassage()':
ccZqpNL8.ltrans8.o:(.text+0xbb0): undefined reference to `savepuissanceMaxBrassage3'
ccZqpNL8.ltrans8.o:(.text+0xbce): undefined reference to `saveoscillo1Angle1'
ccZqpNL8.ltrans8.o:(.text+0xbec): undefined reference to `saveoscillo2Angle1'
ccZqpNL8.ltrans8.o:(.text+0xc0a): undefined reference to `saveoscillo3Angle1'
ccZqpNL8.ltrans8.o:(.text+0xc28): undefined reference to `saveoscillo1Angle2'
ccZqpNL8.ltrans8.o:(.text+0xc46): undefined reference to `saveoscillo2Angle2'
ccZqpNL8.ltrans8.o:(.text+0xc64): undefined reference to `saveoscillo3Angle2'
ccZqpNL8.ltrans8.o:(.text+0xca2): undefined reference to `savedureeTempeteMillis'
ccZqpNL8.ltrans8.o:(.text+0xce4): undefined reference to `savepuissanceTempete'
ccZqpNL8.ltrans8.o:(.text+0xd1c): undefined reference to `savedelaisMouvementOscilloMillis'
ccZqpNL8.ltrans8.o:(.text+0xd4e): undefined reference to `saveaccalmieNocturne'
C'est pas claire en plus :smiley-confuse:
Attention - toutes les adresses de vos saveXXX ne doivent pas être des uint8_t mais des uint16_t
qui est mis au debut du setup
Vous voulez dire que vos saveXXX ce ne sont pas des constantes globales? Si c'est DANS le code de setup() alors la portée de ces variables est limitée au setup, elles sont inconnues en dehors de la fonction. Ce devrait être des constantes globales (idéalement cette partie de variables de configuration devrait être directement dans une structure que vous écrivez ou lisez d'un coup depuis l'EEPROM - c'est bcp plus simple à gérer (cf quelque part dans notre longue discussion je vous avais montré comment faire avec le mot clé genre 0xDEADBEEF ou 0xBADCAFFE pour voir si l'EEPROM avait déjà été initialisée)
Attention - toutes les adresses de vos saveXXX ne doivent pas être des uint8_t mais des uint16_t
partant de zero, la liste de "save" doit actuellement arriver a ~100 d'ou le fait que j'ai mis uint8_t tout en sachant qu'il faudrait passer en uint16_t sur les save d'après.
tu remarqueras que j'ai bien mis
uint16_t adresse = 0;
Apres je supose que c'est pour "harmoniser" qu'il faut pas faire comme j'ai fait ?
Vous voulez dire que vos saveXXX ce ne sont pas des constantes globales? Si c'est DANS le code de setup() alors la portée de ces variables est limitée au setup, elles sont inconnues en dehors de la fonction.
Je n'avait pas reflechit que setup() fonctionnait comme void mafonction().
Les save* était dans le setup au moment de mon message.
Initialement je les avait mis dans mon autres.cpp dans le "rubrique" eeprom et j'avais une erreur, qui m'etait tout aussi incomprehensible puisque je declare bien adresse et il me dit 'adresse' does not name a type
autres.cpp:107: error: 'adresse' does not name a type
adresse += sizeof(puissanceMaxBrassage1);
^
autres.cpp:109: error: 'adresse' does not name a type
adresse += sizeof(puissanceMaxBrassage2);
^
autres.cpp:111: error: 'adresse' does not name a type
adresse += sizeof(puissanceMaxBrassage3);
^
autres.cpp:113: error: 'adresse' does not name a type
adresse += sizeof(accalmieNocturne);
^
autres.cpp:115: error: 'adresse' does not name a type
adresse += sizeof(leverSoleil);
^
autres.cpp:117: error: 'adresse' does not name a type
adresse += sizeof(coucherSoleil);
^
autres.cpp:119: error: 'adresse' does not name a type
adresse += sizeof(dureelevercoucher);
^
autres.cpp:121: error: 'adresse' does not name a type
adresse += sizeof(alertetemperaturebasse);
^
autres.cpp:123: error: 'adresse' does not name a type
adresse += sizeof(alertetemperaturehaute);
^
autres.cpp:125: error: 'adresse' does not name a type
adresse += sizeof(ventilationaquarium);
^
autres.cpp:127: error: 'adresse' does not name a type
adresse += sizeof(ventilationrampe);
^
autres.cpp:129: error: 'adresse' does not name a type
adresse += sizeof(heureNourrissage1);
^
autres.cpp:131: error: 'adresse' does not name a type
adresse += sizeof(heureNourrissage2);
^
autres.cpp:133: error: 'adresse' does not name a type
adresse += sizeof(dureeNourissageMillis);
^
autres.cpp:135: error: 'adresse' does not name a type
adresse += sizeof(consignePhRac);
^
autres.cpp:137: error: 'adresse' does not name a type
adresse += sizeof(alertephbacbas);
^
autres.cpp:139: error: 'adresse' does not name a type
adresse += sizeof(alertephbachaut);
^
autres.cpp:141: error: 'adresse' does not name a type
adresse += sizeof(puissanceMaxBlanc);
^
autres.cpp:143: error: 'adresse' does not name a type
adresse += sizeof(puissanceMaxBleu);
^
autres.cpp:145: error: 'adresse' does not name a type
adresse += sizeof(oscillo1Angle1);
^
autres.cpp:147: error: 'adresse' does not name a type
adresse += sizeof(oscillo2Angle1);
^
autres.cpp:149: error: 'adresse' does not name a type
adresse += sizeof(oscillo3Angle1);
^
autres.cpp:151: error: 'adresse' does not name a type
adresse += sizeof(oscillo1Angle2);
^
autres.cpp:153: error: 'adresse' does not name a type
adresse += sizeof(oscillo2Angle2);
^
autres.cpp:155: error: 'adresse' does not name a type
adresse += sizeof(oscillo3Angle2);
^
Plusieurs bibliothèque trouvées pour "DallasTemperature.h"
Utilisé : C:\Users\djbouns\Documents\Arduino\libraries\DallasTemperature
Non utilisé : C:\Program Files (x86)\Arduino\libraries\DallasTemperature
exit status 1
'adresse' does not name a type
Donc forcement j'ai chercher et peut etre fait pire en metant dans setup() :smiley-confuse:
Ce devrait être des constantes globales (idéalement cette partie de variables de configuration devrait être directement dans une structure que vous écrivez ou lisez d'un coup depuis l'EEPROM - c'est bcp plus simple à gérer
Je ne suis pas sur de comprendre ...
ecrire les adresses de stockage dans l'eeprom ?
(cf quelque part dans notre longue discussion je vous avais montré comment faire avec le mot clé genre 0xDEADBEEF ou 0xBADCAFFE pour voir si l'EEPROM avait déjà été initialisée)
je n'ai pas oublier ;) mais comme j'avais une erreur, je l'ai mis de coté le temps d'écrire se que je voulait et que sa fonctionne.
J'avais bien prévu de le remettre ensuite, jte jure lol
En pratique vous n'avez besoin de qu'une seule adresse - celle du début de la zone de stockage. Mettez tous les Paramètres dans une structure (et oui tout le code à modifier :) ) et vous sauver la structure d'un coup - c'est la fonction update qui se chargera de tout bien écrire où il faut - plus besoin de jongler avec une adresse par paramètre
Regardez mon petit projet de projet fini démo de gestion de Température - c'est comme ça que j'ai fait dans ce code si je me souviens bien
En pratique vous n'avez besoin de qu'une seule adresse - celle du début de la zone de stockage. Mettez tous les Paramètres dans une structure (et oui tout le code à modifier :) ) et vous sauver la structure d'un coup - c'est la fonction update qui se chargera de tout bien écrire où il faut - plus besoin de jongler avec une adresse par paramètre
Regardez mon petit projet de projet fini démo de gestion de Température - c'est comme ça que j'ai fait dans ce code si je me souviens bien
C'est ca : ?
const uint32_t motClef = 0xBEEFDEAD;
const uint16_t adresseMotClef = 0x00;
// __attribute__ ((packed)) permet de forcer la conservation de l'ordre des champs
// c'est utile pour éviter que le compilateur mette le bazar quand on en rajoute au fil du temps
struct __attribute__ ((packed)) _paramS {
int16_t tempConsigne;
int16_t humConsigne;
int16_t ventConsigne;
byte vitesseVentilateur; // PWM pour le ventilateur
} lesParametres;
const uint16_t adresseDesParametres = adresseMotClef + sizeof(motClef);
Si oui,
si je comprend bien,
on liste les paramètre a sauvegarder (pour moi les save***)
un petit coup de EEPROM.put(adresseDesParametres, lesParametres);
et ...
c'est tout ?
oui
bien sûr après partout où vous voulez utilisez les paramètres il faut changer le nom de la variable pour y accéder par la structure
il y a la fonction suivante qui est appelée au démarrage dans le setup()// **********************************************************
void etablirValeursParDefaut()
{
uint32_t lectureMotClef;
EEPROM.get(adresseMotClef, lectureMotClef);
if (lectureMotClef == motClef) {
// la mémoire a déjà été initialisée, on peut charger les paramètres
EEPROM.get(adresseDesParametres, lesParametres);
} else {
// la mémoire n'avait pas encore été initialisée
lesParametres.tempConsigne = 20;
lesParametres.humConsigne = 50;
lesParametres.ventConsigne = 2; // entre 0 (ventilateur vitess min) et 5 PWM ventilateur à 0xFF)
lesParametres.vitesseVentilateur = lesParametres.ventConsigne * 51;
EEPROM.put(adresseDesParametres, lesParametres);
EEPROM.put(adresseMotClef, motClef);
}
et quand je veux sauvegarder les paramètres (après une modif) il suffit de faireEEPROM.put(adresseDesParametres, lesParametres);
je suis presque au bout (de la modif :) )
j'ai fait
struct __attribute__ ((packed)) _paramS {
uint8_t puissanceMaxBrassage1;
uint8_t puissanceMaxBrassage2;
uint8_t puissanceMaxBrassage3;
uint8_t oscillo1Angle1 = number;
uint8_t oscillo2Angle1 = number;
uint8_t oscillo3Angle1 = number;
uint8_t oscillo1Angle2 = number;
uint8_t oscillo2Angle2 = number;
uint8_t oscillo3Angle2 = number;
uint32_t dureeTempeteMillis = (number * minuteEnMillis);
uint8_t puissanceTempete = number;
uint16_t delaisMouvementOscilloMillis = (number * secondeEnMillis);
uint8_t accalmieNocturne = number;
} sauvergarde;
et
void enregistrerBrassage() {
DPRINTLN("******************************************************************************************************");
DPRINTLN("*************************************** sauvegarde brassage ******************************************");
DPRINTLN("******************************************************************************************************");
valeurbrassage1.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage1 = number; // attribut la valeur nextion dans l'arduino
valeurbrassage2.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage2 = number; // attribut la valeur nextion dans l'arduino
valeurbrassage3.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage3 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo1min.getValue(&number); // recupere la valeur dans nextion
oscillo1Angle1 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo2min.getValue(&number); // recupere la valeur dans nextion
oscillo2Angle1 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo3min.getValue(&number); // recupere la valeur dans nextion
oscillo3Angle1 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo1max.getValue(&number); // recupere la valeur dans nextion
oscillo1Angle2 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo2max.getValue(&number); // recupere la valeur dans nextion
oscillo2Angle2 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo3max.getValue(&number); // recupere la valeur dans nextion
oscillo3Angle2 = number; // attribut la valeur nextion dans l'arduino
valeurdureetempette.getValue(&number); // recupere la valeur dans nextion
dureeTempeteMillis = (number * minuteEnMillis); // attribut la valeur nextion dans l'arduino
valeurtempette.getValue(&number); // recupere la valeur dans nextion
puissanceTempete = number; // attribut la valeur nextion dans l'arduino
valeurmouvementoscillo.getValue(&number); // recupere la valeur dans nextion
delaisMouvementOscilloMillis = (number * secondeEnMillis); // attribut la valeur nextion dans l'arduino
valeuraccalemienocturne.getValue(&number); // recupere la valeur dans nextion
accalmieNocturne = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(adresseDeSauvegarde, sauvergarde);
versConfig();
}
et comme d'ab, j'ai une erreur mais la ... je comprend pas le problème de parenthèse.
sketch\affichage.cpp: In function 'void enregistrerBrassage()':
affichage.cpp:946: error: expected primary-expression before ')' token
EEPROM.put(adresseDeSauvegarde, sauvergarde);
^
Plusieurs bibliothèque trouvées pour "DallasTemperature.h"
Utilisé : C:\Users\djbouns\Documents\Arduino\libraries\DallasTemperature
Non utilisé : C:\Program Files (x86)\Arduino\libraries\DallasTemperature
exit status 1
expected primary-expression before ')' token
Si je fait EEPROM.put(adresseDeSauvegarde, 123); ca fonctionne
Le problème est quand il y a sauvegarde.
J'ai bien verifier avec le code source (lcd rtc temp hum vent) et c'est identique
arfff ... :smiley-sweat:
dans la définition de la struct il ne faut pas mettre de valeurs --> virer tous les = number
struct __attribute__ ((packed)) _paramS {
uint8_t puissanceMaxBrassage1;
uint8_t puissanceMaxBrassage2;
uint8_t puissanceMaxBrassage3;
uint8_t oscillo1Angle1;
uint8_t oscillo2Angle1;
uint8_t oscillo3Angle1;
uint8_t oscillo1Angle2;
uint8_t oscillo2Angle2;
uint8_t oscillo3Angle2;
uint32_t dureeTempeteMillis;
uint8_t puissanceTempete;
uint16_t delaisMouvementOscilloMillis;
uint8_t accalmieNocturne;
} sauvergarde;
dans la fonction qui va au début du sketch regarder si l'EEPROM est déjà initialisée ou pas:
- si déjà initialisée alors juste charger sauvergarde avec le contenu de l'EEPROM
- si pas initialisée, alors mettre des valeurs par défaut dans sauvergarde et l'écrire en EEPROM avec le mot magique pour la prochaine fois
sinon vous n'avez pas d'autres erreurs avant?
o ....
Laisse tombé ...
Jsuis le nez dessus des heure et jvois rien, sa me dégoute.
heureusement tu vois tout :)
c'est le soucis souvent - c'est plus simple pour les autres de repérer des erreurs...
Quand on galère comme ça, le mieux c'est d'aller prendre l'air un peu et de revenir avec un regard frais sur le problème.
C'est pas un temps a prendre l'air, je vais etre malade :o :smiley-mr-green: :) :) :)
Tu a du remarquer que souvent je post et je supprime quelque heure après pars-que j'ai trouvé tout seul ...
Si sa pouvait etre tout le temps comme sa :)
Bientôt ... lol
C'est pas un temps a prendre l'air, je vais etre malade :o :smiley-mr-green: :) :) :)
:)
bon ben faut s'occuper de nettoyer l'aquarium alors ou faire le ménage ou un bon repas ! votre femme sera contente :)
:)
bon ben faut s'occuper de nettoyer l'aquarium alors ou faire le ménage ou un bon repas ! votre femme sera contente :)
oula ... c'est demain de st valentin ... faut pas deconner lol lol lol
sinon vous n'avez pas d'autres erreurs avant?
j'ai bien viré tout les "="
L'erreur est la meme :smiley-confuse:
Et non, pas d'erreur avant, et pas d'erreur si je retire EEPROM.put(adresseDeSauvegarde, sauvergarde);
Voici toute l'erreur quand je met EEPROM.put(adresseDeSauvegarde, sauvergarde);
sketch\affichage.cpp: In function 'void enregistrerBrassage()':
affichage.cpp:946: error: expected primary-expression before ')' token
EEPROM.put(adresseDeSauvegarde, sauvergarde);
exit status 1
expected primary-expression before ')' token
postez le code pour voir comment tout cela est défini...
postez le code pour voir comment tout cela est défini...
manquent les fichier NexVariable.h etc - donc je ne peux pas tester la compilation
dans la fonction etablirValeursParDefaut() il faut initialiser toutes les valeurs de la structure à une valeur par défaut correcte
void etablirValeursParDefaut() {
uint32_t lectureMotClef;
EEPROM.get(adresseMotClef, lectureMotClef);
if (lectureMotClef == motClef) {
// la mémoire a déjà été initialisée, on peut charger les paramètres
EEPROM.get(adresseDeSauvegarde, sauvergarde);
}
else {
// la mémoire n'avait pas encore été initialisée
sauvergarde.puissanceMaxBrassage1; // CA FAIT QUOIT CA?? FAUT INITIALISER LES VALEURS
sauvergarde.puissanceMaxBrassage2; // CA FAIT QUOIT CA?? FAUT INITIALISER LES VALEURS
sauvergarde.puissanceMaxBrassage3; // CA FAIT QUOIT CA?? FAUT INITIALISER LES VALEURS
EEPROM.put(adresseDeSauvegarde, sauvergarde);
EEPROM.put(adresseMotClef, motClef);
}
}
la biblioteque est trop grosse mais elle est dispo ICI (https://github.com/itead/ITEADLIB_Arduino_Nextion)
Pour l'initialisation, j'ai fais comme cela car les valeur par Default son déclaré avec la variable et son appelé avant le setup, exemple : (dans brassage.cpp)
uint8_t puissanceMaxBrassage1 = 100 ; // puissance jebao rw 15 en %
uint8_t puissanceMaxBrassage2 = 80 ; // puissance jebao rw 8 en %
uint8_t puissanceMaxBrassage3 = 40 ; // puissance jebao rw 8 en %
donc au moment de void etablirValeursParDefaut(), puissanceMaxBrassage* a déjà une valeur.
mon point c'est que ce code ne fait rien du tout sauvergarde.puissanceMaxBrassage1;
sauvergarde.puissanceMaxBrassage2;
sauvergarde.puissanceMaxBrassage3;
ce ne serait pas plutôt sauvergarde.puissanceMaxBrassage1 = puissanceMaxBrassage1;
sauvergarde.puissanceMaxBrassage2 = puissanceMaxBrassage2;
sauvergarde.puissanceMaxBrassage3 = puissanceMaxBrassage3;
que vous vouliez écrire? et quid des autres valeurs? la structure n'est pas initialisée?
j'arrive à compiler sans erreurs par contre il y a des dizaines de Warnings...
vous devriez activer les "compilation Warnings" au moins au niveau "more" et régler chacun des soucis mentionné. le programme doit compiler sans warnings.
cela dit j'ai ce message:
Sketch uses 54678 bytes (21%) of program storage space. Maximum is 253952 bytes.
Global variables use 7146 bytes (87%) of dynamic memory, leaving 1046 bytes for local variables. Maximum is 8192 bytes.
Low memory available, stability problems may occur.
donc faut faire attention à cette partie et vous assurer que tout vos print de debug soient en F("xxx") par exemple histoire de libérer de la SRAM (vu que vous avez encore plein de place en flash)
qu'est-ce que vous avez comme message à la compilation ??
j'arrive à compiler sans erreurs par contre il y a des dizaines de Warnings...
Je vais me faire engueuler ...Pour continuer a bosser j'ai commenter la ligne qui pose probleme.
elle est dans affichage.cpp, dans void enregistrerBrassage() {, c'est : EEPROM.put(adresseDeSauvegarde, sauvergarde);
Des qu'il y a cette ligne qui doit sauvegarder les paramettre j'ai cette erreur :
sketch\affichage.cpp: In function 'void enregistrerBrassage()':
affichage.cpp:946: error: expected primary-expression before ')' token
EEPROM.put(adresseDeSauvegarde, sauvergarde);
exit status 1
expected primary-expression before ')' token
vous devriez activer les "compilation Warnings" au moins au niveau "more" et régler chacun des soucis mentionné. le programme doit compiler sans warnings.
Je pense avoir trouver comment l'activer et ce n'est pas jolie jolie ...
Je peut meme pas le poster, y fait plus de 9000 lignes :o
cela dit j'ai ce message:
Sketch uses 54678 bytes (21%) of program storage space. Maximum is 253952 bytes.
Global variables use 7146 bytes (87%) of dynamic memory, leaving 1046 bytes for local variables. Maximum is 8192 bytes.
Low memory available, stability problems may occur.
donc faut faire attention à cette partie et vous assurer que tout vos print de debug soient en F("xxx") par exemple histoire de libérer de la SRAM (vu que vous avez encore plein de place en flash)
qu'est-ce que vous avez comme message à la compilation ??
Ok pour le serial.print(F
Mais le serial.println(F n'existe pas et vu que c'est pas commenté, je ne sait trop a quoi cela correspond
#define DEBUG // à commenter pour enleverSoleil les traces
#ifdef DEBUG
#define DPRINTF(s) {Serial.print(F(s));}
#define DPRINTFV(s,v) {Serial.print(F(s)); Serial.print(v);}
#define DPRINTFX(s,v) {Serial.print(F(s)); Serial.print(F("0x")); Serial.print(v, HEX);}
#define DPRINT(x) {Serial.print(x);}
#define DPRINTLN(x) {Serial.println(x);}
#else
#define DPRINTF(s)
#define DPRINTFV(s,v)
#define DPRINTFX(s,v)
#define DPRINT(x)
#define DPRINTLN(x)
#endif
Pour le bug
changez autres.h comme cela#ifndef _autres_h_
#define _autres_h_
#include <stdint.h> // http://www.cplusplus.com/reference/cstdint/
const uint32_t motClef = 0xBEEFDEAD;
const int16_t adresseMotClef = 0x00;
const int16_t adresseDeSauvegarde = adresseMotClef + sizeof(motClef);
// __attribute__ ((packed)) permet de forcer la conservation de l'ordre des champs
// c'est utile pour éviter que le compilateur mette le bazar quand on en rajoute au fil du temps
struct __attribute__ ((packed)) paramS_t {
uint8_t puissanceMaxBrassage1;
uint8_t puissanceMaxBrassage2;
uint8_t puissanceMaxBrassage3;
uint8_t oscillo1Angle1;
uint8_t oscillo2Angle1;
uint8_t oscillo3Angle1;
uint8_t oscillo1Angle2;
uint8_t oscillo2Angle2;
uint8_t oscillo3Angle2;
uint32_t dureeTempeteMillis;
uint8_t puissanceTempete;
uint16_t delaisMouvementOscilloMillis;
uint8_t accalmieNocturne;
};
extern paramS_t sauvergarde;
void nourissage();
void ph();
void graphique();
void testTempsBoucle();
void etablirValeursParDefaut();
#endif
et dans autres.cpp remplacez la partie EEPROM par// *******************************************************************************************************************************************
// **************************************** eeprom
// *******************************************************************************************************************************************
paramS_t sauvergarde;
void etablirValeursParDefaut() {
uint32_t lectureMotClef;
EEPROM.get(adresseMotClef, lectureMotClef);
if (lectureMotClef == motClef) {
// la mémoire a déjà été initialisée, on peut charger les paramètres
EEPROM.get(adresseDeSauvegarde, sauvergarde);
}
else {
// la mémoire n'avait pas encore été initialisée
sauvergarde.puissanceMaxBrassage1;
sauvergarde.puissanceMaxBrassage2;
sauvergarde.puissanceMaxBrassage3;
EEPROM.put(adresseDeSauvegarde, sauvergarde);
EEPROM.put(adresseMotClef, motClef);
}
}
Notez que votre fonction enregistrerBrassage()
void enregistrerBrassage() {
DPRINTLN("******************************************************************************************************");
DPRINTLN("*************************************** sauvegarde brassage ******************************************");
DPRINTLN("******************************************************************************************************");
valeurbrassage1.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage1 = number; // attribut la valeur nextion dans l'arduino
valeurbrassage2.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage2 = number; // attribut la valeur nextion dans l'arduino
valeurbrassage3.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage3 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo1min.getValue(&number); // recupere la valeur dans nextion
oscillo1Angle1 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo2min.getValue(&number); // recupere la valeur dans nextion
oscillo2Angle1 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo3min.getValue(&number); // recupere la valeur dans nextion
oscillo3Angle1 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo1max.getValue(&number); // recupere la valeur dans nextion
oscillo1Angle2 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo2max.getValue(&number); // recupere la valeur dans nextion
oscillo2Angle2 = number; // attribut la valeur nextion dans l'arduino
valeuroscillo3max.getValue(&number); // recupere la valeur dans nextion
oscillo3Angle2 = number; // attribut la valeur nextion dans l'arduino
valeurdureetempette.getValue(&number); // recupere la valeur dans nextion
dureeTempeteMillis = (number * minuteEnMillis); // attribut la valeur nextion dans l'arduino
valeurtempette.getValue(&number); // recupere la valeur dans nextion
puissanceTempete = number; // attribut la valeur nextion dans l'arduino
valeurmouvementoscillo.getValue(&number); // recupere la valeur dans nextion
delaisMouvementOscilloMillis = (number * secondeEnMillis); // attribut la valeur nextion dans l'arduino
valeuraccalemienocturne.getValue(&number); // recupere la valeur dans nextion
accalmieNocturne = number; // attribut la valeur nextion dans l'arduino
EEPROM.put(adresseDeSauvegarde, sauvergarde);
versConfig();
}
lit bien les data affichées sur le nexion mais ne les stockent pas dans sauvergarde.
Yes, merci
Notez que votre fonction enregistrerBrassage()lis bien les data affichées sur le nexion mais ne les stockent pas dans sauvergarde.
Je ne comprend pas ...
Les paramètre son lu dans le nextion puis associé a la variable correspondant dans l'arduino.
a la fin de cela, il y a EEPROM.put(adresseDeSauvegarde, sauvergarde);
qui enregistre toute les variable de la liste struct __attribute__ ((packed)) paramS_t {
non ?
Yes, merci
Je ne comprend pas ...
Les paramètre son lu dans le nextion puis associé a la variable correspondant dans l'arduino.
a la fin de cela, il y a EEPROM.put(adresseDeSauvegarde, sauvergarde);
qui enregistre toute les variable de la liste struct __attribute__ ((packed)) paramS_t {
non ?
Oui mais il faut que vos variables dans sauvegarde soit mis à jour avec les nouvelles valeurs - en pratique vous ne devriez plus avoir nulle part de variable Arduino à sauver qui ne soit pas membre de sauvegarde. Partout vous devriez avoir sauvegarde.XXX et pas juste XXX. Il faut tout remplacer partout.
:smiley-eek-blue: :'(
et oui ... c'est ce que je disais en #571 (http://forum.arduino.cc/index.php?topic=504431.msg3603814#msg3603814)....
En pratique vous n'avez besoin de qu'une seule adresse - celle du début de la zone de stockage. Mettez tous les Paramètres dans une structure (et oui tout le code à modifier :) ) et vous sauver la structure d'un coup - c'est la fonction update qui se chargera de tout bien écrire où il faut - plus besoin de jongler avec une adresse par paramètre
et oui ... c'est ce que je disais en #571 (http://forum.arduino.cc/index.php?topic=504431.msg3603814#msg3603814)....
Faut le temps je comprenne lol
bon ba y a plus qu'as ...
oui :)
donc plus de puissanceMaxBrassage1 nulle part dans le programme, ça doit être remplacé par sauvegarde.puissanceMaxBrassage1
vous pouvez aussi simplifier - au lieu d'écrirevaleurbrassage1.getValue(&number); // recupere la valeur dans nextion
puissanceMaxBrassage1 = number; // attribut la valeur nextion dans l'arduino
vous pouvez mettre la valeur d'un coup au bon endroit en mémoire valeurbrassage1.getValue(&sauvegarde.puissanceMaxBrassage1); // recupere la valeur dans nextion
J'ai remplacer une premier variable puissanceMaxBrassage1 par sauvegarde.puissanceMaxBrassage1
J'ai des erreur de compile comme quoi sauvegarde n'est pas déclaré.
Cette "écriture" pour la sauvegarde devient compliquer alors que je ne voulait juste simplifier l'indexation
(sans parler que j'ai pas compris le fonctionnement)
il doit sans doute y avoir plus simple sans tout avoir a modifier non ?
En plus, si j'ai une méga liste de warning, il faudrait peut être mieux la résorber avec de continuer ?
j'ai passer une bonne partie de la journee a resoudre tout ces Warning.
Je suis content d'en avoir réparé une majorité sans vous embêter :)
Il en reste un peu mais je ne suis pas sur qu'elle vienne de moi.
problème dans la biblioteque EEPROM ?
In file included from C:\Users\djbouns\Documents\AQUABOUNS_V2_gsm\AQUABOUNS_V2_gsm.ino:23:0:
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src/EEPROM.h:43:30: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
operator const uint8_t() const { return **this; }
^
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src/EEPROM.h:92:26: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
operator const int() const { return index; }
^
In file included from C:\Users\djbouns\Documents\AQUABOUNS_V2_gsm\AQUABOUNS_V2_gsm.ino:23:0:
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src/EEPROM.h:145:20: warning: 'EEPROM' defined but not used [-Wunused-variable]
static EEPROMClass EEPROM;
^
In file included from sketch\affichage.cpp:1:0:
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src/EEPROM.h:43:30: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
operator const uint8_t() const { return **this; }
^
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src/EEPROM.h:92:26: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
operator const int() const { return index; }
^
In file included from sketch\affichage.cpp:1:0:
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM\src/EEPROM.h:145:20: warning: 'EEPROM' defined but not used [-Wunused-variable]
static EEPROMClass EEPROM;
^
Le croquis utilise 54826 octets (21%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 7050 octets (86%) de mémoire dynamique, ce qui laisse 1142 octets pour les variables locales. Le maximum est de 8192 octets.
La mémoire disponible faible, des problèmes de stabilité pourraient survenir.
j'ai mis les serial.print en Flash
j'arrive a :
Le croquis utilise 59586 octets (23%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 5311 octets (64%) de mémoire dynamique, ce qui laisse 2881 octets pour les variables locales. Le maximum est de 8192 octets.
et quand je retire le debug je suis a :
Le croquis utilise 52066 octets (20%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 5009 octets (61%) de mémoire dynamique, ce qui laisse 3183 octets pour les variables locales. Le maximum est de 8192 octets.
J'ai pas mal chercher sur le net pour l'eeprom et les seul exemple d'indexation "propre" que j'ai trouvé son identique a ton exemple JM donc va falloir vraiment que je revois tout ... une fois de plus :(
Boun's et tu as même pas eu un bon point de plus pour tout le boulot accomplis ces derniers jours.
C'est pas juste.
J-M-L fais un tit geste.
Courage Boun's.
Pascal.
j'ai tout modifier :smiley-sweat: :smiley-sleep:
J'ai donc dans "eeprom.h"
#ifndef _eeprom_h_
#define _eeprom_h_
#include <stdint.h> // http://www.cplusplus.com/reference/cstdint/
const uint32_t motClef = 0xBEEFDEAD;
const uint16_t adresseMotClef = 0x00;
const uint16_t adresseDeSauvegarde = adresseMotClef + sizeof(motClef);
// __attribute__ ((packed)) permet de forcer la conservation de l'ordre des champs
// c'est utile pour éviter que le compilateur mette le bazar quand on en rajoute au fil du temps
struct __attribute__ ((packed)) paramS_t {
uint8_t puissanceMaxBrassage1;
uint8_t puissanceMaxBrassage2;
uint8_t puissanceMaxBrassage3;
uint8_t accalmieNocturne;
uint8_t puissanceMaxBlanc;
uint8_t puissanceMaxBleu;
uint32_t leverSoleil;
uint32_t coucherSoleil;
uint16_t dureelevercoucher;
uint8_t oscillo1Angle1;
uint8_t oscillo1Angle2;
uint8_t oscillo2Angle1;
uint8_t oscillo2Angle2;
uint8_t oscillo3Angle1;
uint8_t oscillo3Angle2;
uint16_t delaisMouvementOscilloMillis;
uint16_t dureeOsmolationMillis ;
uint8_t compteurOsmolationMax;
float ventilationaquarium;
float alertetemperaturehaute;
float alertetemperaturebasse;
uint8_t ventilationrampe;
boolean adresseSondeRampe;
boolean adresseSondeBac;
uint32_t dureeTempeteMillis;
uint8_t puissanceTempete;
boolean tempeteAleatoireOn;
boolean tempeteDuJour;
uint32_t dureeNourissageMillis;
uint32_t heureNourrissage1;
uint32_t heureNourrissage2;
boolean nourrissage1ON;
boolean nourrissage2ON;
float consignePhRac;
float alertephbacbas;
float alertephbachaut;
};
extern paramS_t sauvergarde;
#endif
Puis dans eeprom.cpp
#include "eeprom.h"
#include <EEPROM.h>
// *******************************************************************************************************************************************
// **************************************** eeprom
// *******************************************************************************************************************************************
paramS_t sauvergarde;
void etablirValeursParDefaut() {
uint32_t lectureMotClef;
EEPROM.get(adresseMotClef, lectureMotClef);
if (lectureMotClef == motClef) {
// la mémoire a déjà été initialisée, on peut charger les paramètres
EEPROM.get(adresseDeSauvegarde, sauvergarde);
}
else {
// la mémoire n'avait pas encore été initialisée
sauvegarde.puissanceMaxBrassage1 = 50 ; // puissance jebao rw 15 en %
sauvegarde.puissanceMaxBrassage2 = 50 ; // puissance jebao rw 8 en %
sauvegarde.puissanceMaxBrassage3 = 50 ; // puissance jebao rw 8 en %
sauvegarde.accalmieNocturne = 50; // pourcentage de reduction du brassage la nuit
sauvegarde.puissanceMaxBlanc = 50 ; // puissance blanc en %
sauvegarde.puissanceMaxBleu = 50 ; // puissance bleu en %
sauvegarde.leverSoleil = 36000ul; // heure debut sauvegarde.leverSoleil (h*3600+m*60+s)
sauvegarde.coucherSoleil = 72000ul; // heure fin couché (h*3600+m*60+s)
sauvegarde.dureelevercoucher = 3600u; // temps de levé (h*3600+m*60+s)
sauvegarde.oscillo1Angle1 = 80 ; // angle oscillo entre 0 et 180
sauvegarde.oscillo1Angle2 = 100 ; // angle oscillo entre 0 et 180
sauvegarde.oscillo2Angle1 = 80 ; // angle oscillo entre 0 et 180
sauvegarde.oscillo2Angle2 = 100 ; // angle oscillo entre 0 et 180
sauvegarde.oscillo3Angle1 = 80 ; // angle oscillo entre 0 et 180
sauvegarde.oscillo3Angle2 = 100 ; // angle oscillo entre 0 et 180
sauvegarde.delaisMouvementOscilloMillis = 3000u ; // delais mouvement oscillo en millis
sauvegarde.dureeOsmolationMillis = 5000u; //durée de l'osmolation en millis
sauvegarde.compteurOsmolationMax = 5; // nombre de declanchement de l'osmolation avant alarme
sauvegarde.ventilationaquarium = 27; // temperature de declanchement de la ventilation du bac
sauvegarde.alertetemperaturehaute = 27.5; // temperature de declanchement de la ventilation du bac
sauvegarde.alertetemperaturebasse = 24.8; // temperature de declanchement de la ventilation du bac
sauvegarde.ventilationrampe = 35; // temperature de declanchement de la ventilation de la rampe
sauvegarde.adresseSondeRampe = 0; // adresse de la sonde de temperature de la rampe
sauvegarde.adresseSondeBac = 1; // adresse de la sonde de temperature du bac
sauvegarde.dureeTempeteMillis = 2000u; // durée tempête millis
sauvegarde.puissanceTempete = 75; // puissance de toutes les pompe jebao en mode tempête en %
sauvegarde.tempeteAleatoireOn = false; // on/off tempette aleatoire
sauvegarde.tempeteDuJour = false; // on/off tempette quotidienne
sauvegarde.dureeNourissageMillis = 300000ul; // durée du nourissage en millis
sauvegarde.heureNourrissage1 = 36000ul; // heure du nourrissage auto 1 (h*3600+m*60+s)
sauvegarde.heureNourrissage2 = 72000ul; // heure du nourissage auto 2 (h*3600+m*60+s)
sauvegarde.nourrissage1ON = true; // on/off nourissage 1
sauvegarde.nourrissage2ON = false;// on/off nourissage 2
sauvegarde.consignePhRac = 6.66; // consigne du ph du RAC
sauvegarde.alertephbacbas = 7.77; // limite PH bas du bac pour alerte
sauvegarde.alertephbachaut = 8.88;; // limite ph haut du bac pour alerte
EEPROM.put(adresseDeSauvegarde, sauvergarde);
EEPROM.put(adresseMotClef, motClef);
}
}
et j'ai modifier toute mes variables a sauvegarder en "sauvegarde.****"
dans mon global.h je les ai donc en
extern uint8_t sauvegarde.oscillo1Angle1;
et j'ai cette erreur
global.h:126: error: expected initializer before '.' token
extern uint8_t sauvegarde.oscillo1Angle1;
donc je me suis dit, sauvegarde doit etre devenu un type donc j'ai fait
extern sauvegarde.oscillo1Angle1;
et j'ai aussi une erreur
exit status 1
'sauvegarde' does not name a type
donc je me retrouve coincé ... encore :(
j'ai plus la niak a force ...
On arrive au bout du 9e mois :smiley-sad-blue:
Suis en déplacement toute la semaine juste sur mon tel et pas bcp de temps avant le week end
Bravo pour le boulot - pour le
Coup du extern il faut définir le type (la structure) dans un .h et en externe dans le .h la variable et importer le .h - c'est ce que j'avais proposé comme modif plus haut
Yes :smiley-grin:
a demain :smiley-sleep: :smiley-razz:
en attendant le week-end pour le boulot fourni :)
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
(https://www.theshulergroupllc.com/wp-content/uploads/2017/05/Yeses.jpg)
J'ai travailler aujourd'hui sur le démarrage de l'automate.
Il me paraissait important qu'au démarrage l'on sache si tout est OK coté périphérique.
Donc plutôt que d'avoir tout dans le Moniteur série, pourquoi ne pas l'avoir directement sur l'écran :)
Ça donne ça (https://youtu.be/qQ1_k_XEz0U)
(le modul gsm et sd ne sont pas branché actuellement :) )
n'hésiter pas a donner vos avis :)
la partie eeprom me fou un sacré bazars ...
Je pense avoir compris pourquoi
Mon fichier est comme ca:
#include "WiFiEsp.h"
#include <Wire.h>
#include <DallasTemperature.h>
#include <OneWire.h>
#include "SdFat.h"
#include "Nextion.h"
#include "RTClib.h"
#include <Servo.h>
#include <EEPROM.h>
#include "pin.h"
// **********************************************************************************************************************************************************************************************
// Declarations des fonctions
// ---------------------------
#include "eeprom.h"
#include "eclairage.h"
#include "autres.h"
#include "gsm.h"
#include "affichage.h"
#include "brassage.h"
#include "horloge.h"
#include "oscillo.h"
#include "osmolation.h"
#include "Sd.h"
#include "temperature.h"
#include "tempete.h"
#include "global.h"
#include "debug.h"
// **********************************************************************************************************************************************************************************************
// Setup
// ---------------------------
void setup() {
etablirValeursParDefaut();
Les valeurs de l'epprom son donc lu des l'entree dans le setup, mais, certain calcule present dans mes fixhier *.h utilise les valeur de l'eeprom.
exemple :
uint32_t finleverSoleilBleu1 = (sauvegarde.leverSoleil + sauvegarde.dureelevercoucher);
Je me retrouve donc avec finleverSoleilbleu1 = 0 :(
A part tout déplacer juste après la lecture de l'eeprom, je vois pas comment faire autrement.
Je commençais a être inquiet de plus voir professeur jean marc
Mais il est bien la lol
Msieu Msieu
Vous pouvez me dire si je doit tout déplacer juste après la lecture de l'eeprom ou si y a une autre solution ?
(je me suis pas reposer en attendant, j'ai quasi finaliser la partie "graphique sur 24h" c'etait pas une mince affaire :) )
Ben oui... faut déplacer après la lecture des valeurs... :)
L'autre option étant d'utiliser des #define mais comme ça c'est recalculé à chaque fois..
#define finleverSoleilBleu1 (sauvegarde.leverSoleil + sauvegarde.dureelevercoucher)
:smiley-confuse:
Entre changer tout les nom puis tout déplacer ...
On va y arrivé :smiley-cool:
Disons que dès que vous avez décidé que ce n'étaient plus des constantes, il n'y a pas de magie, il faut les mettre à jour quand une des valeurs change... le plus simple effectivement est d'avoir tout au même endroit dans le code:
- établissement des valeurs au démarrage qui sont soit des valeurs par défaut si l'EEPROM n'est pas initialisée, soit lecture de l'EEPROM et calcul des valeurs dérivées
- à chaque modification d'une Valeur, sauvegardé en EEPROM et recalculer des valeurs dérivées
ça ne fait que deux fonctions réellement écrire été si dès le début vous avez pris l'approche de la structure pour les paramètres alors le code ne change pas si d'aventure vous devez rajouter des nouveaux param, suffit d'etendre La structure
C'est bon, j'ai donc ton déplacé dans une fonction majValeursParDefaut() qui met a jour toute les variables calculé a partir des sauvegarde.***
Je continu sur la partie graphique
Comme je vous ai dit, le graph 24h est OK
Voir ici le rendu (https://www.youtube.com/watch?v=c8OHEYpF-_Y&feature=youtu.be), la plus grosse galère a été de faire en sorte que la partie droite du graph soit l'heure du dernier relevé et que le graph parte de la en affichant les bonne heure en dessous (au départ le graph allait de 0h a 24h)
A pressent j'attaque la partie graph sur 7 jours, au départ je voulait stocker les mesure dans une variable par heure donc 168 variables*2(température + ph)soit 1344 octets et 2 problèmes :
1) le fait qu'en cas de redémarrage / coupure de courant toutes les mesure était perdu
2) l'optimisation du code
J'ai donc pensé a mettre tout sa sur SD.
Si je met en place ce que je sait déjà faire, sa va être 1 mesure = 1 fichier, un peut cochon tout ça.
>mettre dans un .csv, et la, je doit encore tout apprendre. j'ai trouvé diffèrent poste qui explique comment écrire dans un .csv mais aucun pour récupérer une donné dans un emplacement précis.
Si vous savez ou je peut trouver un tuto pour faire ca :)
A moins qu'il y est une autre solution que le .csv?
Boun's bonjour.
Super boulot que tu as accomplis et très bel écran, en plus deux bon point de plus, tu approche de mes plus vifs applaudissements.
Encore une fois BRAVO.
Pascal.
C'est bon, j'ai donc ton déplacé dans une fonction majValeursParDefaut() qui met a jour toute les variables calculé a partir des sauvegarde.***
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
Je continue sur la partie graphique
Comme je vous ai dit, le graphe 24h est OK
Voir ici le rendu (https://www.youtube.com/watch?v=c8OHEYpF-_Y&feature=youtu.be), la plus grosse galère a été de faire en sorte que la partie droite du graphe soit l'heure du dernier relevé et que le graphe parte de là en affichant les bonnes heures en dessous (au départ le graphe allait de 0h a 24h)
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
A présent j'attaque la partie graphe sur 7 jours, au départ je voulais stocker les mesures dans une variable par heure donc 168 variables*2(température + ph)soit 1344 octets et 2 problèmes :
1) le fait qu'en cas de redémarrage / coupure de courant toutes les mesure était perdu
2) l'optimisation du code
effectivement 24x7 = 168 valeurs à enregistrer... mais avez vous besoin de les mémoriser sur 4 octets chacune?
--> Quelle est la résolution de votre écran? Si l'amplitude du graphe tient sur 256 points (et vous n'êtes pas au pixel près donc à mon avis même pour 512 ça irait), vous feriez mieux de mémoriser le Y sur un seul octet - ça ramène pour la température et le ph à 2 x 168 octets = 336 octets ==> ça tiendrait en SRAM plus facilement mais en cas de reboot on perd tout l'historique, donc si c'est un soucis alors il faut effectivement penser à stocker cela quelque part.
deux options: utiliser l'EEPROM de votre Arduino mega avec un buffer circulaire: Vous définissez une adresse de départ pour la T°, disons à l'adresse 100 et toutes le heures vous mémorisez dans la case suivante la valeur de la T°. Quand vous êtes arrivé à 168 valeurs enregistrée, celle suivante vient écraser celle qui est en 100 et donc au bout d'un moment pour lire les 168 dernières valeurs, il faut commencer à lire au milieu de la mémoire aller jusqu'au bout puis recommencer au début (c'est pour cela qu'on dit que c'est un buffer circulaire). il faut donc mémoriser quelque part aussi l'adresse du pointeur mémoire de la prochaine écriture et le nombre de valeur valides (avant qu'on en ait écrit 168 éventuellement) --> si vous voulez que ça résiste à un reboot il faut aussi mettre cela en EEPROM. comme vous avez moins de 255 valeurs, ça prend aussi 1 octet pour chacun. et l'octet de "case suivant" sera modifié toutes les heures. Comme l'EEPROM a une durée de vie de 100,000 écritures ça veut dire qu'au bout de 100,000 heures cette case mémoire qui contient l'index 'case suivante' ne sera plus fiable.... Bon, 100,000 heures ça veut dire que votre case mémoire risque de devenir défaillante dans 11 ans... ça laisse le temps de voir venir... dans 10 ans vous pouvez recompiler le programme éventuellement et stocker ce pointeur dans une autre case qui n'a pas encore été utilisée et vous repartez pour 10 ans :)
pour la partie stockage des données, vous allez écraser la valeur que tous les 7 jours donc le reste de la mémoire tient 700,000 jours - quasiment 2000 ans... je pense donc que vous n'avez pas à vous en soucier :)
(notez que vous avez sur un MEGA de quoi stocker sur 4 octets chaque valeur si c'est ce que vous voulez conserver)
L'autre option comme vous le mentionnez c'est une carte SD.
J'ai donc pensé a mettre tout sa sur SD.
Si je met en place ce que je sait déjà faire, sa va être 1 mesure = 1 fichier, un peut cochon tout ça.
>mettre dans un .csv, et la, je doit encore tout apprendre. j'ai trouvé diffèrent poste qui explique comment écrire dans un .csv mais aucun pour récupérer une donné dans un emplacement précis.
Si vous savez ou je peut trouver un tuto pour faire ca :)
A moins qu'il y est une autre solution que le .csv?
Vu que le fichier ne sert qu'à l'affichage, perso je ne m'ennuierai pas avec un format lisible par les humains... et je mettrais les valeurs en binaire directement dans le fichier, les unes à la suite des autres, éventuellement avec une gestion un peu smart des fichiers - par exemple un fichier dont le nom est la date du genre
20180304.dat de 2 x 24 valeurs par jour et pour bâtir le graphe final avec votre RTC vous savez quels sont les noms des 7 derniers fichiers à aller lire et vous importez les valeurs en vous en servez pour l'affichage. si le nom de fichier n'existe pas c'est que les données ne sont pas encore là, et vous mettez des 0 par exemple sur le graphe.
il y a de fortes chances que la carte SD ne dure pas 10 ans - donc il faudra prévoir de la changer à un moment et vous perdrez l'historique ce faisant - mais vous n'avez pas à re-compiler le programme.
Perso je prendrai l'option EEPROM car ce sera plus rapide et moins compliqué à gérer que la carte SD.
Voici un exemple de code qui gère un buffer circulaire en EEPROM.
le fonctionnement reprend ce que je décris plus haut (avec 5 valeurs au lieu de 168) et comme je ne veux pas attendre toutes les heures pour rajouter une entrée et voir le tableau se remplir, j'ai mis dans la loop un code qui attend que vous tapiez la lettre 's' sur la console série (réglée à 115200 bauds) pour générer un octet aléatoire et aller le ranger en mémoire, puis afficher la liste des valeurs. en envoyant plusieurs 's' vous verrez le tableau se remplir, puis une fois plein se décaler... c'est le principe du buffer circulaire.
#include <EEPROM.h>
const byte maxValeurs = 5; // le nombre de valeurs max à sauver en EEPROM
byte indexProchaineValeur = 0; // un index par rapport à l'adresse de début (ici on stocke des octets)
byte nombreTotalValeurs = 0; // combien de valeurs sont valides dans la mémoire (une fois le tableau rempli elles sont toutes valides)
// GESTION DE L'EEPROM
const uint32_t motClef = 0xBADCAFE; // pour savoir si l'EEPROM a déjà été initialisée
const uint16_t adresseMotClef = 0x100; // on commencera à stocker tout cela à partir de cette adresse en EEPROM
const uint16_t adresseNombreTotalValeurs = adresseMotClef + sizeof(motClef);
const uint16_t adresseProchaineValeur = adresseNombreTotalValeurs + sizeof(nombreTotalValeurs);
const uint16_t adresseBufferCirculaire = adresseProchaineValeur + sizeof(indexProchaineValeur);
void etablirValeursParDefaut()
{
uint32_t lectureMotClef;
Serial.print(F("Mot Clef = 0x")); Serial.println(motClef, HEX);
Serial.print(F("Adresse Mot Clef = 0x")); Serial.println(adresseMotClef, HEX);
Serial.print(F("Adresse des valeurs = 0x")); Serial.println(adresseBufferCirculaire, HEX);
EEPROM.get(adresseMotClef, lectureMotClef);
if (lectureMotClef == motClef) {
// la mémoire a déjà été initialisée, on peut charger les paramètres
Serial.println(F("initialisation OK"));
EEPROM.get(adresseProchaineValeur, indexProchaineValeur);
EEPROM.get(adresseNombreTotalValeurs, nombreTotalValeurs);
} else {
// la mémoire n'avait pas encore été initialisée
Serial.println(F("params en memoire pas OK"));
indexProchaineValeur = 0;
nombreTotalValeurs = 0;
EEPROM.put(adresseProchaineValeur, indexProchaineValeur);
EEPROM.put(adresseNombreTotalValeurs, nombreTotalValeurs);
EEPROM.put(adresseMotClef, motClef);
}
Serial.print(F("indexProchaineValeur = ")); Serial.println(indexProchaineValeur);
Serial.print(F("nombreTotalValeurs = ")); Serial.println(nombreTotalValeurs);
}
void lireBufferCirculaire()
{
byte n = 1, v;
if (nombreTotalValeurs == 0) return;
Serial.println(F("------ LECTURE DU BUFFER CIRCULAIRE ------"));
// si le buffer est plein on a les plus vieille valeurs entre indexProchaineValeur et la fin du buffer, puis en relisant depuis le début
// sinon on lit simplement depuis le début
if (nombreTotalValeurs >= maxValeurs) { // buffer plein
for (int i = indexProchaineValeur; i < maxValeurs; i++) {
EEPROM.get(adresseBufferCirculaire + i, v);
Serial.print(n++); Serial.print(F("\t->\t")); Serial.println(v);
}
}
for (int i = 0; i < indexProchaineValeur; i++) {
EEPROM.get(adresseBufferCirculaire + i, v);
Serial.print(n++); Serial.print(F("\t->\t")); Serial.println(v);
}
Serial.println(F("-------------------------------------------\n"));
}
void ajouterDansBufferCirculaire(byte v)
{
Serial.println(F("------ AJOUT DANS BUFFER CIRCULAIRE ------"));
Serial.print(F("Valeur = ")); Serial.println(v);
// on range v au bon endroit
EEPROM.put(adresseBufferCirculaire + indexProchaineValeur, v);
indexProchaineValeur++;
if (indexProchaineValeur >= maxValeurs) indexProchaineValeur = 0; // on boucle
EEPROM.put(adresseProchaineValeur, indexProchaineValeur);
if (nombreTotalValeurs < maxValeurs) {
// on met alors à jour le nombre de valeurs correctes ensuite c'est plus la peine
// le buffer circulaire est plein
nombreTotalValeurs++;
EEPROM.put(adresseNombreTotalValeurs, nombreTotalValeurs);
}
Serial.println(F("-------------------------------------------\n"));
}
void setup() {
Serial.begin(115200);
randomSeed(analogRead(0));
etablirValeursParDefaut();
}
void loop() {
// on attend l'envoi d'un 's' pour suivant sur le port série
if (Serial.available()) {
int c = Serial.read();
if ((c != -1) && ((byte) c == 's')) {
ajouterDansBufferCirculaire((byte) random(0, 256));
lireBufferCirculaire();
}
}
}
en tapant plusieurs fois de suite s voici à quoi la console ressemble
Mot Clef = 0xBADCAFE
Adresse Mot Clef = 0x100
Adresse des valeurs = 0x106
initialisation OK
indexProchaineValeur = 0
nombreTotalValeurs = 0
>>> ici je tape s<<<
------ AJOUT DANS BUFFER CIRCULAIRE ------
Valeur = 120
-------------------------------------------
------ LECTURE DU BUFFER CIRCULAIRE ------
1 -> 120
-------------------------------------------
>>> ici je tape s<<<
------ AJOUT DANS BUFFER CIRCULAIRE ------
Valeur = 131
-------------------------------------------
------ LECTURE DU BUFFER CIRCULAIRE ------
1 -> 120
2 -> 131
-------------------------------------------
>>> ici je tape s<<<
------ AJOUT DANS BUFFER CIRCULAIRE ------
Valeur = 224
-------------------------------------------
------ LECTURE DU BUFFER CIRCULAIRE ------
1 -> 120
2 -> 131
3 -> 224
-------------------------------------------
>>> ici je tape s<<<
------ AJOUT DANS BUFFER CIRCULAIRE ------
Valeur = 161
-------------------------------------------
------ LECTURE DU BUFFER CIRCULAIRE ------
1 -> 120
2 -> 131
3 -> 224
4 -> 161
-------------------------------------------
>>> ici je tape s<<<
------ AJOUT DANS BUFFER CIRCULAIRE ------
Valeur = 130
-------------------------------------------
------ LECTURE DU BUFFER CIRCULAIRE ------
1 -> 120
2 -> 131
3 -> 224
4 -> 161
5 -> 130
-------------------------------------------
>>> ici je tape s<<< MAIS LE BUFFER EST DEJA PLEIN
------ AJOUT DANS BUFFER CIRCULAIRE ------
Valeur = 163
-------------------------------------------
------ LECTURE DU BUFFER CIRCULAIRE ------
1 -> 131
2 -> 224
3 -> 161
4 -> 130
5 -> 163
-------------------------------------------
on voit bien dans le dernier affichage que le buffer s'est rempli et que l'on a décalé les valeurs .on perd le 120 et le 131 qui était en 2 devient la première valeur
Wahou ...
Toujours aussi Top les réponse :)
J'avais mis de coté la sauvegarde sur eeprom du fait de l'usure mais c'est vrai qu'avec un petit calcul j'aurais pu m'apercevoir que cela était jouable.
Restons donc sur cette option.
En se qui concerne la valeur stocker sur 4 octets :
Je pense que tu parle de mettre sur un octets l'adresse du pixel a dessiner sur le graphique ?
Si c'est bien cela veut dire qu'il faut revoir l'integralité de ma fonction qui dessine les courbe puisque actuelement le PH et Temperature sont stocker sous forme entiere (26.55°=2655 et ph 8.62 = 862)
Puis ensuite la fonction si dessous prend la température avant après et dessine les pixel "manquant" en passant par une fonction MAP qui adapte les mesure (de 2600 a 2800 pour le température) a la taille du graphique (156pixels) :
void courbeTemperature9Pixels(int16_t Tprecedent, int16_t Tsuivant) { // dessine la courbe temperature sur 7 jours (boucle de 9 pixels, a lancer 84 fois pour graph complet ****************
uint16_t val_courbe = Tprecedent;
uint8_t i = 0;
float unHuitieme = ((Tsuivant - Tprecedent) / 8.00);
while (i < 9) { // boucle 9 fois
val_courbe = val_courbe + (unHuitieme * (i + 1)); // ajoute a la valeur precedent la diference entre les 2 mesures divisé en 9
if (i == 8) {
val_courbe = Tsuivant;
}
if (val_courbe < minimumCourbeTemp) {
val_courbe = minimumCourbeTemp;
}
else if (val_courbe > maximumCourbeTemp) {
val_courbe = maximumCourbeTemp;
}
uint8_t pixel_courbe = map(val_courbe, minimumCourbeTemp, maximumCourbeTemp, 0, hauteurEnPixelCourbe);
variablegraphtemperature7j.addValue(0, (pixel_courbe)); // envoie 1 pixels
i++;
delay(10);
}
DPRINTF("envoie 9 pixels au graph temperature sur 7 jours"); DPRINTLN();
}
Sincèrement, je suis pas venu vous embeter ici avec la parti graphique parce que je savait que vous pouviez pas tester, mais franchement, j'ai galéré lol
Avec la place dispo dans l'eeprom, je peut stocker mes 1344 octets :/ c'est sur que c'est la simplicité et c'est l'eeprom ne seras pas optimisé mais je ne suis pas coincé de ce coté la contrairement a la mémoire de l'arduino déjà a 76% :(
donc si je pouvais éviter :) mais j'ai peut être oublier de prendre un paramètre en compte ?
Pour l'adressage, si on part du principe que la température lundi 0h est stocker en 1000 par exemple lundi 1h a 1004 lundi 2h a 1008, ect ...
Pourquoi avoir besoin d'un pointeur a stocker ?
Il ne suffirait pas de lire le jour+heure pour savoir ou écrire ? (exemple mardi 12h = 1144)
ps: merci Breisleach, content que sa plaise.
ps2: on doit y etre au 10 bon point ... J'ai gagné quoi ? ;D
Pour l'adressage, si on part du principe que la température lundi 0h est stocker en 1000 par exemple lundi 1h a 1004 lundi 2h a 1008, ect ...
Pourquoi avoir besoin d'un pointeur a stocker ?
Il ne suffirait pas de lire le jour+heure pour savoir ou écrire ? (exemple mardi 12h = 1144)
Pour plusieurs raisons - la première étant que Le calcul de l'adresse ne sera pas simple une fois la semaine remplie, le principe du circulaire c'est qu'il faut revenir au début :)
Ensuite si vous avez une panne de courant de 2h vous allez vous retrouver avec deux cases non remplies et ça va faire moche sur le graphe - soit vous aurez 0 soit la valeur de 7 jours d'avant qui n'aura pas été écrite. En se souvenant de là où on doit écrire (pas fait dans le code ci dessus) et en fonction du jour vous pouvez savoir (mais c'est des maths un peu compliqués sauf si vous sauvez l'heure de dernière mis à jour aussi en eeprom) qu'il y a eu un problème éventuellement et remplir les cases avec une valeur par défaut
ps2: on doit y etre au 10 bon point ... J'ai gagné quoi ? ;D
De mon temps on gagnait une image :)
(http://forum.arduino.cc/index.php?action=dlattach;topic=504431.0;attach=247985)
De mon temps on gagnait une image :)
(http://forum.arduino.cc/index.php?action=dlattach;topic=504431.0;attach=247985)
Arfff :( :( :(
Tout ca pour ca ... :/
lol
De mon temps on gagnait une image :)
De notre temps on est plus iphone ou playstation lol
Ensuite si vous avez une panne de courant de 2h vous allez vous retrouver avec deux cases non remplies et ça va faire moche sur le graphe - soit vous aurez 0 soit la valeur de 7 jours d'avant qui n'aura pas été écrite. En se souvenant de là où on doit écrire (pas fait dans le code ci dessus) et en fonction du jour vous pouvez savoir (mais c'est des maths un peu compliqués sauf si vous sauvez l'heure de dernière mis à jour aussi en eeprom) qu'il y a eu un problème éventuellement et remplir les cases avec une valeur par défaut
Effectivement, l'exemple de code circulaire est très bien mais, outre "c'est des maths un peu compliqués" en cas de coupure, un autre point essentiel me parait compliqué :
Lors de l'affichage du graphique, il faudra, tout comme le graphique 24h que l'heure en bas du graphique coïncide avec les mesure relevé (la dernière mesure devant être a droite avec la date/heure correspondante.
Je ne vois pas comment je pourrait faire vu que la sauvegarde dans l'eeprom se fait sans correspondance avec date/heure
Oui soit vous afficher sur le graphe les 168 dernières mesures réalisées en croisant les doigts pour qu'il n'y ait pas eu de coupure de plus d'une heure, ou en vous en fichant un peu, ça donne la tendance, soit il faut faire un peu de maths (et conserver quand même en mémoire le dernier index écrit)
Vous devez noter l'heure de démarrage initial de votre système en EEPROM (temps unix sur 4 octets fournis par la RTC) disons que c'est t0. le temps unix (ou posix) est le nombre de secondes écoulées depuis le 1er janvier 1970 00:00:00 UTC jusqu'à l'événement à dater (hors secondes intercalaires).
À l'instant unix t il s'est donc écoulé (t - t0) secondes si vous divisez (division entière) cela par 24x3600ul ça vous donne le nombre de jours et si vous prenez le modulo de ce nombre de jour par 7 ça vous dit à quel jour J (0 à 6) vous en êtes.
vous calculez d'une manière similaire le N° H de l'heure de l'enregistrement de la journée (je vous laisse réfléchir)
Ensuite le calcul (24 x J + H) vous donne l'index dans l'EEPROM ou vous devriez écrire. si ce n'est pas la case suivante (attention c'est circulaire) de celle où vous avez écrit la dernière fois alors c'est que vous avez perdu des heures, il faut remplir les cases intermédiaires avec une valeur arbitraire (0 ou maintenir la dernière valeur connue par exemple)
Oui soit vous afficher sur le graphe les 168 dernières mesures réalisées en croisant les doigts pour qu'il n'y ait pas eu de coupure de plus d'une heure, ou en vous en fichant un peu, ça donne la tendance, soit il faut faire un peu de maths (et conserver quand même en mémoire le dernier index écrit)
Vous feriez quoi ? ( au vu du nombre de fois ou il va y avoir un reboot / par rapport / difficulté du code a ecrire et la memoire qui va etre utilisé, tout en sachant que cette fonction "graphique" ne me parait pas primordial mais juste "pour info" ?
:smiley-confuse:
Je me demanderais si c'est bien nécessaire dans un premier temps :)
Est-ce que les 7 derniers jours présentent un intérêt sur la gestion de l'écosystème ou c'est juste comme ça ? Pourquoi 7j et pas 1 mois ou un an ?
j'ai envie de dire c'est un +
L'absence de cette fonction est totalement sans incidence sur la gestion du bac.
après c'est utile en cas de problème dans le bac, c'est un premier contrôle, température et ph
Oui, pourquoi pas un ans lol
perso si c'était important j'enverrai les données sur un services en ligne et je consulterai par mon iPhone plutôt que d'avoir un écran sur l'unité
Si la consultation à la semaine est vraiment épisodique alors juste écrire sur la carte SD un fichier cvs et le lire et représenter dans Excel si besoin de vérifier l'historique - vous pouvez ainsi stocker des années de données
L'idée de stocker indéfiniment et voir sur excel me parait un bon compromis, je garderais ainsi que la partie sur 24h "volatile".
faut que je regarde comment ecrire en continu dans un .csv et sans reprendre a zero en cas de coupure.
Écrire un csv est trivial
Vous décidez du format d'une ligne; quelles valeurs vous souhaitez écrire - et ensuite c'est juste des print de ces valeurs séparées par des virgules ou des tabulations Et vous finissez par un retour à la ligne "\r\n"
La première écriture pourrait-être des étiquettes pour les colonnes pour que ça fasse joli sous Excel et faciliter les tris par étiquette au lieu de lettre de colonne
Ma recommendation serait de commencer par mettre aussi la date et l'heure sur chaque ligne éventuellement avec aussi l'heure UNIX - ça vous facilitera la vie sous Excel sans écrire de formules pour faire des tris et des graphes
Il faut bien sûr gérer ça correctement donc vérifier si le fichier existe. Si le fichier n'existait pas alors il faut ouvrir le fichier en vérifiant que ça s'est bien passé et écrire la ligne d'en tête avant la première ligne de données, sinon juste les données à la fin du fichier et une fois la ligne écrite fermer le fichier.
Si vous avez 100 caractères sur une ligne - soit 100 octets - vous pouvez stocker plusieurs milliers d'années de données toutes les heures sur une carte SD de 4Go ou 80 ans si vous écrivez toutes les minutes avant que la carte soit pleine (elle plantera avant :) )
Bien sûr il faut utiliser la librairie SDFat et pas la libraire SD standard
Une fois de plus merci jean Marc pour ce retour.
Mon déménagement approche donc carton et tout le reste ...
On range l'électronique :smiley-confuse:
je doit mettre le projet en Standby :(
Si tout ce passe bien, dans ~10 jours je reprendrait le projet.
Mais ne vous inquiété pas, je vous ferait profité de l'installation du nouvelle Aqua pendant ce lapse de temps ;)
Bon déménagement !!
merci
Boun's bonjour.
Ouep bon courage et bon emménagement dans ta futur demeure.
Pascal.
Merci
Bonsoir,
Des petites NEWS :)
Déménagement fini vendredi, c'était compliqué
La fin du 400l (http://www.recifal-france.fr/400l-de-djbouns-t22449-414.html#p598186)
Le début (chaotique) du 430l (http://www.recifal-france.fr/430l-de-djbouns-t34485.html#p597886)
J'esperre reprendre le projet dans moins de 10 jours, si tout va bien ...
chaotique est un faible mot !!! courage
Boun's, J-M-L bonjour.
Ouep Boun's c'est pas peu dire comme début, la tu manque sérieusement de chance, mais sur que cela vas devenir un beau bocal.
Pascal.
Bonjour tout le monde, je passe juste vous faire un petit coucou.
Comme vous l'avez remarquer, 2 mois ou le projet est au point mort.
Dans un premier temps a cause de mon déménagement + nouvelle aquarium qui a pris beaucoup de temps et d'énergie mais a peine fini, j'ai eu un "petit" problème de santé.
J'ai tout le temps que je veux mais je ne suis pas en mesure de rester concentré.
Si tout se passe bien, j'espère que se seras réglé dans ~2 mois.
Le projet aboutira :)
Dsl de vous faire patienté.
a+
Le début (chaotique) du 430l (http://www.recifal-france.fr/430l-de-djbouns-t34485.html#p597886)
et, dans le lien :
4e bac en 6 ans ...
Et celui la démarre très mal ...
Ça a commencer par un retard de fabrication, du coup j'ai eu le bac mardi soir et il fallait que jeudi soir tout soit transféré.
Quand je pense à tous les étudiants qui passent demander de l'aide pour un seul bac, alors que tu en as déjà 4 !!! (fin mode taquin)
Bon rétablissement et bon courage !
Bon courage !
Bonjour a tous,
L'opération est passer mais je doit encore me reposer un bon moment ...
Quand cela le permettait j'ai bosser un peu sur le plus simple, le moins prise de tete, le pcb :)
Je pense être a la version final, encore quelque petite modif et test de composant et sa seras bon.
Dans les grande ligne :
- Redémarrage a zéro du pcb
- toute les connections sont sur le pcb, jack, rca, din, usb A, usb B ....
- arduino, sd, horloge, wifi sur le pcb, il n'y a que le gsm et les carte PH (sortie sonde traversant le boitier) qui son a déporter dans le boitier.
- 2 connections pour l'écran, soit interne, soit pour le déporter
- un système de secours avec une connection pour batterie de téléphone externe qui prend le relais en cas de coupure de courant (sans reboot, encore en test...)
- réduction de la taille du pcb : de 120*150 (180cm2) a 100*160 (taille standard)(160cm2)pour réduire le cout
- 2 cartes annexe, une pour diviser les flotteurs (5 flotteurs) et l'autre pour diviser les oscillateurs ( 3 oscillateurs). cela permet d'avoir 2 connexions/câbles partant du pcb au lieu de 10+9 fils et de multiple rallonge.
Je vous joint un aperçu du pcb (https://image.noelshack.com/fichiers/2018/26/4/1530176449-pcb.jpg)
Je voudrait aborder un autre aspect de se projet afin d'avoir votre avis ...
J'ai été approcher par le directeur d'une société (création de pièce en alu ....) passionné de récifal et travaillant sur l'automatisation de l'aquarium (rampe éclairage principalement, fabrication du dissipateur en aluminium :) et toute la partie électronique.
Son but étant claire pour moi, commercialiser ces créations.
Son intérêt en vers moi et mon projet me paraissant "bizarre" (je suis un simple amateur comparer a se que j'ai vu de ces réalisations digne d'une société professionnel) je n'est pas donner suite.
Du coup, je me questionne beaucoup sur ce que pourrait faire des personnes de mon projet.
Se l'accaparer ? déposer un "brevet" ? en tirer des bénéfices ?
Cela me perturbe assez, a ne plus savoir comment je doit partager se projet ...
Welcome back!!!
Reposez vous - sans vous poser des questions existentielles sur votre code ! (Sinon rajoutez partout que c'est en GPL V3 dans vos code sources)
Content de te vous relire JM :)
être devant un ordi est reposant :)
entre 2 mal de tete je poursuit le pcb et quand j'ai retrouver mes esprits jme met sur la dernière long ligne droite du code ... (la reprise va etre tres difficile :/)
(déjà 1 ans et 2 mois que j'ai démarrer se projet)
Pour le GPL V3, la lecture ma permis d'en comprendre le principe mais je n'ai pas compris se qu'il faut faire pour etre sous cette license, déclaration ? ou juste l'indiquer dedans ?
Pour le GPL V3, la lecture ma permis d'en comprendre le principe mais je n'ai pas compris se qu'il faut faire pour etre sous cette license, déclaration ? ou juste l'indiquer dedans ?
vous :) Tu peux lire ce document (https://www.gnu.org/licenses/gpl-howto.fr.html)
;) je préfère, depuis le temps que l'on échange :)
La liste des choses a faire est assez simple a mettre en place.
Il ne semble pas y avoir quoi que se soit a "déposer"
La ou je ne comprend pas, c'est que n'importe qui peut récupérer le code, le modifier (jusque la, OK), enlever les mentions et déposer un "vrai" copyright ?
Cette lecture me donne l'impression que la GPL est basé sur la bonne fois de chacun ...
Il suffit de le publier à un endroit public, genre github, ce qui datera la mise à dispo et si le code est piqué par quelqu'un qui ne respecte pas votre license alors vous pourriez l'attaquer. attention ça n'empêche pas d'autres personnes de lancer des produits avec votre code, la communauté libre c'est le partage, mais ça les obligera aussi à publier tout leur code de tout leur produit et donc rester dans l'esprit
En pratique lancer un produit commercial sur du code piqué comme cela plus ou moins au hasard et qui dépend de quelqu'un d'autre pour fixer les bugs est le meilleur moyen de perdre de l'argent
ok ...
il y a déjà plusieurs mois que j'ai déjà crée un compte github pour partager l'aquabouns.
Donc tout le monde pourras prendre le code, l'utiliser, le modifier et même vendre un produit commercial le contenant comme source mais il seras dans l'obligation de toujours laisser le code modifier a disposition ...
Sympa cette License de "partage"
Ok, je suis un peu moins inquiet du coup :)
Oui - il faut juste vous assurer que chaque fichier le texte qui va bien en en tete
Bouns heureux d'avoir de tes nouvelles et d'être de retour sur ton projet.
Bon rétablissement et vas y cool.
Pascal.
Merci Breisleach
J'ai de la chance, assis sa va alors sa me permet d'avancer et ça m'occupe :)
Je crois me souvenir avoir croiser quelqu'un ici qui avait des connaissance en électronique.
Je rencontre un petit problème avec mon système de "relais alim/batterie" en cas de coupure
Si il est toujours la, qu'il me fasse un petit signe :) (que j'encombre pas le poste "code" avec l'électronique :) )
J'ai commencer a relire le code (pour re comprendre lol) et j'ai fait quelque bidouille
La page "horloge" est ok (modif et sauvegarde) mise a part le fait des mois en 30/31 jours qui n'est pas pris en compte.
(https://image.noelshack.com/minis/2018/27/2/1530625894-img-20180703-150522.png) (https://www.noelshack.com/2018-27-2-1530625894-img-20180703-150522.jpg)
et puis une petite liste de problème :
sauvegarde ne fonctionne plus (enregistre le premier paramètre sur tout les suivant)(je vais donc relir tout les poste qui en parlais ici pour trouver d'ou vient le problème)
Problème avec les sonde PH, quand l'ecran nextion se met en veille(10min) il y a moins de chute de tension de le CI (4.80v>5v) du coup les resultat des sonde ph varie de 0.5. (je cherche comment faire ...)
Partie graphique, le code étant déjà assez long je met se point de coté mais il y a de forte chance pour que le graphique ne soit visible que par le wifi et non pas via l'écran nextion.
Toute la partie gestion de floteur a ecrire, il y en auras 5: niveau constant, niveau écumeur, sécurité, niveau bas eau osmose, niveau haut godet écumeur)
après ça, il resteras a me remémorer et inclure toute la partie wifi et gsm ...
Pour le problème de sauvegarde, cela ne vient pas de la partie du code "sauvegarde".
Lorsque je lance la sauvegarde, l'arduino demande la valeur au nextion.
Nextion repond exemple 100 et l'arduino le stock dans un uint8_t number
number a la bonne valeur.
number est ensuite copier : brassage1 = number.
Puis l'arduino lance une deuxième requête et la, la réponse est 100 alors que ce n'est pas la valeur demandé.
Toute les requête suivante obtienne le même resultat.
comme si nextion n'avait pas eu le Temp de répondre ou l'arduino de les lire.
Pourtant le baud rate est a 115200 et pas de problème quand c'est l'arduino qui envoie une variable au nextion ...
:(
Quand l'ecran est brancher directement sur l'arduino ça a l'air OK
Des qu'il est sur le CI sa merde ...
pourtant il n'y a que 54mm de longueur de piste ...
Arff le cuivre ralenti la transmission ? :smiley-confuse:
ça semble résolu ...
J'ai mis une couche de vernis sur les pistes, pour le moment plus de problème .... ouf ...
Petite video (https://youtu.be/RXGwhhRWrbg)
J'ai commencer a bien me remettre dans le code, c'est repartie :)
j'essai au max d'optimiser dans un premier temps car la place manque :/
Et principalement sur la partie affichage mais pas facile avec le nextion.
J'ai pas mal de fonction identique mais avec la bibliothèque nextion et les déclarations je ne trouve pas comment faire.
declaration :
NexButton boutonomoinsoscillo1min = NexButton(9, 8, "b31");
NexButton boutonplusoscillo1min = NexButton(9, 11, "b34");
Par exemple j'ai :
void boutonoscillo1min(void *ptr) {
valeuroscillo1min.getValue(&number);
boutonoscillo1();
}
void boutonoscillo1max(void *ptr) {
valeuroscillo1max.getValue(&number);
boutonoscillo1();
}
void boutonoscillo1(){
positionOscillo1 = number;
oscillo1.write(positionOscillo1);
affichagedegreoscillo1.setValue(positionOscillo1);
}
et je voudrait plutot faire unpeu comme ca :
void boutonoscillo(void *ptr,[b]MonNomDeBoutton[/b]) {
MonNomDeBoutton.getValue(&number);
positionOscillo1 = number;
oscillo1.write(positionOscillo1);
affichagedegreoscillo1.setValue(positionOscillo1);
}
dans la biblioteque il y a :
NexVariable::NexVariable(uint8_t pid, uint8_t cid, const char *name)
:NexTouch(pid, cid, name)
{
}
uint32_t NexVariable::getValue(uint32_t *number)
{
String cmd = String("get ");
cmd += getObjName();
cmd += ".val";
sendCommand(cmd.c_str());
return recvRetNumber(number);
}
se qui voudrait dire qu'il prend l'adresse des déclaration et non le nom donné ?
Concernant les échanges tx/rx ecran/arduino.
quand l'écran est en veille, j'ai l'impression que l'arduino n'aime pas et fini par "beuger".
Quand l'arduino envoie des donner, attend t il un retour de l'écran sur la réception de ces donner ? si oui, cela explique un beug de l'arduino ?
pour info,
Je cherchait depuis un moment a avoir un resultat stable dans la mesure du PH.
Je n'était pas satisfait des module ph 4502c qui me donnait un ph +/- 0.2
J'ai donc cherche et ce module ne semble qu'être qu'un convertisseur 5v>3.5 pour la sonde puis un amplis
J'ai donc fais le montage suivant avec juste la sonde (plus de module):
arduino 3.3v > résistance ajustable 103 > sonde ph > condensateur ceramique 103 > IN analogique arduino.
puis le code suivant :
const byte pinInPhRac = A0; // Connect the sensor's Po output to analogue pin 0.
// Variables:-
float phRac;
float bas = 9999;
float haut = 0;
uint8_t moyenneGlissante = 0;
boolean moyenneGlissanteON = false;
const byte nbrDeMesure = 20;
uint16_t moyennePhRac[nbrDeMesure];
void setup()
{
Serial.begin(74880);
}
void loop()
{
moyennePhRac[moyenneGlissante] = map(analogRead(pinInPhRac), 755, 557, 401, 918);
moyenneGlissante++;
if (moyenneGlissante == nbrDeMesure) {
moyenneGlissanteON = true;
moyenneGlissante = 0;
}
if (moyenneGlissanteON == true) {
for (int i = 0; i < nbrDeMesure; i++) {
phRac += moyennePhRac[i];
}
phRac = phRac / (nbrDeMesure * 200);
Serial.print("ph actuel : "); Serial.println(phRac);
if (phRac < bas) {
bas = phRac;
}
if (phRac > haut) {
haut = phRac;
}
Serial.print("ph bas: "); Serial.println(bas);
Serial.print("ph haut: "); Serial.println(haut);
Serial.println();
}
}
le meilleur compromis a été avec une moyenne rotative sur 20 mesure, j'arrive a un resultat variant de +/- 0.02
une moyenne rotative sur 10 mesure donnait +/- 0.05
Et pour avoir des mesure +/- 0.01 il fallait monter a 50 mesure.
Ce montage a l'air satisfaisant dotant plus qu'il fait économiser 15€ par module (2 module sur mon projet)
Concernant les échanges tx/rx ecran/arduino.
quand l'écran est en veille, j'ai l'impression que l'arduino n'aime pas et fini par "beuger".
non ça ne devrait absolument pas jouer (si vous parlez du PC. sur le nextion je ne sais pas)
non je parle bien du nextion.
J'ai mis une veille au bout de 10 min. et l'arduino fini toujours par beuger.
Donc je me demandait , lorsque il y a des envoie de donner, es que l'arduino envoi bêtement ou es qu'il détecte qu'il n'y a plus de réponse en face et beug.
Je ne sais pas - je n'utilise pas ce composant - faudrait regarder si la librairie attend un retour
Il se peut que ça fonctionne tant que le buffer d'entrée du nextion n'est pas plein et ensuite il perd des données et la synchro ne se ferait plus bien au moment où il se réveille? (Pure hypothèse)
Au lieu de désactiver l'écran si vous aviez une fonction qui le met au noir ?
j'ai déjà jeter un coup d'œil a la bibliothèque mais je n'est rien trouvé mais je ne suis pas spécialiste :)
Il y a bien une fonction pour le rétroéclairage, le problème qui va se poser est que si l'on"Click" sur l'écran sombre a l'endroit ou il y a un bouton, cela va réveiller l'écran mais aussi actionner la fonction.
Il faudrait peut être basculer sur une page "stand-by", ecran sombre ou la totalité de l'écran est un bouton mais cela veut dire que c'est l'arduino qui doit gérer cela au lieu que se soit l'écran lui même ...
Encore une fonction qui va me prendre énormément de temps :smiley-confuse: et de mémoire sur l'arduino
Je ne sais pas - je n'utilise pas ce composant - faudrait regarder si la librairie attend un retour
Oui. Voir la fonction recvRetCommandFinished (https://github.com/itead/ITEADLIB_Arduino_Nextion/blob/master/NexHardware.cpp).
Mais le timeout est court : 100ms.
@+
Bonjour hbachetti,
bool recvRetCommandFinished ( uint32_t timeout)
{
bool ret = false ;
uint8_t temp [ 4 ] = { 0 };
nexSerial. setTimeout (timeout);
if ( sizeof (temp)! = nexSerial. readBytes (( char *) temp, sizeof (temp)))
{
ret = false ;
}
if (temp [ 0 ] == NEX_RET_CMD_FINISHED
&& temp [ 1 ] == 0xFF
&& temp [ 2 ] == 0xFF
&& temp [ 3 ] == 0xFF
)
{
ret = vrai ;
}
et pour toi cela explique le plantage de l'arduino ?
Absolument pas.
ok merci :smiley-confuse:
Lire ce post sur leur forum (http://support.iteadstudio.com/support/discussions/topics/11000012949) ==> comment est géré l'écran ? en dim ou en sleep et avec quels params?
Merci Jean marc cela rejoint mes précédentes lectures.
J'ai donc :
Crée une page "standby" avec un bouton couvrant toute la surface
Dans la page menu, un timer (1min)
Dans chaque bouton de l'écran menu j'ai ajouter une ligne de commande qui remet le timer a zéro des que le bouton en cliquer et remet le rétroéclairage a son niveau "Default" si ce n'est pas le cas
Si aucune activité au bout d'une minute, le luminosité décent progressivement et quand a zéro, bascule sur la page standby et envoi cette info a l'arduino.
L'arduino stop toute mise a jour car plus sur la page "menu"
Quand on touche l'écran "noir" de la page standby, envoie info a l'arduino qui demande d'afficher la page menu et met tout les champs a jour.
Un fois celle ci charger le nextion remet la luminosité a "Default"
depuis ce matin pas de beug, je laisse tourner pour être sur ...
Cool
Mais il ne semblait - à la lecture de leur doc - que vous n'aviez pas vraiment Besoin de le gérer vous même et que des variables d'environnement permettait de gérer cela
la traduction laisse pensé que oui
Précédament j'avais thsp = 60 + thup = 1
sauf que des que l'écran passe en mode "thsp" l'arduino fini par beuger même ci pour hbachetti cela n'en n'est pas la cause.
pour le mode sleep, il faut le gérer le passage dans se mode manuellement, donc faire un timer ... comme je l'ai fait sauf qu'il est préciser "mais la mise à jour d'autres variables pendant le sommeil peut s'attendre à des problèmes." j'ai déjà assez de problème :smiley-lol:
Ce que j'ai fait le met pas l'écran en veille mais éteint le rétroéclairage et passe sur une page "bouton" tout en prévenant l'arduino qui stop toute communication.
depuis que ces en place, pas un seul beug :smiley-cool:
Pour le reste du projet, je voix le bout du tunnel concernant la partie arduino/ecran
Il me reste toute la partie PH que je pensait sur la bonne voix mais qui en faite n'est pas au point.
Le montage plus code est OK pour un PH stable mais dérive des lors que le ph change et le calibrage est impossible. j'attend quelque composant pour me remette dessus.
hormis ça, il me reste 4 flotteurs a cree/coder puis il faut que j'integre la partie SMS et WIFI que l'on a vu il y a plusiseurs mois.
La partie SMS devrait être la plus simple il faut juste que je me remette dans les "char" (comme ben-hur :smiley-eek: :-X )
La partie wifi va demander un gros travaille car il faut déjà intégrer les échanges puis complètement revoir la partie html, un gros morceau :smiley-confuse:
C'est sur la bonne voix :smiley-grin:
Encore du pain sur la planche mais c'est aussi ça qui est intéressant !!
Encore du pain sur la planche mais c'est aussi ça qui est intéressant !!
:o :smiley-roll-sweat:
Mais effectivement ;D je vais donc avoir besoin d'aide 8)
La partie Sms a bien été intégré en fonctionne
voici le retour du maisJ'ai un problème avec le numéro de téléphone sauvegardé.
a la sauvegarde le numéro est bien enregistré mais dans le fichier il y a un passage a la ligne.
Si je retire manuellement dans le fichier txt ce saut de ligne tout fonctionne.
C'est vraiment ce passage a la ligne qui fou tout en l'air.
J'ai beau chercher, je n'est ajouter aucun passage a la ligne, que un caractere de fin.
La partie recup du nextion
void enregistrerTel(void *ptr) {
uint8_t i = 0;
effaceBuffer();
valeurtel.getText(bufferTexte, maxbufferTexte);
strncpy(numero2tel, bufferTexte, maxnumero2tel);
numero2tel[maxnumero2tel] = '\0';
ecritSurSd("numero2tel.txt", numero2tel);
et sauvegarde sur sd
void ecritSurSd(char * nomDuFichierTxt, char * donneeAEcrireSurSD) {// *******************************************************************************************************************************************
SD.remove(nomDuFichierTxt);
myFile = SD.open(nomDuFichierTxt, FILE_WRITE);
if (myFile) {
myFile.println(donneeAEcrireSurSD);
} else {
DPRINTF("Erreur d'écriture dans le fichier "); DPRINTLN(nomDuFichierTxt); DPRINTLN();
}
myFile.close();
}
Le retour charriot à la fin dans le fichier provient du
myFile.println(donneeAEcrireSurSD);
Faudrait just faire un print mais dans l'absolu la fonction de lecture ne devrait pas tenir compte de la fin de ligne et juste ignorer tout ce qui n'est pas des chiffres par exemple
Sinon juste par acquis de conscience, vous aviez bien déclaré la taille du buffer comme celà:
char numero2tel[maxnumero2tel+1];
N'est-ce pas ?
ba oui, comme le serial.println ... pfff
2h de perdu :(
merci
Sinon juste par acquis de conscience, vous aviez bien déclaré la taille du buffer comme celà:
char numero2tel[maxnumero2tel+1];
N'est-ce pas ?
const uint8_t maxnumero2tel = 15;
char numero2tel [maxnumero2tel + 1];
:)
C'était bien fait et sa fait parti des choses vérifier 10 fois avant de comprendre le problème.
En général on procède plutôt comme ceci :
#define MAXNUMERO2TEL 15
char numero2tel [MAXNUMERO2TEL + 1];
Car dans ce cas, la macro MAXNUMERO2TEL n'occupe aucune place en mémoire.
@+
C'est valable pour tout les const uint8_t ?
En général on procède plutôt comme ceci :
#define MAXNUMERO2TEL 15
char numero2tel [MAXNUMERO2TEL + 1];
Car dans ce cas, la macro MAXNUMERO2TEL n'occupe aucune place en mémoire.
En théorie ce que vous dites est correct mais c'est oublier l'étage d'optimisation de code du compilateur. Comme on a pris soin de d'utiliser
const le compilateur sait que ça ne va pas changer et n'alloue pas de mémoire et remplace directement par la valeur dans le code généré. (Idéalement il faudrait utiliser
const byte plutôt que
int. ça aiderait encore plus le compilateur)
D'autre part quand on fait des
define comme cela On laisse moins de chance au compilateur de faire des optimisations car c'est de la substitution de texte et il prend donc en mémoire la place d'un int signé (2 octets sur uno, 4 sur esp par exemple) pour les calculs que le préprocesseur ne sait pas faire (pour le +1 le compilateur ne génère pas de code il sait que c'est 16 le résultat). En utilisant une "variable constante" ça vous donne l'avantage de pouvoir aider le compilateur en typant la constante.
Un const a forcément une adresse en mémoire. Par contre il est plutôt situé dans la zone code.
Sinon, on ne pourrait pas écrire ceci :
void loop() {
// put your main code here, to run repeatedly:
char s[30];
const uint8_t maxnumero2tel = 15;
char numero2tel [maxnumero2tel + 1];
sprintf(s, "addr=%x val=%d", &maxnumero2tel, maxnumero2tel);
Serial.println(s);
delay(1000);
}
// addr=8fb val=15
***** EDIT *****
En déclaration globale, on retrouve son adresse avec avr-objdump :
const uint8_t maxnumero2tel = 15;
char numero2tel [maxnumero2tel + 1];
void loop() {
// put your main code here, to run repeatedly:
char s[30];
sprintf(s, "addr=%x val=%d", &maxnumero2tel, maxnumero2tel);
Serial.println(s);
delay(1000);
}
// addr=112 val=15
@+
Un const a forcément une adresse en mémoire. Par contre il est plutôt situé dans la zone code.
@hbachetti - C'est encore bien pensé mais.... en fait ce que vous voyez c'est que si le code ne peut pas être optimisé alors le compilateur va allouer effectivement de la mémoire.... C'est ce que vous faites ici.
Le compilateur voit que vous avez une référence vers la constante par le biais d'un pointeur et donc qu'il a besoin d'une zone de stockage et à partir de là l'optimiseur de code ne peut plus décider que votre constante ne sert à rien. Il faut comparer ce qui est comparable, Votre programme n'a pas d'équivalent avec un
#define.
Prenez un code de blink par exemple:
const byte pinNb = 13;
void setup() {
pinMode(pinNb, OUTPUT);
}
void loop() {
digitalWrite(pinNb, HIGH);
delay(1000);
digitalWrite(pinNb, LOW);
delay(1000);
}
compilé sur un UNO ça me donne
Sketch uses 928 bytes (2%) of program storage space. Maximum is 32256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.
Maintenant compilez la version avec un
#define#define pinNb 13
void setup() {
pinMode(pinNb, OUTPUT);
}
void loop() {
digitalWrite(pinNb, HIGH);
delay(1000);
digitalWrite(pinNb, LOW);
delay(1000);
}
et vous verrez à la compilation:
Sketch uses 928 bytes (2%) of program storage space. Maximum is 32256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.
On voit bien qu'à programme équivalent la mémoire programme et SRAM utilisées sont identiques.
de plus si vous effectuez une
avr-objdump -S des deux programmes ci dessus et que vous les comparez vous verrez qu'ils sont strictement identiques ==> le compilateur a généré à 100% le même binaire.
En pratique et pour bien faire, le bon usage serait d'utiliser
constexpr byte pinNb = 13; (avec un
static en plus ou pas) et pas
const byte pinNb = 13; mais les vieux usages persistent et les compilateurs étant plutôt doués pour déterminer ce qui est constant et peut être calculé ou optimisé à la compilation, l'adoption de
constexpr (qui date de C++11) n'a pas eu vraiment lieu.
La bonne pratique de programmation c'est de fournir un maximum d'information sur vos intentions au compilateur. un
#define présente l'inconvénient majeur de simplement offrir de la substitution de texte et ensuite le lancer la compilation, ce qui rend le debug et la compréhension des message d'erreurs compliqués. La notion de
const ou
constexpr présente l'avantage sémantique d'informer le compilo sur le type attendu et sur votre intention d'invariabilité, donc le compilateur vous avertirait si vous essayez de modifier un contenu qui est supposé être constant.
Il est cependant très habituel en C / C++ de définir la taille d'un tableau par un #define.
Mon expérience des noyaux LINUX et BSD sans doute ...
@+
Il est cependant très habituel en C / C++ de définir la taille d'un tableau par un #define.
Mon expérience des noyaux LINUX et BSD sans doute ...
@+
oui oui c'est fréquent. Mais c'est moins "informatif" pour le compilateur, donc une moins bonne pratique (qui date du temps où effectivement les optimiseurs ne pouvaient pas virer la variable)
de plus nombreux sont les débutants qui oublient les parenthèses dans les define... par exemple dans ce code on pourrait penser de prime abord que GRAND_BUFFER1 et GRAND_BUFFER2 sont identiques la multiplication étant commutative
2*PETIT_BUFFER_AVEC_NULL ou
PETIT_BUFFER_AVEC_NULL*2 devraient être identiques...
#define PETIT_BUFFER 10
#define PETIT_BUFFER_AVEC_NULL PETIT_BUFFER+1
#define GRAND_BUFFER1 2*PETIT_BUFFER_AVEC_NULL
#define GRAND_BUFFER2 PETIT_BUFFER_AVEC_NULL*2
void setup() {
Serial.begin(115200);
Serial.println(PETIT_BUFFER);
Serial.println(PETIT_BUFFER_AVEC_NULL);
Serial.println(GRAND_BUFFER1);
Serial.println(GRAND_BUFFER2);
}
void loop() {}
et pourtant quand on lance le code on obtient
10
11
21 parce que 2*10+1
12 parce que 10+1*2
Je veux bien mais pratiquant le C depuis 30 ans je préfère privilégier les habitudes en vigueur dans les mondes que je fréquente habituellement et donc la lisibilité pour les communautés auxquelles j'appartiens, LINUX en particulier.
Bien entendu cela suppose certaines précautions, les parenthèses entre autres, mais cela devient vite un réflexe.
Je suis parfaitement conscient que cela équivaut à un nivellement par le bas, mais on n'a pas toujours un compilateur moderne à notre disposition.
Nous avons aussi l'habitude d'utiliser l'éditeur vi, malgré son âge vénérable, car souvent il est disponible sur des plateformes même très rudimentaires.
@+
que de bon souvenirs avec vi :)
Je ne participerais pas a ce debat sur les entraille du codage qui me depasse :)
Par contre,
Idéalement il faudrait utiliser const byte plutôt que int.
uint8_t est definni dans stdint.h et byte dans l'arduino mais tout deux corresponde a un unsigned char.
Donc l'utilisation de l'un ou de l'autre est équivalent sauf que byte évite au compilateur arduino de "modifier" le uint8_t en byte ? si ces ca, cela n'est valable que pour le codage via le compilateur arduino ?
uint8_t ça fait partie de la norme depuis C++11 (unsigned integer type with width of exactly 8 bits)
byte c'est un truc arduino pour dire uint8_t.. en gros il traine quelque part dans un des fichiers (https://github.com/esp8266/Arduino/blob/fcf2ac5d3de6c17fe177e16ccd1a2789826c2aa0/cores/esp8266/Arduino.h#L191) (Arduino.h) un truc du genre
typedef uint8_t byte;
donc si vous programmez dans l'environnement arduino vous pouvez utiliser byte sinon le bon usage c'est uint8_t
donc si vous programmez dans l'environnement arduino vous pouvez utiliser byte sinon le bon usage c'est uint8_t
(http://m.memegen.com/yczjym.jpg)
typedef uint8_t byte;
on etait pas loin
typedef uint8_t octet;
Oh purée...
Oui bon les vous c'est parce que je suis de la vieille école... ça vient tout seul plutôt que le tu...
Sinon j'écris en anglais et j'utilise "you" et que ça veut dire tu ou vous comme
you want
on etait pas loin
typedef uint8_t octet;
vous avez une version localisée du langage??? (si (tu|vous) clique(s|z) sur mon lien c'est bien
byte)
o ouai , laisse tomber ... j'ai le traducteur google qui ma traduit la page tout seul.
Quel boules
Sa me paraissait bizarre que t'es pas raison :smiley-grin: :smiley-grin: :smiley-grin:
J'ai un beug sur l'extraction de la carte sd alors que tout fonctionnait parfaitement
les declarations:
char ssid[maxSsid + 1];
char mdp[maxMdp + 1];
char numero2tel [maxnumero2tel + 1];
const uint8_t maxSsid = 30;
const uint8_t maxMdp = 30;
const uint8_t maxnumero2tel = 15;
l'appel de la fonction:
lisSurSd("ssid.txt", ssid);
lisSurSd("mdp.txt", mdp);
lisSurSd("numero2tel.txt", numero2tel);
la fonction :
void lisSurSd(char * nomDuFichierTxt, char *donneeLueSurSD) {
const uint8_t lectureMAX = 30;
char lecture [lectureMAX + 1];
myFile = SD.open(nomDuFichierTxt);
DPRINTF("Ouverture de :"); DPRINTLN(nomDuFichierTxt);
if (myFile) {
int i = 0;
while (myFile.available()) {
if (i <= lectureMAX) {
lecture[i] = myFile.read();
i++;
}
else {
lecture[i] = ('\0');
break;
}
}
strncpy(donneeLueSurSD, lecture, lectureMAX);
donneeLueSurSD[lectureMAX] = '\0';
} else {
DPRINTF("Erreur a l'ouverture du fichier ");
}
myFile.close();
}
et le resultat :
Extraction des infos presentes dans la carte SD...
Ouverture de :ssid.txt
Le fichier : ssid.txt a bien ete lu =testssid⸮⸮⸮⸮///
Ouverture de :mdp.txt
Le fichier : mdp.txt a bien ete lu =testmdp///
Ouverture de :numero2tel.txt
Le fichier : numero2tel.txt a bien ete lu =0678912345⸮⸮///
Pas de probleme avec testmdp
Mais testssid et 06** son completer par "⸮⸮" jusque a 13 caracteres
Pourquoi ? pourquoi pas testmdp ?
Les fichier sur sd son correct, pas d'espace ni saut de ligne après les caractères.
Je les ai tout de même effacer et recommencer 2 fois, sans changement.
ben oui... c'est normal - vous ne mettez pas le '\0' exactement à la fin de ce que vous avez lu quand le fichier contient moins de lectureMAX caractères... (vous en mettez bien un tout à la fin du tableau mais c'est pas suffisant, il faut qu'il soit pile à la fin de ce que vous avez lu)
en plus vous faites un strncpy(donneeLueSurSD, lecture, lectureMAX); sans vous assurer que ça rentre dans le tableau que vous avez reçu en paramètre (par exemple comme maxnumero2tel vaut 15 vous risquez de mettre le bazar dans la mémoire puisque lectureMAX vaut 30...
idéalement il faudrait passer à votre fonction la taille max du tableau et vous pouvez directement écrire dedans au lieu d'avoir un tableau intermédiaire qui prend de la mémoire sur la pile pour rien.
je ne suis pas sur mon mac mais je vais essayer de vous taper du code à tester
notez aussi qu'il vaut mieux pour la lisibilité déclarer les constantes de taille avant de les utiliser pour les tableaux
J'avais effectivement penser a la taille de numero2tel mais vu que le problème sur testssid je me suis dit que cela devait plutôt être un problème avec le caractere de fin. j'étais sur la bonne piste.
Se que je comprend pas du tout ces pourquoi cela a fonctionné correctement depuis des mois (la lecture/écriture fonctionnait parfaitement jusque a il y a deux jour :/)
notez aussi qu'il vaut mieux pour la lisibilité déclarer les constantes de taille avant de les utiliser pour les tableaux
Je n'ai pas fait le copier coller dans le bon sens.
Les const uint8_t se trouve dans mon .h alors que les char sont dans mon .cpp
tenez voici un bout de code à tester je ne suis pas sur mon ordi donc difficile à vérifier, il se peut qu'il y ait des fautes de frappe
déclaration des constantes:const uint8_t maxSsid = 30;
const uint8_t maxMdp = 30;
const uint8_t maxnumero2tel = 15;
déclaration des tableauxchar ssid[maxSsid + 1];
char mdp[maxMdp + 1];
char numero2tel [maxnumero2tel + 1];
Lecture des différents fichiersif (lisSurSd("ssid.txt", ssid, sizof(ssid)) == false) {
// on a un soucis !
}
if (lisSurSd("mdp.txt", mdp, sizof(mdp)) == false) {
// on a un soucis !
}
if (lisSurSd("numero2tel.txt", numero2tel, sizof(numero2tel)) == false) {
// on a un soucis !
}
La fonction de lectureboolean lisSurSd(const char * nomDuFichierTxt, char *donneeLueSurSD, size_t NbMaxCaracteres)
{
size_t indexEcritureMAX = NbMaxCaracteres - 1; // idéalement il faudrait tester si on nous appelle avec NbMaxCaracteres valant 0 et retourner une erreur dans ce cas là.. ici on sait qu'on ne fait pas de bêtise :)
boolean lectureOK = false;
DPRINTF("Ouverture de :"); DPRINTLN(nomDuFichierTxt);
myFile = SD.open(nomDuFichierTxt);
if (myFile) {
size_t i = 0;
donneeLueSurSD[0] = '\0'; // on initialise la chaîne à vide
while (myFile.available()) { // on lit l'intégralité du fichier, à concurrence de l'espace dispo dans notre buffer
if (i < indexEcritureMAX) {
donneeLueSurSD[i] = myFile.read(); // idéalement il faudrait tester si la lecture s'est bien passée
i++;
} else { // il reste des données dans le fichier mais on n'a plus de place
donneeLueSurSD[indexEcritureMAX] = '\0'; // donc on tronque ici
break; // et on arrête de lire le fichier
}
}
// il n'y a plus rien à lire dans le fichier
donneeLueSurSD[i] = '\0'; // on termine la chaîne proprement à cet endroit
lectureOK = true;
myFile.close();
} else {
DPRINTF("Erreur a l'ouverture du fichier "); DPRINTLN(nomDuFichierTxt);
}
return lectureOK;
}
Sa c'est mis a fonctionner malgré ne rien avoir toucher et les petit carre lu :smiley-confuse: :smiley-confuse: :smiley-confuse:
J'ai tout de même intégrer ton code et effectivement, beaucoup de faute ! :P
je pense que tu voulait sizeof mais même après cette modif j'avait une erreur de compile
J'ai remplacé par size_t
if (lisSurSd("ssid.txt", ssid, sizof(ssid)) == false) {
// on a un soucis !}
Et d'autre petit truc mais c'est OK.
J'avais essayer de récupérer la taille du char de destination mais je ne m'y suis pas pris comme cela.
Et j'ai pas encore assimiler de faire de boolean qui sont des fonctions.
euh oui sizof() --> sizeof()
J'ai remplacé par size_t
je ne sais pas où... size_t c'est un type qui représente une taille
Une fonction peut soit rien retourner, auquel cas on la déclare void, soit elle retourne quelque chose genre un booléen ou un entier ou un pointeur etc... et dans ce cas c'est le type que l'on met devant le nom de la fonction. ici je me suis dit que ce serait bien si l'appelant pouvait savoir si la lecture du fichier s'est bien passée, donc je retourne VRAI si c'est OK et FAUX sinon
ba oui mais non, c'est size_t puisque :
boolean lisSurSd(const char * nomDuFichierTxt, char *donneeLueSurSD, size_t NbMaxCaracteres){
sinon:
invalid application of 'sizeof' to incomplete type 'char []'
ba oui mais non, c'est size_t puisque :
boolean lisSurSd(const char * nomDuFichierTxt, char *donneeLueSurSD, size_t NbMaxCaracteres){
sinon:
invalid application of 'sizeof' to incomplete type 'char []'
je ne sais pas à quelle partie du code vous faites référence ...
dans la fonction boolean il y a "size_t NbMaxCaracteres"
boolean lisSurSd(const char * nomDuFichierTxt, char *donneeLueSurSD, size_t NbMaxCaracteres){
donc pour l'appeler il faut:
if (lisSurSd("ssid.txt", ssid, size_t(ssid)) == false) {
// on a un soucis !
}
et pas
if (lisSurSd("mdp.txt", mdp, sizof(mdp)) == false) {
// on a un soucis !
}
non ?
Non ;)
size_t (https://en.cppreference.com/w/cpp/types/size_t) c'est un type (en gros un unsigned int). donc dans la définition de la fonction on dit que le type de NbMaxCaractere représente une taille. et quand vous devez appeler la fonction vous devez donner la taille donc appeler avec
lisSurSd("mdp.txt", mdp, sizeof(mdp))
j'avais juste oublié le e dans sizeof
C'est la premiere chose que j'avais faite mais il y a erreur de compil
AQUABOUNS_V2_gsm:128: error: invalid application of 'sizeof' to incomplete type 'char []'
if (lisSurSd("ssid.txt", ssid, sizeof(ssid)) == false) {
^
AQUABOUNS_V2_gsm:132: error: invalid application of 'sizeof' to incomplete type 'char []'
if (lisSurSd("mdp.txt", mdp, sizeof(mdp)) == false) {
^
AQUABOUNS_V2_gsm:136: error: invalid application of 'sizeof' to incomplete type 'char []'
if (lisSurSd("numero2tel.txt", numero2tel, sizeof(numero2tel)) == false) {
comment les tableaux sont déclarés dans le fichier? si le compilateur à la compilation ne connait pas la taille ça ne fonctionnera pas effectivement. dans ce cas il faudra faire
lisSurSd("ssid.txt", ssid, maxSsid + 1)
et donner la taille directement
et comme la première chose que je fais dans la fonction c'est -1, autant pas faire le +1 à l'appel et dire que c'est directement le maxCar et enlever le -1 dans la fonction et appeler avec lisSurSd("ssid.txt", ssid, maxSsid)
donc la fonction devientboolean lisSurSd(const char * nomDuFichierTxt, char *donneeLueSurSD, size_t indexEcritureMAX)
{
boolean lectureOK = false;
DPRINTF("Ouverture de :"); DPRINTLN(nomDuFichierTxt);
myFile = SD.open(nomDuFichierTxt);
if (myFile) {
size_t i = 0;
donneeLueSurSD[0] = '\0'; // on initialise la chaîne à vide
while (myFile.available()) { // on lit l'intégralité du fichier, à concurrence de l'espace dispo dans notre buffer
if (i < indexEcritureMAX) {
donneeLueSurSD[i] = myFile.read(); // idéalement il faudrait tester si la lecture s'est bien passée
i++;
} else { // il reste des données dans le fichier mais on n'a plus de place
donneeLueSurSD[indexEcritureMAX] = '\0'; // donc on tronque ici
break; // et on arrête de lire le fichier
}
}
// il n'y a plus rien à lire dans le fichier
donneeLueSurSD[i] = '\0'; // on termine la chaîne proprement à cet endroit
lectureOK = true;
myFile.close();
} else {
DPRINTF("Erreur a l'ouverture du fichier "); DPRINTLN(nomDuFichierTxt);
}
return lectureOK;
}
au oui c'est plus simple et compréhensif comme ça.
Comme je l'ai deja dit, le code devient gros avec beaucoup de fonction.
La fonction qui lit les bouton de l'ecran a besoin d'au moins 2500 "loop"/10 secondes pour que l'écran réponde correctement.
Toute les fonctions d'affiler font 1300 loup/10 secondes.
après différent test J'ai donc fait ceci :
lireBoutonsNextion(); //boucle éffectuée : 45633 fois en 10 secondes soit : 0 millis par boucle
if (roulement <= 3000) {
rafraichirSiSurMenu();//boucle éffectuée : 57316 fois en 10 secondes soit : 0 millis par boucle
rafraichirHeureSiSurMenu();// boucle éffectuée : 40595 fois en 10 secondes soit : 0 millis par boucle
horaire(); //boucle éffectuée : 9372 fois en 10 secondes soit : 0 millis par boucle
rebootAlarme();//boucle éffectuée : 18753 fois en 10 secondes soit : 0 millis par boucle
tempeteAleatoireAutomatique();//boucle éffectuée : 9371 fois en 10 secondes soit : 0 millis par boucle
nourrissage();// boucle éffectuée : 9325 fois en 10 secondes soit : 0 millis par boucle
eclairage();//boucle éffectuée : 8247 fois en 10 secondes soit : 0 millis par boucle
brassage();// boucle éffectuée : 8451 fois en 10 secondes soit : 0 millis par boucle
oscillateur();//boucle éffectuée : 9195 fois en 10 secondes soit : 0 millis par boucle
ventilation();// boucle éffectuée : 9246 fois en 10 secondes soit : 0 millis par boucle
ph(); //boucle éffectuée : 7040 fois en 10 secondes soit : 0 millis par boucle
roulement++;
if (roulement == 1000) {
flotteurs();// boucle éffectuée : 1491 fois en 10 secondes soit : 0 millis par boucle
}
else if (roulement == 2000) {
temperature();// boucle éffectuée : 223 fois en 10 secondes soit : 0 millis par boucle
}
else if (roulement == 3000) {
messageAlerteTemperature(); // boucle éffectuée : 4674 fois en 10 secondes soit : 0 millis par boucle
roulement = 0;
}
}
j'arrive a 4663 boucle mais je ne trouve pas cela très propre.
J'ai tester avec des compteur millis mais cela nécessite plusieurs variable uint32_t donc de la place de stockage.
dans l'absolu chaque fonction devrait avoir son propre rythme de mise à jour.. par exemple ça sert sans doute à rien de lire le pH toutes les 15 millisecondes. une fois toutes les 5 minutes par exemple ça devrait être suffisant non?
donc oui il faudrait un uint32_t par fonction clé qui se souviendrait du dernier moment de déclenchement et l'appel à la fonction commencerait par faire un test pour voir si ça fait assez longtemps ou pas qu'on a appelé vraiment la fonction de mesure et si c'est trop récent ne rien faire.
si vous voulez gagner de la place dans vos DPRINT vous pourriez utiliser la macro F() non ? (j'ai vu plus haut que ce n'était pas le cas) - même si au final (j'espère) la macro quand on compile en mode non debug doit carément virer le texte, au moins pendant que vous testez vous aurez un peu plus de RAM de dispo
Effectivement certaine fonction son moins importante ou ne nécessite pas une réaction instantané.
Je voulait éviter de crée plusieurs variable car je commence vraiment a être bloqué coté mémoire malgré toute les améliorations et changement que j'ai fait.
Le compilateur indique un risque de beug au dela de 75% de mémoire utilisé.
Cette limite est elle vraiement a ne pas depasser ?
Pour le DPRINTF, je ne peut l'utiliser que pour DPRINTF("nananere") et j'utilise DPRINT pour les variables.
quand je désactive debeug je gagne 1%
Le compilateur indique un risque de beug au dela de 75% de mémoire utilisé.
Cette limite est elle vraiement a ne pas depasser ?
Tout dépend de votre programme et de combien de place vous utilisez dynamiquement (tous ces tableaux temporaires par exemple celui que j'ai viré dans le code ci dessus c'est toujours ça de pris)
tous ces tableaux temporaires par exemple celui que j'ai viré dans le code ci dessus c'est toujours ça de pris
:smiley-confuse: j'en est fait a toute les fonction répétitive (brassage 1,2,3, éclairage 1,2,3,4, oscillo 1,2,3, nourrissage 1,2
Tant que ça rentre. ....
Oui lol mais je suis revenu a 80% est reste le wifi et divers petites chose alors vu que l'automate tourneras H24 il ne faut pas que ca beug
Insérez du code qui vérifie la mémoire restante (https://github.com/McNeight/MemoryFree) et regardez ce que ça donne
sa indique 1463 libre au lieu des 1524 du compilateur.
Par contre ces fix j'ai déclencher plein de fonction via bouton ou l'écran, envoyer des SMS et toujours 1463.
C'est sensé évoluer avec l'utilisation ?
Bonjour
Si tu veilles bien à laisser TOUTES les chaînes de caractère en flash (via progmem), les 8 ko de RAM d'une mega doivent être largement suffisants pour un programme de cette nature.
75% de RAM occupée dès la compil me laisse à penser que la gestion des chaînes est perfectible.
C'est effectivement un peu limite (à voir selon les besoins temporaires de chaque fonction).
Indépendamment de ce point, il faut inclure une gestion du watchdog dans ton automate, si ce n'est pas déjà le cas.
C'est un filet de sécurité contre toutes sortes d'aléa (bug ou autre), qui déclenchera un reboot du système au moindre problème.
Même avec le meilleur programme du monde, cette sécurité est indispensable pour le pilotage d'un aquarium, pour ne prendre aucun risque avec la vie de tes hôtes à écailles ou polypes.
Bonjour bricoleau
concernant la mémoire, je l'ai vu s'envoler lorsque j'ai fait toute les déclarations lier a l'écran.(je ne parle que de la déclaration, exemple pour déclarer l'emplacement d'un bouton ou d'une variable sur l'écran)
Si j'ai bien compris la bibliothèque, tout est en string.
C'est ça qui prend beaucoup de place ?
Pour la gestion des chaines, j'ai fait cela:
// **************************************** declarations
uint8_t pwmEclairage[4];
uint32_t debutleverSoleil[4];
uint32_t finleverSoleil[4];
uint32_t debutcoucherSoleil[4];
uint32_t fincoucherSoleil[4];
uint8_t eclairageOnOffPwm[4] {pwm, pwm, pwm, pwm};
void eclairage() { // *******************************************************************************************************************************************
for (uint8_t numeroEclairage = bleu1; numeroEclairage <= blanc2; numeroEclairage++) {
if (eclairageOnOffPwm [numeroEclairage] == Arret) {
pwmEclairage[numeroEclairage] = zero;
analogWrite(pinOutEclairage[numeroEclairage], zero);
//DPRINTF("OFF"); DPRINTF(" // ");DPRINT(Heure);DPRINTF("h");DPRINT(minut);DPRINTLN();
}
else if (eclairageOnOffPwm [numeroEclairage] == Actif) {
pwmEclairage[numeroEclairage] = sauvegarde.puissanceMaxEclairage[numeroEclairage];
analogWrite(pinOutEclairage[numeroEclairage], sauvegarde.puissanceMaxEclairage[numeroEclairage] * pourCentEnPwm);
// DPRINTF("ON"); DPRINTF(" // ");DPRINT(Heure);DPRINTF("h");DPRINT(minut);DPRINTLN();
}
else if (eclairageOnOffPwm [numeroEclairage] == pwm) {
if ((Time >= debutleverSoleil[numeroEclairage]) && (Time < finleverSoleil[numeroEclairage])) {
pwmEclairage[numeroEclairage] = map(Time, debutleverSoleil[numeroEclairage], finleverSoleil[numeroEclairage], zero, sauvegarde.puissanceMaxEclairage[numeroEclairage]);
analogWrite (pinOutEclairage[numeroEclairage], pwmEclairage[numeroEclairage] * pourCentEnPwm);
//DPRINTF("pwm monte");DPRINT(numeroEclairage); DPRINTF(" // ");DPRINT(Heure);DPRINTF("h");DPRINT(minut);DPRINTLN();
}
else if ((Time >= debutcoucherSoleil[numeroEclairage]) && (Time < fincoucherSoleil[numeroEclairage])) {
pwmEclairage[numeroEclairage] = map(Time, debutcoucherSoleil[numeroEclairage], fincoucherSoleil[numeroEclairage], sauvegarde.puissanceMaxEclairage[numeroEclairage], zero);
analogWrite (pinOutEclairage[numeroEclairage], pwmEclairage[numeroEclairage] * pourCentEnPwm);
//DPRINTF("pwm baisse"); DPRINT(numeroEclairage); DPRINTF(" // ");DPRINT(Heure);DPRINTF("h");DPRINT(minut);DPRINTLN();
}
else if ((Time >= finleverSoleil[numeroEclairage] ) && (Time < debutcoucherSoleil[numeroEclairage])) {
pwmEclairage[numeroEclairage] = sauvegarde.puissanceMaxEclairage[numeroEclairage];
analogWrite(pinOutEclairage[numeroEclairage], sauvegarde.puissanceMaxEclairage[numeroEclairage] * pourCentEnPwm);
//DPRINTF("pwm full"); DPRINT(numeroEclairage); DPRINTF(" // ");DPRINT(Heure);DPRINTF("h");DPRINT(minut);DPRINTLN();
}
else {
pwmEclairage[numeroEclairage] = zero;
analogWrite(pinOutEclairage[numeroEclairage], zero);
//DPRINTF("pwm zero");DPRINT(numeroEclairage); DPRINTF(" // ");DPRINT(Heure);DPRINTF("h");DPRINT(minut);DPRINTLN();
}
}
}
}
Pour se que tu nome "watchdog" j'avait cette idée derrière la tète, plus précisément, j'imaginais que le wemos d1 mini pro puisse reboot l'arduino si bug et vis versa.
C'est juste que je garde sa pour le fin pour ne pas m'éttaler partout a la fois (j'ai déjà tendance a trop le faire)
Non dans ce bout de code je ne vois rien qui cloche au niveau RAM.
75% occupé à la compil sur une mega, cela veut dire qu'il y a près de 6000 octets réservés en permance, c'est qui est énorme.
Dans ces 6000 octets on trouve :
1) tes variables globales
2) les chaines de caractères utilisées sans progmem <= c'est là qu'est le problème la plupart du temps
3) les variables statiques internes aux bibliothèques utilisées (par ex environ 70 octets pour Serial). La lib du LCD est peut-être très gourmande?
Pour le watchdog, pas besoin d'un wemos, c'est une fonctionnalité intégrée au hardware de l'arduino.
Le principe est tout simple : le watchdog est comme un compte à rebours qui déclenche le reboot lorsqu'il arrive à zéro. Pour éviter le reboot, il faut venir régulièrement le réinitialiser.
Concretement :
1) dans setup() tu actives le watchdog avec une durée (par exemple maximum 8 secondes)
2) dans loop() tu fais un reset systématique du watchdog
ainsi, s'il s'écoule plus de 8 secondes entre deux exécutions de loop(), l'arduino effectue un reboot automatique.
Je suis content de voir que je n'est pas fait d'erreur grossière d'écriture dans mes fonctions :) je devient bon ... ou pas lol
Comment savoir si la bibliothèque de l'écran nextion est gourmande ?
En tout cas un déclaration d'un champs de l'écran c'est 72 octets de stockage et 12 octets de mémoire dynamique.
j'ai 141 déclarations donc 10152 octets de stockage et 1692 de mémoire dynamique juste pour les déclarations.
"Pour 2) les chaines de caractères utilisées sans progmem" il faut que je me renseigne la dessus, je connait pas, comme d'hab :/ je voudrait finir d'inclure les SMS et mes flotteurs avant.
Pour le "watchdog" sa a l'air simple ... unpeu trop d'ailleurs ...
Pour la mémoire imprimez là en sortie de vos fonctions - ou en début mais juste après la déclaration des variables locales.
Pour la mémoire imprimez là en sortie de vos fonctions - ou en début mais juste après la déclaration des variables locales.
Ok je vais faire ca avec un bout de qui garderas en mémoire la plus petite mémoire mesuré
Le problème, c'est que cela ne prendra pas en compte les allocations de RAM temporaires effectuées par les fonctions des bibliothèques utilisées.
Néanmoins cela donne quand même une indication. Il faudra juste veiller à garder assez de RAM dispo pour ces fonctions de bas niveau.
Il me semble avoir vu des outils plus subtils, qui initialisent le contenu de la RAM disponible avec une valeur spécifique, pour ensuite aller regarder jusqu'où elle a été temporairement utilisée.
Mais bon là vu les symptômes, pas la peine d'investir du temps dans un thermomètre trop sophistiqué.
Il vaut mieux essayer directement de comprendre où sont les 6000 octets de RAM alloués dès le départ.
Pour le watchdog, voici un petit bout de code très démonstratif //Test du watchdog
#include <avr/wdt.h>
void setup() {
Serial.begin(9600);
Serial.println("Debut");
//Activation du watchdog
//15mS WDTO_15MS
//30mS WDTO_30MS
//60mS WDTO_60MS
//120mS WDTO_120MS
//250mS WDTO_250MS
//500mS WDTO_500MS
//1S WDTO_1S
//2S WDTO_2S
//4S WDTO_4S
//8S WDTO_8S
wdt_enable(WDTO_8S);
//Attention :
//Certaines cartes sont livrées avec un bootloader qui ne désactive pas le watchdog
//Résultat : suite au premier reboot, le watchdog peut se redéclencher perpétuellement alors même que l'exécution du bootloader n'est pas terminée
//Solution : mettre la carte hors puis sous tension pour sortir de la boucle infernale, puis mettre à jour le bootloader de la carte.
}
void loop() {
wdt_reset(); //instruction à exécuter régulièrement pour éviter le reboot automatique
//reboot déclenché manuellement via le terminal série
if (Serial.available()) {
for (int i=0; i<20; i++) {
Serial.println(i);
delay(1000);
}//La boucle dure 20 secondes => trop long pour le watchdog
}
}
je me suis fait discret car je me tire les cheveux sur mes mesure PH.
Enfaîte, tout fonctionnais.
Mais il est ou le problème alors ? :o
Et bien c'est un problème hardware. Lorsque l'écran est allumer (650mAh), il y a un petit chute de tension sur le PCB 0.15v qui ne pause aucun problème de fonctionnement a quiconque.
ROoo mais il est ou le problème alors ? :o
Et bien, lorsque l'ecran Nextion passe en veille, hop, la chute de tension disparait ... et hop ... ~+0.30 dans la mesure PH.
Dans un premier temps j'en cru que la variation de tension qui alimente la carte PH était a la base du problème j'ai donc alimenter cette carte avec le 12v via un l78l05.
Malgré la tension constante j'ai retrouver les même variation de mesure.
Donc cette variation doit en faite affecter l'arduino qui du coup fait fluctuer les valeur mesuré.(l'arduino est alimenter en 5v, pas par le Vin)
La solution que j'envisage est donc d'alimente l'écran via le 12v via un TSR 1-2450 comme sa il n'y auras plus de chute de tension sur le 5v.
Pour se qui est de la stabilité des mesures (~0.08 de variation entre les mesure), j'ai testé plusieurs méthode dont certaine utilisant de la mémoire, j'ai fini par opter pour du simple, efficace et peu gourmand.
Le loop lance la fonction PH.
Celle ci mesure un coupe sur deux le PH bac puis PH rac.
je multiplie le resultat précédent par 9 et ajoute la nouvelle mesure.
Puis divise par 10.
Les mesure sont stables et cela ne (devrait pas) pause pas de problème puisque il n'y a pas de gros changement brusque a mesurer.
// **************************************** PH
void ph() {
// **************************************** recuperation ph
if (switchPhRacBac == true) {
recupererPh(pinInPhBac, phBac);
phBac = moyennePh;
DPRINTF("ph bac : "); DPRINT(phBac); DPRINTF(" // "); DPRINT(Heure); DPRINTF("h"); DPRINT(minut); DPRINTLN();
}
else if (switchPhRacBac == false) {
recupererPh(pinInPhRac, phRac);
phRac = moyennePh;
DPRINTF("ph rac : "); DPRINT(phRac); DPRINTF(" // "); DPRINT(Heure); DPRINTF("h"); DPRINT(minut); DPRINTLN();
}
void recupererPh(const byte pin, float moyennePrecedente) {
uint16_t mesurePh;
mesurePh = map(analogRead(pin), 774, 595, 401, 918);
switchPhRacBac = !switchPhRacBac;
moyennePh = (mesurePh / 100);
moyennePh = ((moyennePrecedente * 9) + moyennePh) / 10;
}
Je fini de faire mes fonction flotteur et les alerte SMS associé.
Apres je me met sur la mémoire (freeMemory) et a chercher se qui me prend autant de place.
(si vous connaissez un forum d'électronique avec des gas aussi sympa qu'ici ça m'intéresse pars que soit personne ne répond soit on se fait mal parlé :( )
Comment fonctionne la lecture de pH ? C'est sur une pin analogique ? dans ce cas la variation de tension d'entrée de la carte peut jouer sur la référence analogique si vous êtes resté sur le défaut
alors la carte est sur pin analogique mais :
Il n'y a aucun datasheet pour cette carte donc difficile de connaitre toute les caractéristiques. Les composant visible son des amplificateur lm358 et un 4502c.
C'est la première chose que j'ai envisager, que cette chute de tension affecte directement la carte. mais après l'avoir alimenté séparément j'ai constater exactement les même écart donc (je me demande si) l'arduino étant alimenter en 4.80v quand l'écran est allumer, prend pour "référence 5v" 4.8v. (?)
Salut
Utiliser la référence interne 1.1V de l'ADC peut être une solution. Par contre elle n'est pas très précise, 10% je crois, mais probablement plus stable.
Sinon, une référence externe genre LM4040.
@+
Salut hbachetti,
La carte ph retourne ~3.7v (donc y va y avoir un problème si la plage max est 1.1v ?)
Pour le lm4040 si j'ai bien compris c'est pour mettre sur AREF et il faut appeler AR_EXTERNAL
Cette solution semble faisable.
Mais ne faut'il pas éviter cette chute de tension en alimentent l'écran différemment plutôt que remédier au consequance ?
Bien sûr, dans le cas où on utilise la ref interne, il faut diviser la tension mesurée afin qu'elle soi inférieure à 1.1V.
Utiliser un pont diviseur résistif, donc, avec de préférence des résistances à film métallique, pour la stabilité en température.
Mais ne faut'il pas éviter cette chute de tension en alimentent l'écran différemment plutôt que remédier au consequance ?
Je n'ai pas suivi le sujet depuis le départ, et je ne sais pas d'où sort ton 5V. Est-il stable dans le temps et en fonction de la température ?
@+
le pcb est alimenter par une alimentation 5v 3A.
Je n'ai pas remarqué de variation avec la température (a par la chute de tension du a l'écran allumé)
je me suis fait discret car je me tire les cheveux sur mes mesure PH.
Lorsque l'écran est allumer (650mAh), il y a un petit chute de tension sur le PCB 0.15v qui ne pause aucun problème de fonctionnement a quiconque.
mais lorsque l'ecran Nextion passe en veille, hop, la chute de tension disparait ... et hop ... ~+0.30 dans la mesure PH.
Dans un premier temps j'en cru que la variation de tension qui alimente la carte PH était a la base du problème j'ai donc alimenter cette carte avec le 12v via un l78l05.
Malgré la tension constante j'ai retrouver les même variation de mesure.
Donc cette variation doit en faite affecter l'arduino qui du coup fait fluctuer les valeur mesuré.(l'arduino est alimenter en 5v, pas par le Vin)
La solution que j'envisage est donc d'alimente l'écran via le 12v via un TSR 1-2450 comme sa il n'y auras plus de chute de tension sur le 5v.
Une alimentation dont tu connais les caractéristiques?
:( a part 5v 5A(rectif 5A pas 3A)
celui ci (https://www.amazon.fr/gp/product/B073RJL3VW/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1)
et pareil pour le 12V 3A je n'en sait pas plus
(mais a mon niveau je ne vois pas se qu'il faudrait savoir :( )
Connaître la stabilité, surtout en température serait un plus.
Tu devrais essayer tout de même de faire la mesure en utilisant la référence interne 1.1V.
Ce n'est pas une grosse modification et cela apportera probablement une meilleure stabilité.
Pense à découpler la référence interne à l'aide d'un condensateur 1µF sur la pin AREF.
@+
Tu devrais essayer tout de même de faire la mesure en utilisant la référence interne 1.1V.
Je vais essayer de mieux comprendre avant de tenter ce branchement pars que :
you must set the analog reference to EXTERNAL before calling analogRead(). Otherwise, you will short together the active reference voltage (internally generated) and the AREF pin, possibly damaging the microcontroller on your Arduino board.
m'inquiète un peu.
j'ai l'impression que toute les pin analogique vont être affecter par cette référence.
Donc les autre pin vont aussi avoir cette référence et ne plus émettre ou recevoir de 5v mais plutôt cette référence ?
Ce n'est pas une grosse modification et cela apportera probablement une meilleure stabilité.
Pense à découpler la référence interne à l'aide d'un condensateur 1µF sur la pin AREF.
tout comme en codage je découvre alors faut le temps que je comprenne (et que j'ai se qui faut sous la main)
you must set the analog reference to EXTERNAL before calling analogRead(). Otherwise, you will short together the active reference voltage (internally generated) and the AREF pin, possibly damaging the microcontroller on your Arduino board.
Si tu utilises la référence interne 1.1V, il faut appeler analogReference(
INTERNAL) et non pas analogReference(
EXTERNAL).
j'ai l'impression que toute les pin analogique vont être affecter par cette référence.
Pas forcément.
analogReference(INTERNAL);
analogRead(A0);
int sensorValue = analogRead(A0);
analogReference(DEFAULT);
analogRead(A1);
int sensorValue = analogRead(A1);
N'oublie pas qu'à chaque changement d'entrée, il faut faire une lecture à vide afin de stabiliser la charge de la capacité de l'entrée.
@+
Les boutons son fini et les SMS associer aussi
Reste a voir si il y a d'éventuel bug.
Pour la mémoire, j'ai mis freememory dans les fonctions et un fonction qui enregistre la plus faible et plus haute mémoire disponible.
resultat:
memoire ici : 1359 // memoire min : 1322 // memoire max : 1359
contrairement a tout se que j'aurais penser c'est la fonction éclairage qui utilise le plus la mémoire.
Mais la variation est tout de même limité, 37 octets.
Donc le "problème" vient d'ailleurs ....
J'essai de comprendre et faire le montage avec la référence intern 1.1v
j'ai pas compris ca :
Pense à découpler la référence interne à l'aide d'un condensateur 1µF sur la pin AREF
Je met le condo sur AREF et GND ?
d'après se que j'ai trouvé sur le net le condo est a mettre entre AREF et GND.(je n'avais qu'un condo 474)
Pour le pont diviseur j'ai fait vcc>r6k8 et gnd r2k2 , la sortie de la carte ph est 3.7v max
J'ai ensuite étalonner la sonde pour arriver a des mesure cohérente.
Je remarque de suite des écarts plus important dans les mesure ~0.15 au lieu de ~0.8 avant.
Je lance les mesure puis branche l'écran (entre mesure 81 et 82) et :
mesure n°80
ph actuel : 920.00
ph bas: 909.00
ph haut: 994.00
ph moyen: 950.45
mesure n°81
ph actuel : 915.00
ph bas: 909.00
ph haut: 994.00
ph moyen: 950.01
mesure n°82
ph actuel : 833.00
ph bas: 833.00
ph haut: 994.00
ph moyen: 948.59
mesure n°83
ph actuel : 855.00
ph bas: 833.00
ph haut: 994.00
ph moyen: 947.46
Le problème reste le même et est encore plus accentué puisque j'avais des variation ~0.3 et maintenant ~0.8
Par contre, j'ai appris le pont diviseur :smiley-grin: :smiley-cool:
Cela me paraît insensé que la référence interne soit si sensible à une chute de tension d'alimentation.
Je savais qu'elle était imprécise - entre 1V et 1.2V - mais je la croyais stable.
Il faut que je fasse un essai ce soir.
Une solution à base de référence de tension LM4040 ? ou éventuellement TL431.
@+
Personnellement quand je veux de la précision j'utilise ceci :
ADS1115 (https://fr.aliexpress.com/item/16-Bit-IIC-I2C-4-Channel-ADS1115-Module-ADC-with-Pro-Gain-Amplifier-For-Arduino-2V/32639483684.html?spm=a2g0s.9042311.0.0.74816c37ZhTrnP)
Il y a 4 voies analogiques, résolution 16 bits et cela se lit en I2C.
@+
Mais ne crois tu pas que régler le problème de base (séparer l'écran de l'alimentation 5v) serait préférable ?
Parsque si l'ecran ne bouge pas (reste allumer ou eteind) le problème n'est pas présent.
A essayer. Il faut absolument un 5V très stable.
ADS1115 (https://fr.aliexpress.com/item/16-Bit-IIC-I2C-4-Channel-ADS1115-Module-ADC-with-Pro-Gain-Amplifier-For-Arduino-2V/32639483684.html?spm=a2g0s.9042311.0.0.74816c37ZhTrnP)
Je vais tenter ...
il faut inclure une gestion du watchdog dans ton automate,
J'ai commencer a tester et bien sur je suis tomber sur une carte avec un vieux bootloader.
Heureusement, j'en est d'autre ou ça marche parfaitement.
C'est d'une simplicité ... si tout pouvais être comme sa.
J'ai eu une idée (non non peut etre pas de génie :smiley-evil:),
Le problème avec la carte ph et le pont diviseur ces que la carte ph a une tension qui fluctue ~1v entre ph4 et ph9
Avec le pont diviseur cette tension et INTERNAL1V1 cette variation de tension est diviser par 5.
Cela fait des palier de 0.07ph
Ce matin j'ai donc fait :
analogReference(EXTERNAL); en relient au 3.3v de l'arduino
Fait un pont diviseur "plus faible" de
3.7v a 3.2v
J'ai des palier de ~0.02ph et joint au code le resultat est assez stable.
L'idéal serait d'avoir une référence externe de 3.7-4v pour ne pas avoir de pont diviseur et avoir l'échelé original de la carte ph.
Et si j'utilisait un L78L5 (via 12v) pour alimenter AREF ? j'aurais une tension stable ...
La résistance variable sur la carte ph permet de descendre a 3.05v avec un ph 4.00
La tension diminue lorsque le ph monte donc pas de risque de dépasser 3.3v
du coup pas besoin de pond diviseur
toujours des palier de 0.02ph mais beaucoup plus stable
Par contre, les mesure devienne instable quand l'arduino n'est plus brancher par usb.
Je suppose que cela est du au fait que l'arduino est alimenter par le 5v au lieu du vin ou du jack.
Ton alimentation 5V n'est pas stable.
Effectivement si tu alimentes en 12V sur VIN ou JACK, le régulateur 5V de la carte fournira une tension de meilleure qualité.
Inutile de mettre du 5V sur AREF. Ceci revient au même :
analogReference(DEFAULT);
@+
quand je test les mesure ph sans tout le reste du montage pas de problème mais je pense que le fait que l'arduino et tout les élément sur le pcb soit sur la même alimentation génère c'est variation (exemple quand le GSM émet un sms)
le 12v est dédier a la partie relais pour ne pas avoir de parasite c'est pour cette raison que je ne m'en sert pas pour alimenter l'arduino.
Si je prend un petit régulateur 5v>3.3v pour brancher AREF je devrait avoir des mesures stable.
Je commence a remettre les pied dans le html et le wemos d1 mini ...
Une référence de tension serait préférable à un régulateur.
(http://www.oubouger.fr/_oubouger/static/upload/vig_detail/vig_ev_Pouce.jpg)
c'est fou la variante de composant qu'il peut exister.
On se rend vite compte que c'est un metier :/
j'ai trouver le MCP1541-I/TO qui délivre 4.096v
Il n'y auras plus aucun risque même avec la carte ph au max a 3.7v
Le montage semble identique a un régulateur, Vin gnd Vout, condo avant et après.
Vous avez aussi un moteur de recherche sur Texas (http://www.ti.com/power-management/voltage-reference/series-voltage-reference/products.html#p2352max=0.05)
J'ai recu le MCP1541-I/TO qui délivre la tension de référence sur AREF.
Les mesure son stable.
Et quand l'écran passe en veille, les mesure s'affole :(
J'ai testé d'alimenté les carte ph sur source externe en plus
Meme resultat :(
il me reste a tester avec le ads1115
Et c'est ragent car mis a par cela je pense avoir fini toute la partie fonction et ecran. je laisse tourner pour "découvrir" d'éventuel bug mais rien pour le moment :)
Quid des masses - sont elles bien communes?
quand vous dites
Et quand l'écran passe en veille, les mesure s'affole
--> est-ce que vous voyez cela par programme Arduino ou simplement en branchant un voltmètre sur la sonde ?
alors concernant les masses c'est une grande question a la quelle je n'est pas obtenu de réponse.
A la base, les alimentation 5v et 12v sont séparer pour qu'il n'y est pas de parasite du au relais.
Donc arduino > r220 > pc817 > r1k > uln2803 > diode > relais 12v
Jusque la les masse séparé.
J'ai fait une carte test avec les masse commune. Je n'est pas vu (visuellement sur ecran et moniteur série) de conséquence de parasite. Par contre, lorsque le 5V est couper et alimenter en 12V , la LED qui indique l'alimentation 5V etait mi allumée ...
Pour les mesures c'est avec le moniteur série.
Mais j'ai également la constatation sur l'écran nextion a sa sortie de veille (quand non brancher en usb pour avoir le moniteur série).
J'ai déjà lors des test précédent brancher le multimètre sur la carte ph sans qu'il y est de fluctuation significative du au passage allumer/veille de l'écran
A quoi pense tu ?
J'ai recu le MCP1541-I/TO qui délivre la tension de référence sur AREF.
Les mesure son stable.
Et quand l'écran passe en veille, les mesure s'affole :(
J'ai testé d'alimenté les carte ph sur source externe en plus
Meme resultat :(
il me reste a tester avec le ads1115
Et c'est ragent car mis a par cela je pense avoir fini toute la partie fonction et ecran. je laisse tourner pour "découvrir" d'éventuel bug mais rien pour le moment :)
J'ai changer d'arduino et de carte ph, refait tout les branchements.
Depuis 30 min j'ai des resultat stable ...
Dodo pour aujourd'hui.
Demain je laisse tourner pour être sur ...
:D ;D sa tourne depuis ce matin.
J'ai juste ajuster la valeur basse ph4.01 et haute ph9.18
quand je mesure une solution 6.86, je trouve 6.87 8)
J'ai donc voulu aller plus loin.(me mettre en temps qu'utilisateur final pour voir si juste en ajustant le potentiomètre de la carte ph on retrouvait bien la bonne valeur haute et basse.)
J'ai changer de sonde PH pour voir.
c'est très long a se stabiliser ( du la résistance variable ?)
j'arrive a bien avoir 4.01 et 9.18 et j'ai 6.85 au lieu de 6.86.
Donc l'on peut dire que ce montage + code est concluant :smiley-cool:
Merci hbachetti pour tes conseilles pour ce montage.
et pour faciliter tout ça, j'ai ajouter un bouton pour l'étalonnage. (https://www.youtube.com/watch?v=YwYlPJ3uWxg&feature=youtu.be)
Lorsque l'étalonnage est activé, les mesure sont prise a chaque loop et toute les 500 millis l'écran est mise a jour.
Mais quelle est la conclusion ?
Carte PH défectueuse ?
Non pas de carte ph défectueuse.
J'ai retourner le problème dans tout les sens.
Les branchement était bon donc a par un faut contact sur la platine d'essai ou l'un des fils de montage, je vois pas.
cc tout lmonde,
Depuis se matin je regarde le fonctionnement de progmem.
J'ai réussi a modifier mes déclarations pour ne pas avoir d'erreur.
La mémoire qui etait utiliser bascule bien sur la mémoire flash
declarations
const char alerte[] PROGMEM = "!!! ALERTE !!!";
const byte maxbufferTexte = 30;
char bufferTexte[maxbufferTexte + 1];
puisque lorsque je veut utiliser le char*
strncpy_P (bufferTexte,alerte, maxbufferTexte);
Lors de la première utilisation de la fonction tout est ok.
Ensuite, c'est du n'importe quoi :(
Si j'ai bien compris il faut "reconstruire la chaîne" en faisant par exemple for (int i =0; i==maxbufferTexte; i++)
Mais même avec l'aide de ceci (https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga963f816fc88a5d8479c285ed4c630229)
Je ne ni arrive pas
et avec l'aide de ceci (https://www.arduino.cc/reference/en/language/variables/utilities/progmem/) ?
Lors de la première utilisation de la fonction tout est ok.
Ensuite, c'est du n'importe quoi :(
ça veut dire quoi 'ensuite' ?
ça veut dire quoi 'ensuite' ?
Lorsque la fonction contentent
strncpy_P (bufferTexte,alerte, maxbufferTexte);
est re demandé après la première fois.
l'exemple arduino est le premier que j'ai lu mais lorsque je test :
const PROGMEM uint16_t charSet[] = { 65000, 32796, 16843, 10, 11234};
// save some chars
const char signMessage[] PROGMEM = {"I AM PREDATOR, UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};
unsigned int displayInt;
int k; // counter variable
char myChar;
void setup() {
Serial.begin(74880);
while (!Serial); // wait for serial port to connect. Needed for native USB
// put your setup code here, to run once:
// read back a 2-byte int
for (k = 0; k < 5; k++)
{
displayInt = pgm_read_word_near(charSet + k);
Serial.println(displayInt);
}
Serial.println();
// read back a char
for (k = 0; k < strlen_P(signMessage); k++)
{
myChar = pgm_read_byte_near(signMessage + k);
Serial.print(myChar);
}
Serial.println();
}
void loop() {
// put your main code here, to run repeatedly:
}
j'ai dans la console
65000
32796
16843
10
11234
I AM PREDATOR, UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART
Se que je ne comprend pas c'est pourquoi je n'est qu'un ligne "
I AM PREDATOR, UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"
alors que je j'ai un serial.print a chaque loop du "K" temps que "K" est inférieur au nombre de caractere de "signMessage"
for (k = 0; k < strlen_P(signMessage); k++)
{
myChar = pgm_read_byte_near(signMessage + k);
Serial.print(myChar);
}
il n'y a pas de raison....
essayez de faire tourner cela, vous verrez qu'on peut le copier autant de fois qu'on veut...
const char alerte[] PROGMEM = "!!! ALERTE !!!";
const byte maxbufferTexte = 30;
char bufferTexte[maxbufferTexte + 1];
unsigned long n = 0;
void setup() {
Serial.begin(115200);
}
void loop() {
strncpy_P (bufferTexte, alerte, maxbufferTexte);
Serial.print(n++);
Serial.print(F("\t"));
Serial.println(bufferTexte);
delay(100);
}
j'ai dans la console
I AM PREDATOR, UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART
Ce que je ne comprend pas c'est pourquoi je n'est qu'un ligne "
I AM PREDATOR, UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"
alors que je j'ai un serial.print a chaque loop du "K" temps que "K" est inférieur au nombre de caractere de "signMessage"
for (k = 0; k < strlen_P(signMessage); k++)
{
myChar = pgm_read_byte_near(signMessage + k);
Serial.print(myChar);
}
euh pas compris... c'est pas dans la loop, c'est dans le setup.. donc fait qu'une seule fois
k parcours la totalité des caractères en mémoire flash dans la chaine
signMessage et les imprime un par un...
il n'y a pas de raison....
essayez de faire tourner cela, vous verrez qu'on peut le copier autant de fois qu'on veut...
const char alerte[] PROGMEM = "!!! ALERTE !!!";
const byte maxbufferTexte = 30;
char bufferTexte[maxbufferTexte + 1];
unsigned long n = 0;
void setup() {
Serial.begin(115200);
}
void loop() {
strncpy_P (bufferTexte, alerte, maxbufferTexte);
Serial.print(n++);
Serial.print(F("\t"));
Serial.println(bufferTexte);
delay(100);
}
effectivement :(
euh pas compris... c'est pas dans la loop, c'est dans le setup.. donc fait qu'une seule fois
k parcours la totalité des caractères en mémoire flash dans la chaine signMessage et les imprime un par un...
ba oui je suis d'accord.
Le fait de voir la phrase s'afficher en en une fois dans le moniteur ma perturbé!
L'arduino est plus rapide que moi :)
oui je parlait de loop mais je parlait fait dans le "for" je ne vois pas comment le nommer autrement.
L'arduino est plus rapide que moi :)
LOL :)
a ba on se moque maintenant :smiley-razz: :smiley-evil: ...
Il y avait plusieurs fonction qui utilisant ce "char" et ne les aillant pas toute modifier le problème venait de la.
Donc selon la fonction appeler il y avait une erreur au lieu du même message qui avait fonctionné précédemment ... (et non pas la même fonction qui "bugait" après premier utilisation)
Il y a tellement de ligne et de fichier que ça devient compliquer a la moindre modification.
(~3250 lignes dans 15 fichiers sans compter les .h)
Je poursuis le remplacement en progmem ...
Ok !
dsl y a vraiment des fois ou jvous embête pour rien :smiley-confuse:
sa pic les yeux :o ...
Mais sa en valait le coup :smiley-razz:
Mémoire de stockage :
Utilisé avant : 70896 octets, après 71970 octets, delta 1074 octets
Les variables :
Utilisé avant : 6730 octets, après 5360 octets, delta 1370 octets , 65% au lieu de 82%
:smiley-money:
Bravo!
Il y a d'autre variable que je peut mettre en flash ? Les "const ***" ?
Depuis hier je commence a mettre a jour tout les commentaires
Certaines informations sont nécessaires pour les futurs utilisateurs afin que le montage + bibliothèque soit en osmose avec le code.
Voici la première page ci jointe.
La mise en page est correcte ? lisible ? compréhensif ?
Non les variables entières type const byte ou const int etc ne vous prennent sans doute aucune place et sont directement remplacé dans le code à la compilation
Je suis sur l'on iPhone - peux pas lire le .ino facilement
Non les variables entières type const byte ou const int etc ne vous prennent sans doute aucune place et sont directement remplacé dans le code à la compilation
Ok
Le croquis utilise 59850 octets (23%) de l'espace de stockage de programmes.
Les variables globales utilisent 5263 octets (64%) de mémoire dynamique
Ça parais plus coherent avec la taille du code ou je doit encore chercher du "gras" ?
Je suis sur l'on iPhone - peux pas lire le .ino facilement
J'espère que tu profite du soleil au moins :smiley-cool:
j'essai de regarder depuis quelque temps le fonctionnement du wemos d1 mini.
J'arrive a téléverser dans le wemos et a accéder a une page web.
Se qu'il faut mettre en place :
Echange entre wemos et l'arduino
Page web dans un fichier html
Pour les échange il faut bien sur coder des deux cotés :smiley-confuse: sur le net je trouve des post qui code le wemos (pour utilisation seul) mais pas avec des échange rx/tx avec l'arduino.
De même qu'il faudra déclarer les variables coté wemos ( et la je me demande comment faire concorder les variable)
Pour la page web .html, pour ne pas avoir a ajouter un client.print a chaque ligne.
J'ai un souvenir lointain... j'avais tenter de stocker le html sur SD mais le resultat était catastrophique.
Il me semble également me souvenir que l'on avait parler d'une mémoire "caché" sur le wemos qui pouvait contenir cette page ?
En bref j'ai repousser la partie wifi au bout du bout mais nous y voila ... il faut que j'y m'attelle !
Disons que c'est le dernier sprint ... de 5km lol
Bonjour, je suis le sujet presque depuis le début car il s'avère que j'y trouve des réponses à mes questions sans souvent avoir besoin de les poser...
Désolé si je n'ai pas tout correctement suivi et sans vouloir remettre en cause tout le taf effectué, pourquoi ne pas utiliser que le Wemos ? (nombre de pins dispo ?) Le wemos (esp8266) est plus puissant et possède plus de mémoire qu'un arduino. Cela permettrai de s'affranchir de la comm Wemos<-> arduino.
Le codage est "globalement" (tout est dans le globalement) identique entre ESP8266 et arduino..
Fin du HS, c'est avec hâte que je suivrais la fin de ce projet, en esperant pouvoir partager le mien dès qu'il seras 'terminé.'
Bonjour caps1g3f,
Le wemos ne dispose tout simplement pas d'assez de pin :)
Pour ma culture personel :)
Pourquoi quand les declarations ci dessous son dans le .cpp il prenne 2 octets de plus que quand il sont dans le .h ?
const uint8_t maxSsid = 30;// prévoir la bonne longueur
const uint8_t maxMdp = 30;// prévoir la bonne longueur
Parce qu'il semblerait que dans un cas l'optimiseur réussisse à les virer (remplacement direct en mémoire là où ces constantes sont utilisées) et pas dans l'autre...
Tout dépend si ces variables sont utilisées dans d'autres fichiers. Si ce n'est pas le cas, le linker les ignore.
Il est déconseillé de déclarer des variables dans un .h
Cela conduit forcément à une double définition si tu les utilises dans 2 .cpp ou .ino différents.
Dans un .cpp ou .ino :
const uint8_t maxSsid = 30;// prévoir la bonne longueur
const uint8_t maxMdp = 30;// prévoir la bonne longueur
Dans un .h :
extern const uint8_t maxSsid;
extern const uint8_t maxMdp;
@+
Peut être n'as tu pas suivi la manière dont est sont faite les déclarations dans mon fichier :
J'ai mon .ino qui comporte bibliothèque setup et loop.
Pour chaque fonction principal j'ai un .h + un .cpp
et j'ai en plus un pin.h ou sont les déclarations de toutes les pin, et un global.h contenant en "extern" toute les variable utiliser dans plusieurs .cpp
j'appel global.h dans les .cpp qui utilise une variable commune.
Si dans un .cpp j'utilise une fonction qui est dans déclarer dans un autre .h je suis obliger d''appeler le .h dans le .cpp en question.
Je ne sait pas si c'est très claire mais explication. :)
un petit aperçu du pcb.
Il ne devrait plus y avoir de modification.
Si des modification ont lieu ce seras sur la partie alimentation en amont (pour ceux qui voudrais un système sur batterie en cas de coupure EDF) dans un boitier dédier.
Pour les sorties flotteur et oscillateur il y auras 2 très petite carte déporter pour diviser les flotteurs/oscillateurs (cela évite d'avoir 5x 2fils flotteurs partant du boitier et 3x 3 fils oscillateur)
(https://image.noelshack.com/fichiers/2018/31/4/1533201563-pcb-dessus.jpg)
(https://image.noelshack.com/fichiers/2018/31/4/1533201549-pcb-face-1.jpg)
(https://image.noelshack.com/fichiers/2018/31/4/1533201540-pcb-face-2.jpg)
Peut être n'as tu pas suivi la manière dont est sont faite les déclarations dans mon fichier :
Si, parfaitement.
et j'ai en plus un pin.h ou sont les déclarations de toutes les pin, et un global.h contenant en "extern" toute les variable utiliser dans plusieurs .cpp
C'est bien ce que je disais. Déclarations "extern" dans les .h.
Dans ton #775, tu parlais de déclarations de constantes dans un .h, sans "extern".
A proscrire.
@+
Dans ton #775, tu parlais de déclarations de constantes dans un .h, sans "extern".
A proscrire.
effectivement, j'ai "retrouver" ces deux variables dans un .h et de mémoire je les avait mis la car j'avais une erreur de compil des que je l'ai mettais ailleurs.
Je les est bien basculer dans le .cpp correspondant sans erreur de compil (c'est que j'ai du m'améliorer :) )
et ces la que j'ai vu c'est 2 octets d'augmentation donc je me demandait pourquoi.
Bouns bonjour.
Sympa le montage à blanc de ton système.
Bonne journée à toi.
Parce qu'il semblerait que dans un cas l'optimiseur réussisse à les virer (remplacement direct en mémoire là où ces constantes sont utilisées) et pas dans l'autre...
j'avais pas vu ton message.
ok
Bouns bonjour.
Sympa le montage à blanc de ton système.
Bonne journée à toi.
j'attend un jack 2.5 pour pcb en remplacement du 2e jack DC pour qu'il n'y est pas de confusion lors du branchement et je fait un pcb "final avec toutes les prises et si tout est Ok.
Bouns,
Cela le seras Ok c'est sur....
Bon je retourne essayer de comprendre le Millis..
Pascal.
bon courage ;)
j'ai mis du temps moi aussi.
C'était tellement plus simple delay(500) lol
oups, je vais me faire taper sur les doigt,
C'était tellement plus simple delay(500); lol
Cc,
localBuffer[0] = '\0';
memset(localBuffer, '\0', localBufferSize);
Le premier indique au premier caractere le caractere de fin
Le deuxième écrit un caractere de fin sur tout les emplacement jusque a la taille max du char (localBufferSize)
j'ai bon ? :)
y a t'il une différence et une préférence a avoir entre les deux ?
Bouns bonjour,
Tu as raison le millis c'est bien, mais la je me prend la tête, alors comme avec delay; cela fonctionne pour ce que je veux faire et que je ne bloque l' arduino qu'une fois par jour pour le nourrissage de mes bestioles, je verrais plus tard le millis.
Bonne soirée à toi.
Et vive l' Aquabouns.
Cc,
localBuffer[0] = '\0';
memset(localBuffer, '\0', localBufferSize);
Le premier indique au premier caractere le caractere de fin
Le deuxième écrit un caractere de fin sur tout les emplacement jusque a la taille max du char (localBufferSize)
j'ai bon ? :)
y a t'il une différence et une préférence a avoir entre les deux ?
Vous avez bon :)
La différence est celle que vous expliquez donc tout dépend de si votre a besoin d'avoir des 0 partout ou pas (la première version est bien sur localBufferSize fois plus rapide puisqu'on écrit qu'a Un seul endroit)
quelle intérêt d'avoir des 0 partout ?
Que ce soit l'une ou l'autre, il sont utilisé juste avant que localBuffer ne soit de nouveau utilisé.
ça sert parfois quand on remplit une cstring caractère par caractère et qu'on a la flemme ou pas le temps au moment de la réception d'ajouter le '\0' ou qu'il y a plusieurs processus écrivant en mémoire par interruption et qu'il,faut aller vite (et quand vous avez prévu une case de plus à la fin) de cette façon vous avez en permanence une cstring bien formée;..
mais effectivement bien Souvent aucun intérêt si ce n'est de connaître la fonction :)
ok merci :)
Petit break de quelque jour.
Je ne prend pas l'arduino :)
Mais juste le code pour continuer a commenter, il me reste 1 page, l'affichage, 1500 lignes :)
a mon retour faut vraiment je me mette sur le wifi et html.
Pour ceux qui sont déjà en vacance, bonne vacance.
Pour les autres, bon courage :)
Profite(z) bien !
cc tout le monde,
de retour, ça avance ...
(https://forums.futura-sciences.com/attachment.php?attachmentid=371973&d=1535026970)
(https://forums.futura-sciences.com/attachment.php?attachmentid=371974&d=1535027015)
(https://forums.futura-sciences.com/attachment.php?attachmentid=371975&d=1535027063)
(https://forums.futura-sciences.com/attachment.php?attachmentid=371976&d=1535027104)
(https://forums.futura-sciences.com/attachment.php?attachmentid=371977&d=1535027171)
le montage ma permis de mettre le doit sur des petit bug/erreur que je n'avait pas vu jusque la.
Ça commence a ressembler a quelque chose :)
j'ai profiter de se repos pour lire.
D'un coté de l'électronique et de l'autre html et fonctionnement du wemos d1 mini.
Ok pour programmer l'ESP via usb et afficher une page web.
Sur le net on trouve pour programmer l'ESP ou pour que l'arduino échange avec l'ESP (sans programmation de l'ESP) par exemple (https://forum.arduino.cc/index.php?topic=511147.0) ;)
Par contre difficile facile de trouver des tuto / exemple pour se dont j'ai besoin, échange de donner entre arduino et ESP ( l'ESP étant indépendamment programmer)
Pourtant, c'est le même principe qu'avec l'écran nextion donc il y a peut etre une bibliothèque spécifique ?
Car dans mon cas c'est l'ESP qui va demander des info a l'arduino et non l'inverse.
Welcome back :)
Il me semble que j'avais posté un petit exemple de discussion entre un esp (qui a un programme arduino) et un arduino non ?
En gros suffit d'inventer un petit protocole série d'échange d'infos
Il me semble que j'avais posté un petit exemple de discussion entre un esp (qui a un programme arduino) et un arduino non ?
Si c'est sur l'une des 54 pages, je ne l'ai pas trouvé mais je m'était fait la remarque également que j'avais vu cela passer (mais j'en lit tellement a droite a gauche ...), je vais reparcourir les pages ...
si c'est sur l'un de tes tuto ... pas trouver non plus, en même temps, retrouver cela parmi tes 12291 messages ... c'est comme cherché un point virgule manquant dans une page de code :)
En gros suffit d'inventer un petit protocole série d'échange d'infos
lol, oui, y a qu'a ... lol
De mémoire j'avais posté dans cette discussion mais peut être pas... je vais voir (là je suis sur mon tel) si je retrouve mon exemple
jusque a la page 10 on parle ESP mais sans qu'il soit programmer.( donc maintenant cette partie de code est directement dans l'ESP avec la page web)
A partir de page 11 on attaque la partie GSM.
Je continu ...
Apres la parti GSM on parle CARTE SD et puis NEXTION, on ne revient pas sur le wifi donc je ne crois pas que cela soit ici
Ok - je regarde ce week end - mais ça nécessitera bcp de changements...
Ok - je regarde ce week end
merci
mais ça nécessitera bcp de changements...
:o
Il vous faudra une architecture propre donc toutes les valeurs affichables et modifiables sur le web devront être en cache sur l'ESP.
Quand l'utilisateur ou le programme côté Arduino modifie une de ces valeurs ils faut informer l'ESP
Quand l'utilisateur modifie une valeur par le biais du web il faut informer l'Arduino pour que l'Arduino le prenne en compte
Une façon de faire cela est « simple ».
Au setup l'Arduino lit en eeprom toutes ses valeurs et par le biais d'un protocole notifie l'ESP (envoi d'un message de RESET et pleins de variable=valeur\n par exemple )
Donc l'ESP doit avoir dans sa loop() une écoute d'une communication série (Serial ou I2C etc). L'ESP écoute aussi bien sûr les requêtes web
Ensuite côté Arduino à chaque fois que vous modifiez une variable qui est en cache côté ESP s'il faut envoyer de la même manière la mise à jour
Enfin - côté Arduino il faut que la loop() écoute un protocole similaire de la part de l'ESP qui lui dira « eh l'utilisateur web vient de changer un truc, modifie cette valeur et déclenche ce qu'il faut pour que ce soit pris en compte »
Maintenant si votre page web ne permet pas de modifs mais juste la consultation alors c'est plus simple, il n'y a qu'un sens à coder
Dans mon rêve je voudrait que l'interface ecran soit dupliqué sur la page web.
Mais dans un premier temps je me met sur la page web "menu" (qui affiche les info sans modif)
en se qui concerne les variable, je pensait que l'ESP allait demander les variable uniquement lors de la demande de page. je ne vois pas l'utilité d'envoyer a chaque fois les variable alors que la page web n'est pas demandé.
Ça a l'air encore bien compliqué :(
Il n'y a pas une bibliothèque qui existe tout comme le nextion ?
Non c'est pas super compliqué. vous pouvez effectivement coder cela « on demand » - l'ESP quand il a besoin des données pour afficher la page web demande toutes les data à l'arduino qui balance tout d'un coup, c'est plus simple mais un peu plus lent car vous allez envoyer tout d'un coup sans vous poser la question de ce qui a changé ou pas sans doute.. mais bon ça devrait être quasiment transparent puisque derrière il y'a une communication web. dans ce cas vous pouvez aussi même vous permettre de balancer le tout en binaire - équivalent à la structure de toutes les données pertinentes (il me semble que vous aviez fait le boulot de toutes les mettre dans une structure déjà)
Pour la vitesse, l'affichage de la page et le temps de réaction lors des clic sur les bouton était assez long (1 a 3 sec entre le Click et le "réafichage" de la page MAJ
Apparemment il y a possibilité d'afficher juste le champs modifier en utilisant AJAX mais j'ai vraiment du mal avec le HTML.
j'avance très peu du coup sa me saoul :(
<!DOCTYPE html>
<html>
<head>
<style>
body {
background-image: url("https://rrzk5q.by.files.1drv.com/y4mCCAD49BMT212H9PwvLw2BZLL2ayFfDNuyycms7GtBHxBAt8t8tskfjOMSJx7F0UnFYm2pVUn7Zy9bCScSyZXZkqdAOlgMlj81-jThw923fX4mZSAvawqJzTXc2HhmcCCEG6rnJDaPBrbXAsalshfUZwEx6YeJBVF8mCVNT-MD0jvDQyNkwsh8B1gFpjBk4l3Cg3eLcTHNYuLuFXtUOWpGA?width=800&height=480&cropmode=none");
background-repeat:no-repeat;
}
p {
color: white;
font-family: Comic Sans MS;
font-size: 18px;
}
div.fix1 {
position: fixed;
top: 161px;
left: 140px;
}
div.fix2 {
position: fixed;
top: 192px;
left: 140px;
}
</style> </head>
<body>
<div class="fix1">
<p> 93</p>
</div>
<div class="fix2">
<p> 58</p>
</div>
</body>
</html>
vous pouvez aussi même vous permettre de balancer le tout en binaire - équivalent à la structure de toutes les données pertinentes (il me semble que vous aviez fait le boulot de toutes les mettre dans une structure déjà)
Si tu parle des variable, oui elle son dans uns structure
struct __attribute__ ((packed)) paramS_t {// force l'ordre des champs
// page menu
bool tempeteAleatoireOn;
bool nourrissageON[2];
uint8_t remonteDelay;
uint8_t eclairageOnOffPwm[4];
uint8_t brassageOnOffPwm[3];
// page config
uint8_t puissanceMaxEclairage[4];
uint32_t leverSoleil;
uint32_t coucherSoleil;
uint16_t dureelevercoucher;
float alertetemperaturebasse;
float alertetemperaturehaute;
float ventilationaquarium;
uint8_t ventilationrampe;
bool adresseSondeRampe;
bool adresseSondeBac;
uint32_t heureNourrissage[2];
uint32_t dureeNourrissageMillis;
uint16_t dureeOsmolationMillis ;
uint8_t compteurOsmolationMax;
uint16_t consignePhRac;
uint16_t alertephbacbas;
uint16_t alertephbachaut;
// page brassage
uint8_t puissanceMaxBrassage[3];
uint8_t angle1Oscillo[3];
uint8_t angle2Oscillo[3];
uint16_t delaisMouvementOscilloMillis[3];
uint32_t dureeTempeteMillis;
uint8_t puissanceTempete;
uint8_t accalmieNocturne;
// horloge
uint32_t heureDebutAlerte;
uint32_t heureFinAlerte;
};
J'ai retrouvé mon bout de code de démo, effectivement je pense que je ne l'avais pas posté dans cette discussion
SerialComMega et SerialComD1 en PJ car trop gros sinon.
pour le code de l'ESP, pensez à éditer
const char* ssid = "***********";
const char* password = "***********";
En gros le programme sur le MEGA écoute des commandes sur la ligne Serial (le moniteur Série Arduino) et balance de l'info ou des commandes sur Serial1. Au bout de Serial1 vous avez un ESP (D1 mini dans mon exemple ici) dont la loop() fait deux choses:
1/ écouter sur Serial si on a des demandes en provenances du MEGA
2/ écouter si on a une connexion web
ce qui est échangé entre les 2 Arduino est dans une structure, ici 3 octets a, b et c
struct data_t {
int8_t a, b, c;
} data;
le MEGA reconnait des petites commandes simples
- taper i incrémentera de 1 a, b et c
- taper 0 remet a, b et c à 0
- taper r demande à recevoir les valeurs depuis le monde ESP
Si vous vous connectez sur le D1 mini (votre ESP) depuis le web en même temps et que vous modifiez a,b et c par les petites commandes, vous verrez que tout cela reste synchronisé.
entre le MEGA et le D1, il y a un petit protocole tout simple. le MEGA envoie $$E quand il veut envoyer des données, donc suivi de la structure et $$R quand il veut que le D1 lui envoie les valeurs courantes.
voilà un truc à explorer :)
Jean-Marc
toujours au TOP !
merci
vous verrez que tout cela reste synchronisé.
non c'est pas pour le vous lol, synchronisé oui a condition de rafraîchir la page
bon ba ya plus qu'as ...
déchiffrer tout cela dans un premier temps ... (les sentinelles, c'est pas dans matrix ou les château fort normalement ?:) )
et puis adapter tout cela
et ensuite resteras a faire la/les pages web GRRrrrr
;D 8) :D 8) ;D :D
J'ai fait les modif et integrer le code dans mon code principale
J'ai fait les modif dans le code de L'esp
J'ai fait le test avec 3 variables issu de l'eeprom de l'arduino et sur la page web j'ai bien les bonnes valeurs
:smiley-sweat:
Il falait bien ajouter a la fin du setup
envoyerAd1mini(sauvegarde);
Pour que l'ESP reçoive bien les valeurs après chargement de l'eeprom.
Il faut que j'ajoute cette ligne également après chaque sauvegarde des paramètre fait a partir de l'écran pour que l'ESP soit a jour.
Par contre :smiley-confuse:
Il s'agit la des paramètres de l'eeprom donc utile pour la page "paramétrage"
Mais pour la page "menu" il me faut les variables "en cours" (puissance éclairage, brassage, température, ph ect ...) et toutes ces variable ne sont pas dans une structure ...
donc il faut tout mettre dans une structure aussi ?
bravo :)
donc il faut tout mettre dans une structure aussi ?
tel que je l'ai codé c'est assez "bourrin" - ça balance tous les octets d'un coup et une struct c'est pratique pour cela... donc si vous voulez faire autrement, il faut soit rajouter un peu d'intelligence dans la façon dont les cartes discutent, soit effectivement tout mettre dans la même structure... :smiley-roll-blue:
euh ... rajouter un peu d'intelligence dans la façon dont les cartes discutent ... je voudrait bien mais j'en ai déjà pas assez pour moi ... alors si il faut en rajouter je passe avant le bout de code :)
mettre dans une structure me parait tout de même plus accessible :)
euh ... rajouter un peu d'intelligence dans la façon dont les cartes discutent ... je voudrait bien mais j'en ai déjà pas assez pour moi ... alors si il faut en rajouter je passe avant le bout de code :)
:)
la structure est faite et tout a l'air de fonctionner comme avant.
Un peu de galère mais sa y est.
J'en arrive a la partie "écoute" il faudrait que la même fonction écoute et comprenne quelle structure est demandé.
Mais je ne comprend pas le &d
void ecouterCommande(paramS_t& d) {
et je pense que paramS_t est implanter a la place du (uint8_t*) ?
uint8_t * ptr = (uint8_t *)&d;
on peut lui faire comprendre quelle structure l'on veut ou alors j'écrit 2 début fonctions différent puis je lance la suite commune ?
j'ai juste remplacer la structure en attendant
et voila
(https://i.goopics.net/lm1v2.jpg)
J'en arrive a la partie "écoute" il faudrait que la même fonction écoute et comprenne quelle structure est demandé.
Mais je ne comprend pas le &d
void ecouterCommande(paramS_t& d) {
le & c'est pour dire qu'on passe la structure par référence, donc j'ai une structure globale et quand j'appelle la fonction aucune copie n'est créée, je modifie bien la structure globale
dans mon code d'exemple j'ai
void ecouterCommande(data_t& d)
par ce que toutes les data sont stockées dans une structure de ce type là
struct data_t {
int8_t a, b, c;
} data;
donc si dans votre côté arduino vous avez tout dans une structure
paramS_t alors il faut déclarer la structure
paramS_t aussi côté ESP de la même façon et utiliser cela.
pouvez vous poster ce à quoi ressemble votre grosse structure maintenant ?
--> il y a un truc auquel il faut faire attention, c'est la taille des types suivant les architectures... un
int c'est 2 octets sur votre Arduino MEGA mais ce sera 4 sur l'ESP. donc il faut s'assurer de bien déclarer les types de la structure avec des
int16_t par exemple des 2 côtés et pas le type "générique". Sinon quand on va balancer les octets d'un côté on va en envoyer 2 et de l'autre il va en attendre 4. Utililsez vous des
double côté Arduino ou juste des
float ? (ou pas du tout ?)
Il faudrait aussi faire attention si les octets sont inversés (les petits et grands indiens) mais on est en little endian des deux côtés il me semble donc pas de soucis de ce côté là.
j'ai juste remplacer la structure en attendant
et voila
(https://i.goopics.net/lm1v2.jpg)
(https://i.goopics.net/lm1v2.jpg)
joli !!!
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
joli !!!
mdr, faut pas exagerer lol
(https://www.hieroglyphes.fr/io/shop_produit/images/dyn_image2_0005502_Herbin_Bon_Point_2_Vert_jpg_large.jpg)
merci :)
le & c'est pour dire qu'on passe la structure par référence, donc j'ai une structure globale et quand j'appelle la fonction aucune copie n'est créée, je modifie bien la structure globale
je n'est pas bien compris car cette fonction ne modifie rien elle ne fait que lire ?
J'ai fini par trouver ca (http://www.cplusplus.com/doc/tutorial/pointers/)
je n'est pas tout lu mais juste la partie &
il explique que de mètre & pointe vers l'adresse mémoire de la variable (pour nous la structur ?) au lieu de la valeur contenu a cette adresse.
donc si dans votre côté arduino vous avez tout dans une structure paramS_t alors il faut déclarer la structure paramS_t aussi côté ESP de la même façon et utiliser cela.
J'avais bien compris et c'est bien se que j'ai fait, les structur sont identique dans les 2 code (arduino et esp)
pouvez vous poster ce à quoi ressemble votre grosse structure maintenant ?
Non, secret defence !
--> il y a un truc auquel il faut faire attention, c'est la taille des types suivant les architectures...
je n'utilise que des uint*_t depuis que j'ai appris chef :)
Utililsez vous des double côté Arduino ou juste des float ? (ou pas du tout ?)
que du float
Il faudrait aussi faire attention si les octets sont inversés (les petits et grands indiens)
c'est toi qui a inversé !!!
c'est pas petits et grands indien,
c'est grand indien, petit indiens
(https://images-na.ssl-images-amazon.com/images/I/61FmERXAEKL._SX391_BO1,204,203,200_.jpg)
pendant que tu souri, j'envoie mes structures :
celle de l'eeprom
struct __attribute__ ((packed)) paramS_t {// force l'ordre des champs
// page menu
bool tempeteAleatoireOn;
bool nourrissageON[2];
uint8_t remonteDelay;
uint8_t eclairageOnOffPwm[4];
uint8_t brassageOnOffPwm[3];
// page config
uint8_t puissanceMaxEclairage[4];
uint32_t leverSoleil;
uint32_t coucherSoleil;
uint16_t dureelevercoucher;
float alertetemperaturebasse;
float alertetemperaturehaute;
float ventilationaquarium;
uint8_t ventilationrampe;
bool adresseSondeRampe;
bool adresseSondeBac;
uint32_t heureNourrissage[2];
uint32_t dureeNourrissageMillis;
uint16_t dureeOsmolationMillis ;
uint8_t compteurOsmolationMax;
uint16_t consignePhRac;
uint16_t alertephbacbas;
uint16_t alertephbachaut;
// page brassage
uint8_t puissanceMaxBrassage[3];
uint8_t angle1Oscillo[3];
uint8_t angle2Oscillo[3];
uint16_t delaisMouvementOscilloMillis[3];
uint32_t dureeTempeteMillis;
uint8_t puissanceTempete;
uint8_t accalmieNocturne;
// horloge
uint32_t heureDebutAlerte;
uint32_t heureFinAlerte;
};
extern paramS_t sauvegarde;
et la nouvelle des variables
struct valeursActuel_t { // structure des valeurs actuels
float temperatureBac; // pour la temperature du bac
uint8_t temperatureRampe; // pour la temperature de la rampe
uint8_t Heure;
uint8_t minut;
uint8_t pwmEclairage[4];
uint8_t pwmBrassage[3];
uint16_t phRac;
uint16_t phBac;
};
extern valeursActuel_t variable;
je n'ai pas bien compris car cette fonction ne modifie rien elle ne fait que lire ?
Elle lit mais stocke les octets lus dans la structure
Vous pouvez lire cela pour le & au niveau du paramètre de fonction (https://cpp.developpez.com/cours/cpp/?page=page_6) (passage par référence - à ne pas confondre même si c'est le Même symbole avec la notation de pointeurs )
2 lecture mais bon ... c'est un peut hard ... surtout a cette heure.
je resserrait demain :)
Elle lit mais stocke les octets lus dans la structure
Vous pouvez lire cela pour le & au niveau du paramètre de fonction (https://cpp.developpez.com/cours/cpp/?page=page_6) (passage par référence - à ne pas confondre même si c'est le Même symbole avec la notation de pointeurs )
Je suis ok pour la lecture mais pour "boolean envoyer" elle n'ecrit rien, elle ne fait que lire et envoyer.
Pourtant elle est aussi déclaré : boolean envoyerAd1mini(paramS_t
& d) {
pour lire / envoyer mes differente structure, j'ai tenter ... j'ai echoué
boolean envoyerAd1mini(struct& d, const uint8_t taillestructure ){
mais je me dit es que le nom de la structure deviendrait pas un type ?
J'ai rien trouver qui parle de ca sur le net pour l'instant.
Oui passer une grosse structure par référence simplifie l'écriture - c'est commme passer le pointeur mais on n'en s'ennuie pas à faire de * et des & et -> partout, on code comme si la structure était locale mais on lit les données que la fonction appelante passe en param
ok c'es pas encore tres claire (pour moi, pas tes explication lol) mais quand jsuis mieux reveiller je relit "le cours" d'hier
Ok :)
pour lire / envoyer mes differente structure, j'ai tenter ... j'ai echoué
boolean envoyerAd1mini(struct& d, const uint8_t taillestructure ){
mais je me dit es que le nom de la structure deviendrait pas un type ?
J'ai rien trouver qui parle de ca sur le net pour l'instant.
C'est bien ca ? je ne pourrait pas appeler la même fonctions pour lire / envoyer des structure différente ?
J'ai aussi remarquer autre chose, quand l'on envoie ou reçoit la boucle de lecture / écriture se fait plusieurs fois, pourtant .read est sencer effacer l'octects une fois lu ?
Exemple, j'ai fait une demande de réception. (j'ai mis un Serial.print une foit toute la structure receptionné et j'ai :
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
on a tout recu !!!
je suis un peu perdu quant à savoir à quelle fonction vous faites référence...
ma fonction:
void ecouterCommande(data_t& d)
{
static boolean receptionEnCours = false;
uint8_t * ptr = (uint8_t *)&d; // on pointe sur le début de la zone de stockage des données
if (portSerie.available()) { // on a reçu quelque chose
if (receptionEnCours) { // on était déjà en train de recevoir des données, on ajoute à la fin
static uint8_t index = 0;
*(ptr + index) = (uint8_t) portSerie.read();
if (++index >= tailleData) { // on a tout reçu
index = 0;
receptionEnCours = false;
}
} else { // on attend toujours le marqueur de début de trame (une sentinelle)
...
si on a reçu la sentinelle et que des données sont en train d'arriver (la réception du buffer de données était en cours), alors je continue à ajouter dans la structure petit à petit chaque octet reçu. ptr pointe sur le début de la structure et index qui est static progresse d'octet en octet avant d'être remis à 0 (ainsi que receptionEnCours ) pour la prochaine fois quand on a tout reçu
sinon (en vert) je suis encore en train d'attendre une commande et donc j'ai le code d'analyse de commande qui attend une "sentinelle" (un ensemble d'octets qui marque le début d'un message).
j'ai a peut prés compris cette parti du code.
Je réexplique mes deux "problèmes"
J'ai 2 structures, une qui contient les paramètres (récupérer dans l'eeprom) et l'autre qui contient les variables (t°, PH ...)
Il faut que les fonctions void ecouterCommande(data_t& d) et void envoyerCommande(data_t& d)
Puisse envoyer et recevoir l'une ou l'autre.
dans la simplicité je pourrait dupliquer les fonctions (pour chaque structure)
l'idéal serait d'avoir une fonction envoyer et recevoir du genre
boolean envoyerAd1mini(struct& d, const uint8_t taillestructure ){
struct = la structure que l'on veut "traité"
Pour ne pas dupliquer des fonctions identique.
Je sait pas si j'ai été très claire et compréhensif lol
l'autre point que j'ai remarqué :
l'esp envoie "M" a l'arduino pour lui stipuler qu'il veut recevoir la structure variable.
L'arduino reçoit bien le "M" et envoie bien la structure mais il le fait plusieurs fois (~x15)
pourquoi ? une fois Serial.read fait, le "M" ne devrait plus être lu.
j'ai mis un boolean pour voir et ne plus avoir ce phénomène mais j'ai la même chose coté ESP. il lit la structure recut complète ~x15
sans votre code difficile à expliquer...
si vous avez 2 structures différentes à traiter, le plus simple c'est 2 fonctions sinon il ne faut pas passer le type de structure en paramètre de la fonction et dire que c'est juste un uint8_t * (pointeur sur des octets) et