{"id":219,"date":"2016-04-05T19:50:29","date_gmt":"2016-04-05T18:50:29","guid":{"rendered":"https:\/\/kevoster.wordpress.com\/?p=219"},"modified":"2024-05-02T13:08:37","modified_gmt":"2024-05-02T11:08:37","slug":"mesure-de-niveau-pour-puit","status":"publish","type":"post","link":"https:\/\/kev-it.fr\/index.php\/2016\/04\/05\/mesure-de-niveau-pour-puit\/","title":{"rendered":"Mesure de niveau pour puit"},"content":{"rendered":"<p>C&rsquo;est toujours utile de savoir quel est le niveau de son puit ou de son r\u00e9servoir. Et puis, il faut bien bien trouver des choses un peu int\u00e9ressantes \u00e0 faire avec les Arduino \ud83d\ude09<\/p>\n<p>On va essayer de mener l&rsquo;exp\u00e9rience sous la forme d&rsquo;un mini-projet<\/p>\n<h1>Etape 1 : Expression de besoin<\/h1>\n<ul>\n<li>Mesurer le niveau d&rsquo;un r\u00e9servoir ou d&rsquo;un puits en m\u00e8tre et en pour\u00e7entage.<\/li>\n<li>Attention au diam\u00e8tre du puits peut \u00eatre variable et m\u00eame parfois faible (80cm).<\/li>\n<li>Le projet doit fournir un capteur qui fonctionne de mani\u00e8re isol\u00e9e (capteur + affichage), mais qui peut s&rsquo;interfacer \u00e0 un syst\u00e8me domotique via mySensors (il faut tenir compte de l&rsquo;existant).<\/li>\n<li>La mesure soit \u00eatre fiable (+\/- 10cm) et mesurer un niveau jusqu&rsquo;\u00e0 10 m\u00e8tres.<\/li>\n<\/ul>\n<h1>Etape 2 : Possibilit\u00e9s techniques<\/h1>\n<p>Pour mesurer un niveau nous avons plusieurs possibilit\u00e9s:<\/p>\n<h2>Mesure ultra-son<\/h2>\n<p>La plus simple et la moins ch\u00e8re \u00e0 mettre en \u0153uvre. Le probl\u00e8me est que c&rsquo;est aussi la moins fiable. En fonction de la forme du puits et notamment de son rapport diam\u00e8tre profondeur la mesure devient rapidement impossible. Autre probl\u00e8me l&rsquo;humidit\u00e9 ambiante provoque la corrosion rapide du capteur.<\/p>\n<h2>Mesure par capteur de niveau<\/h2>\n<p>La solution la plus basique. Ici, on pr\u00e9pare un rail et on place des capteurs \u00e0 bascule \u00e0 intervalles r\u00e9guliers. Mis \u00e0 part le fait que la d\u00e9tection soit purement m\u00e9canique il s&rsquo;agit d&rsquo;une solution extr\u00eamement fiable (\u00e0 l&rsquo;usure des capteurs pr\u00e8s). N\u00e9anmoins cela reste la solution la moins pr\u00e9cise et la plus ch\u00e8re \u00e0 mettre en \u0153uvre (pr\u00e9parer un rail en inox de la bonne longueur, le percer, fixer les capteurs, tirer des tresses de c\u00e2bles, selon le nombre de capteurs ajouter un multiplexeur).<\/p>\n<h2>Mesure par capteur de pression<\/h2>\n<p>Au final, il nous reste la solution la plus simple. immerger un capteur un capteur de pression. Il suffit d&rsquo;un capteur de pression et d&rsquo;une longueur de c\u00e2ble. Ici pas de parties m\u00e9caniques, pas de montage compliqu\u00e9, pas composants complexes.<\/p>\n<h1>Etape 3 : Composants retenus<\/h1>\n<h2>Le capteur<\/h2>\n<p>On ne va pas r\u00e9inventer l&rsquo;eau ti\u00e8de. Quelqu&rsquo;un a d\u00e9j\u00e0 fait tout le travail <a href=\"http:\/\/www.dom-ip.com\/wiki\/R%C3%A9alisation_d'un_capteur_de_niveau_d'eau_pour_un_puits\" target=\"_blank\" rel=\"noopener\">ICI<\/a>. On va utiliser son travail pour la partie capteur et notamment son id\u00e9e ing\u00e9nieuse de fixer le capteur sur une bouteille remplie d&rsquo;huile alimentaire pour ne pas risquer de boucher\/alt\u00e9rer le capteur. Ensuite, le capteur sera pour sa part noy\u00e9 dans de la r\u00e9sine pour assurer son \u00e9tanch\u00e9it\u00e9.<\/p>\n<h3>Composant<\/h3>\n<p>Nous allons utiliser un <a href=\"http:\/\/www.nxp.com\/files\/sensors\/doc\/data_sheet\/MPX5700.pdf\">MPX5700AP de chez NXP<\/a> disponible par exemple sur AliExpress pour moins de 8 euros. Il peut mesurer des pressions entre 15 et 700 kPa (soit 7bar). Ce qui nous permet au passage de r\u00e9aliser des mesures jusqu&rsquo;\u00e0 60 m\u00e8tres sous le niveau de l&rsquo;eau (1bar \/ 10m\u00e8tres).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-281\" src=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/04\/mpx5700ap.png\" alt=\"MPX5700AP\" width=\"169\" height=\"279\" \/><\/p>\n<h3>Filtrage du bruit<\/h3>\n<p>Puisqu&rsquo;il s&rsquo;agit d&rsquo;un capteur analogique, il doit poss\u00e9der des filtres ad\u00e9quats pour que la mesure soit la plus pr\u00e9cise possible. Le d\u00e9tail est disponible dans <a href=\"http:\/\/www.nxp.com\/files\/sensors\/doc\/app_note\/AN1646.pdf\" target=\"_blank\" rel=\"noopener\">la fiche AN1646 sur le site de NXP<\/a>. Trois solutions sont propos\u00e9s : le filtrage RC, le filtrage par ampli-op et le filtrage logiciel. Pour la simplicit\u00e9 du montage, nous allons combiner un filtrage RC et un filtrage logiciel (apr\u00e8s tout il faut bien que les 16Mhz de l&rsquo;arduino servent \u00e0 quelque chose). Le filtrage se fera par \u00e9chantillonnage (64 \u00e9chantillons), qui permet d&rsquo;am\u00e9liorer la pr\u00e9cision \u00e0 1mV (pic \u00e0 pic).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-289\" src=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/04\/pressure-sensor-noise-filter.png\" alt=\"Pressure Sensor Noise filter\" width=\"579\" height=\"265\" srcset=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/04\/pressure-sensor-noise-filter.png 579w, https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/04\/pressure-sensor-noise-filter-300x137.png 300w\" sizes=\"auto, (max-width: 579px) 100vw, 579px\" \/><\/p>\n<h3>Pr\u00e9cision de la mesure<\/h3>\n<p>Afin d&rsquo;am\u00e9liorer encore la pr\u00e9cision, il faudra utiliser une alimentation r\u00e9gul\u00e9e par un 7805 comme indiqu\u00e9 dans la fiche AN1646.<\/p>\n<pre><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-321\" src=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/04\/mpx5700-formula.png\" alt=\"MPX5700-Formula\" width=\"434\" height=\"329\" srcset=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/04\/mpx5700-formula.png 434w, https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/04\/mpx5700-formula-300x227.png 300w\" sizes=\"auto, (max-width: 434px) 100vw, 434px\" \/><\/pre>\n<p>La mesure est donn\u00e9e par la formule suivante :<\/p>\n<pre style=\"padding-left: 60px;\">Vout = Vs * (0.0012858 * P +0.04) \u00b1 Error\nVs = 5Vdc\n\nSoit\n\n\/\/ takes a A\/D sample (0 to 1024) based on 5V ref\n\/\/\u00a0 to compute pressure on MPX sensor\nfloat getPressure(int analogValue) {\n\u00a0 \/\/ Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):\n\u00a0 float value = analogValue * (5 \/ 1023.0);\n\u00a0 \/\/ print out the value you read:\n\u00a0 return ((value \/ 5) - 0.04) \/ 0.0012858;\n}<\/pre>\n<p>Le probl\u00e8me est que le capteur mesure une pression entre 0.15 et 7bar. Et c&rsquo;est justement cette plage qui va servir \u00e0 la conversion A\/D.<\/p>\n<p>Si on consid\u00e8re un puits de 9 m\u00e8tres maximum, cela correspond \u00e0 une pression maximale de 2bars sur le capteur. De m\u00eame, la pression minimale mesur\u00e9e sera toujours de 1bar (pression atmosph\u00e9rique ambiante). Cela signifie que notre plage de tension sera entre 0.7 et 1.5V soit une utilisation de seulement 16% de la plage de mesure. Le convertisseur A\/D de l&rsquo;arduino \u00e9chantillonne sur 10 bits soit (1024 pour 5V), ce qui nous laisse environs 160 valeurs pour mesure une pression situ\u00e9e entre 1 et 2 bars (9m\u00e8tres) soit une pr\u00e9cision approximative de 6cm par step.<\/p>\n<p>Si on souhaite am\u00e9liorer la pr\u00e9cision, nous pouvons modifier la valeur de r\u00e9f\u00e9rence du convertisseur de l&rsquo;arduino (pin Aref) afin de le calibrer sur une valeur plus faible, par exemple 1.5V. Cela permet des mesures jusqu&rsquo;\u00e0 2bars soit 1 m\u00e8tres. Du coup, notre plage de mesure repr\u00e9sente 50% de la plage d&rsquo;\u00e9chantillonnage (de 1 \u00e0 2 bars), ce qui correspond \u00e0 512 valeurs pour 10 m\u00e8tres soit une pr\u00e9cision d&rsquo;environ 2cm.<\/p>\n<p>Attention, cette customisation n&rsquo;est pas sans poser probl\u00e8me. A ne faire qu&rsquo;en connaissance de cause et acceptation du risque :<\/p>\n<ul>\n<li>La modification de la r\u00e9f\u00e9rence s&rsquo;applique \u00e0 toutes les entr\u00e9es du convertisseur<\/li>\n<li><span style=\"color: #ff0000;\">Le d\u00e9passement du seuil de r\u00e9f\u00e9rence lors d&rsquo;une mesure peut endommager le convertisseur<\/span><\/li>\n<\/ul>\n<p>Une solution moins risqu\u00e9e mais plus complexe \u00e0 mettre en \u0153uvre pourrait \u00eatre l&rsquo;utilisation d&rsquo;un amplificateur op\u00e9rationnel qui amplifierai la mesure mais saturerait \u00e0 la tension maximale, prot\u00e9geant alors le convertisseur.<\/p>\n<h2>L&rsquo;afficheur<\/h2>\n<p>Ici, pas de chichi, un bon vieux LCD1602 en I2C (1.96\u20ac pi\u00e8ce) connect\u00e9 directement \u00e0 l&rsquo;arduino avec la librairie <em><strong>LiquidCrystal_I2C.h<\/strong><\/em>.<\/p>\n<h2>L&rsquo;unit\u00e9 de traitement<\/h2>\n<p>Pas de contraintes de RAM ni de CPU. N&rsquo;importe quel Arduino fonctionnant en 5V fera l&rsquo;affaire (donc exit les arduino pro mini).<\/p>\n<p>J&rsquo;ai retenu un Arduino Nano 3 \u00e0 1.70\u20ac sur AliExpress.<\/p>\n<p>On ajoutera une puce NRF24L01+ (\u00e0 6\u20ac pour 10 pi\u00e8ces sur AliExpress) pour l&rsquo;int\u00e9gration au r\u00e9seau mySensors ainsi qu&rsquo;un r\u00e9gulateur 3.3V s\u00e9par\u00e9 pour alimenter correctement la puce NRF.<\/p>\n<h2>Le logiciel<\/h2>\n<h2>Librairies<\/h2>\n<p>Nous avons besoin des librairies :<\/p>\n<ul>\n<li>mySensors<\/li>\n<li>LiquidCrystal_I2C.h<\/li>\n<\/ul>\n<h2>Sp\u00e9cifique<\/h2>\n<h3>Affichage<\/h3>\n<p>On va afficher le niveau du puit en m\u00e8tres ainsi qu&rsquo;une \u00e9valuation en pourcentage (bas\u00e9 sur la hauteur max du puit. Cerise sur le gateau on \u00e9teint le r\u00e9tro-\u00e9clairage au bout de 30 secondes (GreenIT). Pour le rallumer, on appuie sur le bouton pr\u00e9vu \u00e0 cet effet. Du coup, une petite LED rouge en fa\u00e7ade permet d&rsquo;indiquer que le niveau du puits passe en dessous des 10% sans consulter l&rsquo;\u00e9cran.<\/p>\n<h3>Mesure<\/h3>\n<p>Il faudra quand m\u00eame une petite fonction pour r\u00e9aliser une mesure \u00e9chantillonn\u00e9e, moyenn\u00e9e et lui appliquer la formule pour obtenir le r\u00e9sultat escompt\u00e9.<\/p>\n<p>Les plus t\u00e9m\u00e9raires pourraient inclure une fonction de calibrage automatique avec un mesure \u00e0 niveau 0 et une mesure \u00e0 2m par exemple. Si on suppose que la relation entre mesure et pression est lin\u00e9aire, le tour est jou\u00e9.<\/p>\n<h3>Calibrage<\/h3>\n<p>Comme le capteur va d\u00e9j\u00e0 r\u00e9agir \u00e0 la pression ambiante (\u00e0 la mani\u00e8re d&rsquo;un barom\u00e8tre), il convient de le calibrer lors de sa mise en service. Pour faciliter cette \u00e9tape, je vais ajouter un petit potentiom\u00e8tre de 100KOhms sur une entr\u00e9e analogique afin de disposer d&rsquo;un offset r\u00e9glable \u00e0 l&rsquo;aide d&rsquo;un tournevis.<\/p>\n<h3>MySensors<\/h3>\n<p>On int\u00e8gre la librairie MySensors afin de remonter les mesures au serveur de domotique. Cela permet une consultation \u00e0 distance ainsi qu&rsquo;une historisation des valeurs.<\/p>\n<h3>Capteur de temp\u00e9rature<\/h3>\n<p>Puisqu&rsquo;on est en route, pourquoi s&rsquo;arr\u00eater l\u00e0. Et si on ajoutait un capteur pour mesurer la temp\u00e9rature ext\u00e9rieure par la m\u00eame occasion ? Allez hop, on ajoute un DS18B20 \u00e9tanche de 5m (3,42\u20ac) connect\u00e9 en OneWire sur l&rsquo;arduino.<\/p>\n<h1>Etape 4 : R\u00e9alisation<\/h1>\n<h2>Sch\u00e9ma<\/h2>\n<p>Facilit\u00e9 oblige, j&rsquo;ai utilis\u00e9 Fritzing. un logiciel OpenSource bien con\u00e7u qui permet de faire les sch\u00e9mas \u00e9lectroniques ainsi que pr\u00e9parer les PCB et le routage des pistes.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-420\" src=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_schema.png\" alt=\"Puits_v2_schema\" width=\"1677\" height=\"1143\" srcset=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_schema.png 1677w, https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_schema-300x204.png 300w, https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_schema-1024x698.png 1024w, https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_schema-768x523.png 768w, https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_schema-1536x1047.png 1536w\" sizes=\"auto, (max-width: 1677px) 100vw, 1677px\" \/><\/p>\n<h2>Circuit<\/h2>\n<p>En ajustant manuellement les pistes, j&rsquo;ai pr\u00e9par\u00e9 le PCB suivant que je n&rsquo;ai pas imprim\u00e9 faute de temps (et peut-\u00eatre de ma\u00eetrise technique du proc\u00e9d\u00e9). Allez, les plaques de prototypage c&rsquo;est pas si mal &#8230;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-421\" src=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_ci.png\" alt=\"Puits_v2_CI.png\" width=\"690\" height=\"813\" srcset=\"https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_ci.png 690w, https:\/\/kev-it.fr\/wp-content\/uploads\/2016\/06\/puits_v2_ci-255x300.png 255w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/p>\n<h2>Assemblage<\/h2>\n<p>La partie capteur est faite comme dans le site de r\u00e9f\u00e9rence :<\/p>\n<ul>\n<li>les condensateurs de filtrage sont soud\u00e9s sur les pattes du MPX<\/li>\n<li>les \u00ab\u00a0oreilles\u00a0\u00bb du MPX ont \u00e9t\u00e9 coup\u00e9e<\/li>\n<li>le MPX a \u00e9t\u00e9 plac\u00e9 dans un petit tube PVC et noy\u00e9 dans la r\u00e9sine Epoxy<\/li>\n<li>le tube PVC a \u00e9t\u00e9 coll\u00e9 sur le bouchon d&rsquo;une bouteille de produit vaisselle remplie d&rsquo;huile<\/li>\n<\/ul>\n<p>La partie centrale a \u00e9t\u00e9 rang\u00e9e dans une boite de d\u00e9rivation. Une d\u00e9coupe a \u00e9t\u00e9 r\u00e9alis\u00e9e pour l&rsquo;\u00e9cran LCD, la LED et le bouton.<\/p>\n<h2>Logiciel<\/h2>\n<p>Le logiciel a \u00e9t\u00e9 r\u00e9alis\u00e9 avec ArduinoIDE.<\/p>\n<pre>#include &lt;Wire.h&gt;\n#include &lt;OneWire.h&gt;\n#include &lt;DallasTemperature.h&gt;\n#include &lt;LiquidCrystal_I2C.h&gt;\n#include &lt;SPI.h&gt;\n#include &lt;MySensor.h&gt;\n\n#define LED_PIN_ALERT\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 3\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Pin for Alert LED\n#define BIAS_PIN\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 A7\n#define DIMMER_PIN\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 11\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Pin for led dimmer\n#define BTN_PIN\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 9\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Btn to turn backlight on\n#define SENSOR_PIN\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 A0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Pin for MPX sensor\n#define TEMPERATURE_PIN\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 2\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ DS18B20 OneWire Data Pin\n\n#define MAX_ATTACHED_DS18B20\u00a0\u00a0\u00a0 1\n#define LEVEL_THRESHOLD_PERCENT 5 \/\/ Alert threshold in percent to turn on LED\n#define MAX_HEIGHT_WELL_METER\u00a0\u00a0 6\u00a0 \/\/ Well height for percentage computing\n#define BACKLIGHT_DELAY_SEC\u00a0\u00a0\u00a0\u00a0 30\u00a0\u00a0\u00a0 \/\/ delay before screen backlight off\n#define REDRAW_DELAY_SEC\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 0.5\n#define SEND_DATA_DELAY_SEC\u00a0\u00a0\u00a0\u00a0 900 \/\/ Send data to mySensors Gateway every quarter hour\n#define SEND_TEMP_DELAY_SEC\u00a0\u00a0\u00a0\u00a0 15 \/\/ Send data to mySensors Gateway every quarter hour\n\n#define MAX_SAMPLES\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 64\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Number of collected sampled values\n#define MEDIAN_SAMPLES\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 32\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Number of median values used for average\n\n\/\/ Dimmer \/ Shift REgsiter\n#define CHILD_ID_LEVEL 1\n\/\/#define CHILD_ID_DIMMER 2\n#define CHILD_ID_TEMP 3\n\nMySensor gw;\n\/\/kublib_dimmer myDimmer(CHILD_ID_DIMMER);\nMyMessage msg(CHILD_ID_LEVEL, V_DISTANCE);\nMyMessage msgTemp(CHILD_ID_LEVEL, V_TEMP);\n\nint numSensors=0; \/\/ Supports multiple sensors connected\nboolean metric = true; \nfloat temp=0; \/\/ Global value that stores temperature\nOneWire ds(TEMPERATURE_PIN);\nDallasTemperature sensors(&amp;ds); \/\/ Pass the oneWire reference to Dallas Temperature.\n\n\/\/ Set the LCD I2C address\n\/\/ And the I2C pin mapping\nLiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); \u00a0\nboolean backlightOn = true;\nboolean btnLastState = HIGH;\nunsigned long timeRefBacklight = 0;\nunsigned long timeRefRedraw = 0;\nunsigned long timeRefSendData = 0;\nunsigned long timeRefSendTemp = 0;\nfloat samples[MAX_SAMPLES];\nint sampleIndex = 0;\nfloat waterHeight = 0;\nint waterLevel = 0;\nint digitalBias = analogRead(BIAS_PIN);\nfloat lastTemperature;\u00a0 \/\/ Stores last measured temperature\n\u00a0 \n\/\/ the setup routine runs once when you press reset:\nvoid setup() {\n\u00a0 \/\/ initialize serial communication at 9600 bits per second:\n\u00a0 Serial.begin(115200);\n\u00a0 lcd.begin(16,2);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ initialize the lcd \n\u00a0 \/\/ Initialize LED\n\u00a0 pinMode(LED_PIN_ALERT, OUTPUT);\n\u00a0 digitalWrite(LED_PIN_ALERT, HIGH);\n\u00a0 delay(300);\n\u00a0 \/\/ Initialize BTN\n\u00a0 pinMode(BTN_PIN, INPUT_PULLUP);\n\u00a0 \/\/ Initialize timer\n\u00a0 \/\/timeRefBacklight = millis();\n\u00a0 \/\/timeRefRedraw = millis();\n\u00a0 \/\/ Initialize MySensors\n\u00a0 lcd.home ();\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ go home\n\u00a0 lcd.clear();\n\u00a0 lcd.print (\"MySensors:\");\n\u00a0 lcd.setCursor ( 0, 1 );\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ go to the next line\n\u00a0 lcd.print (\"Radio init.\");\n\n\n\u00a0 \n\u00a0 gw.begin(incomingMessage);\n\u00a0 gw.sleep(1000);\n\u00a0 \/\/ Send the Sketch Version Information to the Gateway\n\u00a0 gw.sendSketchInfo(\"Kub Puits\", \"1.0\");\n\u00a0 \/\/myDimmer.initialize(&amp;gw, DIMMER_PIN);\n\u00a0 \/\/ Configure Level sensor\n\u00a0 gw.present(CHILD_ID_LEVEL, S_DISTANCE, \"Niveau Puits (cm)\");\n\n\u00a0 \/\/ Startup up the OneWire library\n\u00a0 sensors.begin();\n\u00a0 \/\/ requestTemperatures() will not block current thread\n\u00a0 sensors.setWaitForConversion(false);\n\u00a0 \/\/ Fetch the number of attached temperature sensors \u00a0\n\u00a0 numSensors = sensors.getDeviceCount();\n\n\u00a0 \/\/ Present all sensors to controller\n\u00a0 char label[14] = \"\";\n\u00a0 \n\u00a0 for (int i=0; i&lt;numSensors &amp;&amp; i&lt;MAX_ATTACHED_DS18B20; i++) {\u00a0 \u00a0\n\u00a0\u00a0\u00a0 sprintf(label, \"Temperature %d\", i + CHILD_ID_TEMP);\n\u00a0\u00a0\u00a0\u00a0 gw.present(i + CHILD_ID_TEMP, S_TEMP, label);\n\u00a0 }\n\u00a0 \n\u00a0 lcd.setCursor ( 0, 1 );\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ go to the next line\n\u00a0 lcd.print (\"Intialized !\u00a0\u00a0 \");\n\u00a0 gw.sleep(1000);\n\u00a0 checkTemperature();\n}\n\n\/\/ the loop routine runs over and over again forever:\nvoid loop() {\n\u00a0 gw.process(); \n\u00a0 \n\u00a0 \/\/myDimmer.check();\n\u00a0 \/\/ When MAX SAMPLES is reached\n\u00a0 \/\/\u00a0 we have enough values to compute a result\n\u00a0 if (sampleIndex &gt;= MAX_SAMPLES) {\n\u00a0\u00a0\u00a0 float sum = 0.0;\n\u00a0\u00a0\u00a0 \/\/ Sort the sampled data\n\u00a0\u00a0\u00a0 BubbleSort(samples, MAX_SAMPLES);\n\u00a0\u00a0\u00a0 \/\/ Use only the median values to compute average\n\u00a0\u00a0\u00a0 for (int i =(MAX_SAMPLES-MEDIAN_SAMPLES)\/2; i&lt;(MAX_SAMPLES-MEDIAN_SAMPLES)\/2 + MEDIAN_SAMPLES; i++)\n\u00a0\u00a0\u00a0\u00a0\u00a0 sum += samples[i];\n\u00a0\u00a0\u00a0 \/\/ Compute final values\n\u00a0\u00a0\u00a0 waterHeight = sum \/ MEDIAN_SAMPLES;\n\u00a0\u00a0\u00a0 waterHeight = (float)((int)(waterHeight *100) \/100.0);\n\u00a0\u00a0\u00a0 waterLevel = waterHeight \/ MAX_HEIGHT_WELL_METER * 100;\n\u00a0\u00a0\u00a0 \/\/ Reset index\n\u00a0\u00a0\u00a0 sampleIndex = 0;\n\u00a0\u00a0\u00a0 \/\/ Send value\n\u00a0\u00a0\u00a0 if ((millis() - timeRefSendData) &gt; (SEND_DATA_DELAY_SEC * 1000)) {\n\u00a0\u00a0\u00a0\u00a0\u00a0 gw.send(msg.set((int)(waterHeight*100))); \/\/ niveau d'eau en cm\n\u00a0\u00a0\u00a0\u00a0 \u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0 timeRefSendData = millis();\n\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0 digitalBias = analogRead(BIAS_PIN);\n\u00a0 \n\u00a0 }\n\u00a0 if ((millis() - timeRefSendTemp) &gt;= (SEND_TEMP_DELAY_SEC * 1000)) {\n\u00a0\u00a0\u00a0 checkTemperature();\n\u00a0\u00a0\u00a0 timeRefSendTemp = millis();\n\u00a0 }\n\u00a0 \/\/ Load sensor data to sampling array\n\u00a0 int value = analogRead(SENSOR_PIN);\n\u00a0 samples[sampleIndex] = getWaterHeight(getPressure(value), digitalBias);\n\u00a0 sampleIndex++;\n\u00a0 \n\u00a0 \/\/ Turn light off after delay (GreenIT)\n\u00a0 if (((millis() - timeRefBacklight) &gt; (BACKLIGHT_DELAY_SEC * 1000)) &amp;&amp; (backlightOn)) {\n\u00a0\u00a0\u00a0 Serial.println(\"Turn Off\");\n\u00a0\u00a0\u00a0 backlightOn = false;\n\u00a0\u00a0\u00a0 lcd.noBacklight();\n\u00a0 }\n\u00a0 \n\u00a0 \/\/ Turn light ON on btn press\n\u00a0 if ((digitalRead(BTN_PIN) == LOW) &amp;&amp; (btnLastState == HIGH)) {\n\u00a0\u00a0\u00a0 Serial.println(\"Turn On\");\n\u00a0\u00a0\u00a0 btnLastState = LOW; \/\/ Manage simple debounce\n\u00a0\u00a0\u00a0 backlightOn = true;\n\u00a0\u00a0\u00a0 lcd.backlight();\n\u00a0\u00a0\u00a0 timeRefBacklight = millis();\n\u00a0 }\n\n\u00a0 \/\/ Manage simple debounce\n\u00a0 if ((digitalRead(BTN_PIN) == HIGH) &amp;&amp; (btnLastState == LOW)){\n\u00a0\u00a0\u00a0 btnLastState = HIGH;\n\u00a0 }\n\u00a0 \n\u00a0 \/\/ If light on redraw screen according refresh rate\n\u00a0 if (backlightOn &amp;&amp; ((millis() - timeRefRedraw) &gt; (REDRAW_DELAY_SEC * 1000))) {\n\u00a0\u00a0\u00a0 char strResult[5]=\"\";\n\u00a0\u00a0\u00a0 char strTemp[6]=\"\";\n\u00a0\u00a0 \u00a0\n\u00a0\u00a0\u00a0 \/\/ftoa(strResult,waterHeight, 3);\n\u00a0\u00a0\u00a0 dtostrf(waterHeight, 3, 1, strResult);\n\u00a0\u00a0\u00a0 dtostrf(lastTemperature, 4, 1, strTemp);\n\u00a0\u00a0 \u00a0\n\u00a0\u00a0\u00a0 \/\/ Show data on LCD Screen\n\u00a0\u00a0\u00a0 lcd.home ();\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ go home\n\u00a0\u00a0\u00a0 lcd.clear();\n\u00a0\u00a0\u00a0 lcd.print (\"Puits: \");\n\u00a0\u00a0\u00a0 lcd.print (strResult);\n\u00a0\u00a0\u00a0 lcd.print (\"m \");\n\u00a0\u00a0\u00a0 lcd.print (waterLevel);\n\u00a0\u00a0\u00a0 lcd.print (\"%\");\n\u00a0\u00a0 \u00a0\n\u00a0\u00a0\u00a0 lcd.setCursor ( 0, 1 );\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ go to the next line\n\u00a0\u00a0\u00a0 lcd.print (\"Temp Ext: \");\n\u00a0\u00a0\u00a0 lcd.print (strTemp);\n\u00a0\u00a0\u00a0 lcd.print (\"C\");\n\u00a0\u00a0\u00a0 timeRefRedraw = millis();\n\u00a0 }\n\n\u00a0 \/\/ Manage led alert\n\u00a0 if (waterLevel &lt; LEVEL_THRESHOLD_PERCENT) {\n\u00a0\u00a0\u00a0 digitalWrite(LED_PIN_ALERT, HIGH);\n\u00a0 } else {\n\u00a0\u00a0\u00a0 digitalWrite(LED_PIN_ALERT, LOW);\n\u00a0 }\n\n\u00a0 \n}\n\nvoid incomingMessage(const MyMessage &amp;message) {\n\u00a0 \/\/myDimmer.processMessage(message);\n}\n\n\/\/ takes a A\/D sample (0 to 1024) based on 5V ref\n\/\/\u00a0 to compute pressure on MPX sensor\nfloat getPressure(int analogValue) {\n\u00a0 \/\/ Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):\n\u00a0 float value = analogValue * (5 \/ 1023.0);\n\u00a0 \/\/ print out the value you read:\n\u00a0 return ((value \/ 5) - 0.04) \/ 0.0012858;\n}\n\n\/\/ Compute water level in meters according to pressure (KPa)\nfloat getWaterHeight(float pressureKPa, int digitalBias) {\n\u00a0 \/\/ Remove 100KPa for current air pressure (1 bar)\n\u00a0 \/\/ And convert from KPa to meters (10 meter of water = 1bar)\n\u00a0 float bias = ((float)(digitalBias)-512.0) \/ 100.0 \/ 4;\n\u00a0 return ((pressureKPa - 100) \/ 10) + bias;\n\u00a0 \n}\n\nvoid BubbleSort(float *num, int numLength)\n{\n\u00a0\u00a0\u00a0\u00a0\u00a0 int i, j, flag = 1;\u00a0\u00a0\u00a0 \/\/ set flag to 1 to start first pass\n\u00a0\u00a0\u00a0\u00a0\u00a0 float temp;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ holding variable\n\u00a0\u00a0\u00a0\u00a0\u00a0 for(i = 1; (i &lt;= numLength) &amp;&amp; flag; i++)\n\u00a0\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 flag = 0;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for (j=0; j &lt; (numLength -1); j++)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (num[j+1] &gt; num[j])\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ ascending order simply changes to &lt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 { \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 temp = num[j];\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ swap elements\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 num[j] = num[j+1];\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 num[j+1] = temp;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 flag = 1;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ indicates that a swap occurred.\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0\u00a0 return;\u00a0\u00a0 \/\/arrays are passed to functions by address; nothing is returned\n}\n\n\nvoid checkTemperature() {\n\u00a0 \/\/\n\u00a0 \/\/ Manage temperature\n\u00a0 \/\/\n\u00a0 \/\/ Fetch temperatures from Dallas sensors\n\u00a0 sensors.requestTemperatures();\n\n\u00a0 \/\/ query conversion time and sleep until conversion completed\n\u00a0 int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());\n\u00a0 \/\/ sleep() call can be replaced by wait() call if node need to process incoming messages (or if node is repeater)\n\u00a0 gw.sleep(conversionTime);\n\n\u00a0 \/\/ Read temperatures and send them to controller \n\u00a0 for (int i=0; i&lt;numSensors &amp;&amp; i&lt;MAX_ATTACHED_DS18B20; i++) {\n\u00a0\n\u00a0\u00a0\u00a0 \/\/ Fetch and round temperature to one decimal\n\u00a0\u00a0\u00a0 float temperature = static_cast&lt;float&gt;(static_cast&lt;int&gt;((gw.getConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) \/ 10.;\n\u00a0\u00a0\u00a0 Serial.print(\"Temperature for sensor \");\n\u00a0\u00a0\u00a0 Serial.print(i);\n\u00a0\u00a0\u00a0 Serial.print(\" :\");\n\u00a0\u00a0\u00a0 Serial.println(temperature);\n\u00a0\u00a0\u00a0 \/\/ Only send data if temperature has changed and no error\n\u00a0\u00a0\u00a0 if (temperature != -127.00 &amp;&amp; temperature != 85.00) {\n\u00a0\u00a0 \u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Send in the new temperature\n\u00a0\u00a0\u00a0\u00a0\u00a0 gw.send(msgTemp.setSensor(i).set(temperature,1));\n\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Save new temperatures for next compare\n\u00a0\u00a0\u00a0\u00a0\u00a0 lastTemperature=temperature;\n\u00a0\u00a0\u00a0 }\n\u00a0 }\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>C&rsquo;est toujours utile de savoir quel est le niveau de son puit ou de son r\u00e9servoir. Et puis, il faut bien bien trouver des choses un peu int\u00e9ressantes \u00e0 faire avec les Arduino \ud83d\ude09 On va essayer de mener l&rsquo;exp\u00e9rience sous la forme d&rsquo;un mini-projet Etape 1 : Expression de besoin Mesurer le niveau d&rsquo;un [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[36,38],"class_list":["post-219","post","type-post","status-publish","format-standard","hentry","category-electronique","tag-electronique","tag-mysensors"],"_links":{"self":[{"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/posts\/219","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/comments?post=219"}],"version-history":[{"count":1,"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/posts\/219\/revisions"}],"predecessor-version":[{"id":672,"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/posts\/219\/revisions\/672"}],"wp:attachment":[{"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/media?parent=219"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/categories?post=219"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kev-it.fr\/index.php\/wp-json\/wp\/v2\/tags?post=219"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}