From 767333aa36c4b68802f5a4b8f4026ffa86eef818 Mon Sep 17 00:00:00 2001 From: Christian Zufferey Date: Mon, 20 Jul 2020 19:19:33 +0200 Subject: [PATCH] =?UTF-8?q?Commenc=C3=A9=20le=20projet=20pet=5Ftracker=5F2?= =?UTF-8?q?,=20une=20nouvelle=20=C3=A9volution=20avec=20mes=20nouveaux=20o?= =?UTF-8?q?utils=20de=20l'ancien=20pet-tracker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DeepSleep/Pet_tracker_2/0_cron.lua | 49 +++ DeepSleep/Pet_tracker_2/0_http_post.lua | 45 ++ DeepSleep/Pet_tracker_2/0_rtelnet1.lua | 148 +++++++ DeepSleep/Pet_tracker_2/0_tst5_socat.lua | 115 +++++ DeepSleep/Pet_tracker_2/README.md | 163 +++++++ DeepSleep/Pet_tracker_2/_secrets_project.lua_ | 35 ++ DeepSleep/Pet_tracker_2/_secrets_wifi.lua_ | 18 + DeepSleep/Pet_tracker_2/_zlocal_cmd.txt | 79 ++++ DeepSleep/Pet_tracker_2/_zremote_cmd.txt | 301 +++++++++++++ DeepSleep/Pet_tracker_2/a.lua | 40 ++ DeepSleep/Pet_tracker_2/a_test_power_wifi.lua | 57 +++ DeepSleep/Pet_tracker_2/boot.lua | 65 +++ DeepSleep/Pet_tracker_2/cat.lua | 18 + DeepSleep/Pet_tracker_2/dir.lua | 31 ++ DeepSleep/Pet_tracker_2/dir3.lua | 52 +++ DeepSleep/Pet_tracker_2/dsleep.lua | 64 +++ DeepSleep/Pet_tracker_2/head.lua | 19 + DeepSleep/Pet_tracker_2/initz.lua | 86 ++++ DeepSleep/Pet_tracker_2/luatool.py | 408 ++++++++++++++++++ DeepSleep/Pet_tracker_2/repair.lua | 16 + DeepSleep/Pet_tracker_2/restart.lua | 12 + .../Pet_tracker_2/schemas/water-level.fzz | Bin 0 -> 77042 bytes DeepSleep/Pet_tracker_2/set_time.lua | 24 ++ DeepSleep/Pet_tracker_2/upload_s.sh | 48 +++ DeepSleep/Pet_tracker_2/wifi_info.lua | 45 ++ DeepSleep/Pet_tracker_2/wifi_init.lua | 92 ++++ zflash-esp-m3.sh | 44 ++ zflash-eps01.sh => zflash-esp01.sh | 14 +- 28 files changed, 2081 insertions(+), 7 deletions(-) create mode 100644 DeepSleep/Pet_tracker_2/0_cron.lua create mode 100644 DeepSleep/Pet_tracker_2/0_http_post.lua create mode 100644 DeepSleep/Pet_tracker_2/0_rtelnet1.lua create mode 100644 DeepSleep/Pet_tracker_2/0_tst5_socat.lua create mode 100644 DeepSleep/Pet_tracker_2/README.md create mode 100644 DeepSleep/Pet_tracker_2/_secrets_project.lua_ create mode 100644 DeepSleep/Pet_tracker_2/_secrets_wifi.lua_ create mode 100644 DeepSleep/Pet_tracker_2/_zlocal_cmd.txt create mode 100644 DeepSleep/Pet_tracker_2/_zremote_cmd.txt create mode 100644 DeepSleep/Pet_tracker_2/a.lua create mode 100644 DeepSleep/Pet_tracker_2/a_test_power_wifi.lua create mode 100644 DeepSleep/Pet_tracker_2/boot.lua create mode 100644 DeepSleep/Pet_tracker_2/cat.lua create mode 100644 DeepSleep/Pet_tracker_2/dir.lua create mode 100644 DeepSleep/Pet_tracker_2/dir3.lua create mode 100644 DeepSleep/Pet_tracker_2/dsleep.lua create mode 100644 DeepSleep/Pet_tracker_2/head.lua create mode 100644 DeepSleep/Pet_tracker_2/initz.lua create mode 100755 DeepSleep/Pet_tracker_2/luatool.py create mode 100644 DeepSleep/Pet_tracker_2/repair.lua create mode 100644 DeepSleep/Pet_tracker_2/restart.lua create mode 100644 DeepSleep/Pet_tracker_2/schemas/water-level.fzz create mode 100644 DeepSleep/Pet_tracker_2/set_time.lua create mode 100644 DeepSleep/Pet_tracker_2/upload_s.sh create mode 100644 DeepSleep/Pet_tracker_2/wifi_info.lua create mode 100644 DeepSleep/Pet_tracker_2/wifi_init.lua create mode 100755 zflash-esp-m3.sh rename zflash-eps01.sh => zflash-esp01.sh (63%) diff --git a/DeepSleep/Pet_tracker_2/0_cron.lua b/DeepSleep/Pet_tracker_2/0_cron.lua new file mode 100644 index 0000000..172b16e --- /dev/null +++ b/DeepSleep/Pet_tracker_2/0_cron.lua @@ -0,0 +1,49 @@ +-- Petit script pour faire office de crontab pour les mesures +print("\n 0_cron.lua zf200705.2247 \n") + +cron1=tmr.create() +cron1:alarm(15*1000, tmr.ALARM_AUTO, function() + -- if verbose then print("cron1........................") end + -- if verbose then gpio.write(zLED, gpio.LOW) tmr.delay(10000) gpio.write(zLED, gpio.HIGH) end + + + -- rt_launch() + + + -- http_post(influxdb_url,"energy,value=test1_"..yellow_id.." val=1") + + -- http_post(influxdb_url,"energy,memory=cron1_"..yellow_id.." ram="..node.heap()) + + -- if yellow_id == 60 then http_post(influxdb_url,"energy,compteur=3 puissance="..zpower/1000) end + -- if yellow_id == 64 then http_post(influxdb_url,"energy,compteur=4 puissance="..zpower/1000) end + + + if yellow_id == 69 then + local zmes="bolo_ruru,capteur="..node_id.." level="..zlevel + zmes=zmes.."\n".."bolo_ruru,capteur="..node_id.." hauteur="..zlength + http_post(influxdb_url,zmes) + end + + + + + + + -- http_post(influxdb_url,"energy,value=test2_"..yellow_id.." val=2") + -- http_post(influxdb_url,"energy,value=test3_"..yellow_id.." val=3") + -- http_post(influxdb_url,"energy,value=test4_"..yellow_id.." val=4") + + + -- f = "0_zdyndns.lua" if file.exists(f) then dofile(f) end + -- f=nil + + -- if verbose then print("End cron:") end + collectgarbage() + -- if verbose then print(node.heap()) end +end) + + +--[[ +cron1:stop() +cron1:start() +]] diff --git a/DeepSleep/Pet_tracker_2/0_http_post.lua b/DeepSleep/Pet_tracker_2/0_http_post.lua new file mode 100644 index 0000000..7f847bc --- /dev/null +++ b/DeepSleep/Pet_tracker_2/0_http_post.lua @@ -0,0 +1,45 @@ +-- Petit script pour envoyer en // es valeurs sur un serveur WEB (InfluxDB) +-- via un http POST à travers un FIFO + +if verbose then print("\n 0_http_post.lua zf200625.1137 \n") end + +t_zurl={} t_zarg={} f_zpost=false + +-- function tprint(t) +-- for key,value in pairs(t) do print(key, value) end +-- end + +function zpost() + f_zpost=true local zurl=t_zurl[1] local zarg=t_zarg[1] + -- zarg=zarg.."\n".."energy,value=nb_waiting_"..yellow_id.." val="..#t_zurl +-- zarg=zarg.."\n".."energy,memory=zpost_"..yellow_id.." ram="..node.heap() + if verbose then print("zurl: "..zurl) end if verbose then print("zarg: "..zarg) end + http.post(zurl, 'Content-Type: application/x-www-form-urlencoded\r\n', zarg, function(code, data) + if (code < 0) then + print("HTTP request failed") + print("zuzu", code, data) + if debug_rec~=nil then debug_rec("HTTP request failed: ", code, data) end + else + if verbose then print(code, data) end + end + table.remove(t_zurl, 1) table.remove(t_zarg, 1) + if t_zurl[1]==nil then + f_zpost=false + else + zpost() + end + -- if verbose then print("End zpost:") end + collectgarbage() + -- if verbose then print(node.heap()) end + end) +end + +function http_post(zurl,zarg) + if #t_zurl <=10 then table.insert(t_zurl, zurl) table.insert(t_zarg, zarg) end + -- if verbose then print("Nb wait: "..#t_zurl) print(node.heap()) end + -- if verbose then print("t_zurl:") tprint(t_zurl) print("t_zarg:") tprint(t_zarg) end + if f_zpost==false then zpost() end + -- if verbose then print("End http_post:") end + collectgarbage() + -- if verbose then print(node.heap()) end +end diff --git a/DeepSleep/Pet_tracker_2/0_rtelnet1.lua b/DeepSleep/Pet_tracker_2/0_rtelnet1.lua new file mode 100644 index 0000000..f1efa9c --- /dev/null +++ b/DeepSleep/Pet_tracker_2/0_rtelnet1.lua @@ -0,0 +1,148 @@ +-- script telnet pour le socat + +function telnet_listener(socket) + print("\n 0_rtelnet1.lua zf200621.2309 \n") + + -- node, table, tmr, uwrite, tostring = + -- node, table, tmr, uart.write, tostring + + print("................telnet_listener") + -- insert, remove, concat, heap, gc = + -- table.insert, table.remove, table.concat, node.heap, collectgarbage + fifo1, fifo1l, fifo2, fifo2l = {}, 0, {}, 0 + -- local s -- s is a copy of the TCP socket if and only if sending is in progress + + function flushGarbage() + if node.heap() < 13440 then collectgarbage() end + end + + function sendLine() + if not s then return end + + if fifo2l + fifo1l == 0 then -- both FIFOs empty, so clear down s + s = nil + return + end + flushGarbage() + if #fifo2 < 4 then -- Flush FIFO1 into FIFO2 + table.insert(fifo2,table.concat(fifo1)) + fifo2l, fifo1, fifo1l = fifo2l + fifo1l, {}, 0 + end + rec = table.remove(fifo2,1)..(table.remove(fifo2,1) or '') ..(table.remove(fifo2,1) or '') .. (table.remove(fifo2,1) or '') + fifo2l = fifo2l - #rec + flushGarbage() + if srv_rt:getpeer()~=nil then + s:send(rec) + end + end + + local F1_SIZE = 256 + + function queueLine(str) + while #str > 0 do -- this is because str might be longer than the packet size! + k, l = F1_SIZE - fifo1l, #str + if #fifo1 >= 32 or (k < l and k < 16) then + table.insert(fifo2, table.concat(fifo1)) + fifo2l, fifo1, fifo1l, k = fifo2l + fifo1l, {}, 0, F1_SIZE + end + if l > k+16 then -- also tolerate a size overrun of 16 bytes to avoid a split + chunk, str = str:sub(1,k), str:sub(k+1) + else + chunk, str = str, '' + end + table.insert(fifo1, chunk) + fifo1l = fifo1l + #chunk + end + if not s and socket then + s = socket + sendLine() + else + flushGarbage() + end + end + + function receiveLine(s, line) + node.input(line) + end + + function disconnect(_,zerr) + node.output(nil) + gpio.write(zLED, gpio.HIGH) + print("................disconnect") + + if socket~=nil then + -- if http_post~=nil then http_post(influxdb_url,"energy,memory=srv_rt_no_nil_"..yellow_id.." ram="..node.heap()) end + print("................disconnect 2e", socket, socket:getpeer()) + socket:on("connection", nil) + socket:on("reconnection", nil) + socket:on("disconnection", nil) + socket:on("receive", nil) + socket:on("sent", nil) + + socket=nil + end + + -- fifo1, fifo1l, fifo2, fifo2l, s = nil, nil, nil, nil, nil + print("disconnected... "..zerr..", "..node.heap()) + -- if debug_rec~=nil then debug_rec("disconnect, disconnected, "..zerr..", "..node.heap()) end + + + -- node, table, tmr, uwrite, tostring = nil, nil, nil, nil, nil + -- insert, remove, concat, heap, gc = nil, nil, nil, nil, nil + -- fifo1, fifo1l, fifo2, fifo2l = nil, nil, nil, nil + -- rec = nil + -- s = nil + -- socket = nil + -- flushGarbage = nil + -- sendLine = nil + -- queueLine = nil + -- receiveLine = nil + -- zconnection = nil + -- disconnect = nil + -- -- telnet_listener=nil + + + + fifo1, fifo1l, fifo2, fifo2l = nil, nil, nil, nil + rec = nil + k = nil + l = nil + s = nil + chunk = nil + socket = nil + flushGarbage = nil + sendLine = nil + queueLine = nil + receiveLine = nil + zconnection = nil + disconnect = nil + telnet_listener=nil + srv_rt=nil + + + -- collectgarbage() + rt_connect() + end + + --zzz + function zconnection(s) + print("socket: ",socket) + if socket~=nil then + -- if http_post~=nil then http_post(influxdb_url,"energy,memory=srv_rt_no_nil_"..yellow_id.." ram="..node.heap()) end + print(socket:getpeer()) + end + + local zstr="zconnection, Oups, on ne devrait jamais passer par là to NodeMCU world." + print(zstr) if debug_rec~=nil then debug_rec(zstr) end + socket=nil + end + + socket:on("connection", zconnection) + socket:on("receive", receiveLine) + socket:on("disconnection", disconnect) + socket:on("sent", sendLine) + + node.output(queueLine, 0) + -- print(queueLine, 0) +end + diff --git a/DeepSleep/Pet_tracker_2/0_tst5_socat.lua b/DeepSleep/Pet_tracker_2/0_tst5_socat.lua new file mode 100644 index 0000000..0daa852 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/0_tst5_socat.lua @@ -0,0 +1,115 @@ +--[[ +tests connection reverse telnet commande à faire tourner sur le GATEWAY ! + +1ere console +pour une liaison directe: +socat TCP-LISTEN:23064,fork,reuseaddr STDIO +pour une console sur un port: +socat TCP-LISTEN:23043,reuseaddr,fork TCP-LISTEN:23000,reuseaddr,bind=127.0.0.1 + +2e console +telnet -r localhost 23000 +]] + +--[[ +tests connection reverse telnet commande à faire tourner sur le GATEWAY ET sur sa MACHINE ! + +1ere console sur le GATEWAY +socat TCP-LISTEN:23043,reuseaddr,fork TCP-LISTEN:23000,reuseaddr,bind=127.0.0.1 + +2e console sur sa MACHINE +ssh -L 23000:localhost:23000 user@GATEWAY + +3e console sur sa MACHINE (~.return pour sortir !) +telnet -r localhost 23000 +ou sur MAC +telnet -rN localhost 23000 +]] + +print("\n 0_tst5_socat.lua zf200628.1458 \n") + +function rt_connect() + -- print("................rt_connect") + collectgarbage() + local zlaps=tmr.now()/1000000-ztime_connect + -- print("time of retry connect... "..zlaps) + -- if debug_rec~=nil then debug_rec("time of retry connect... "..zlaps..", "..node.heap()) end + if zlaps>1 then + local zstr="trying connect to "..console_host..":"..console_port..", "..node.heap() + -- if debug_rec~=nil then debug_rec(zstr) end + if verbose==verbose then + gpio.write(zLED, gpio.LOW) tmr.delay(10000) gpio.write(zLED, gpio.HIGH) + -- print(zstr) + end + if http_post~=nil then http_post(influxdb_url,"energy,memory=socat_try_con_"..yellow_id.." ram="..node.heap()) end + ztime_connect=tmr.now()/1000000 + + srv_rt=nil + srv_rt = net.createConnection(net.TCP, 0) + + srv_rt:on("connection", function(sck) + print("................connection") + if debug_rec~=nil then debug_rec("rt_connect, srv_rt:on, connected on, "..node.heap()) end + collectgarbage() + -- if verbose then + gpio.write(zLED, gpio.LOW) + print("connected on "..console_host..":"..console_port..", "..node.heap()) + print(node.heap()) + -- end + if http_post~=nil then http_post(influxdb_url,"energy,memory=socat_connected_"..yellow_id.." ram="..node.heap()) end + dofile("0_rtelnet1.lua") + telnet_listener(sck) + print("Welcome to NodeMCU world.") + end) + + srv_rt:on("reconnection", function(sck) + -- print(";;;;;;;;;;;;;;;;reconnection") + srv_rt:on("connection", nil) + srv_rt:on("reconnection", nil) + end) + + -- srv_rt:on("disconnection", function(sck) + -- print(";;;;;;;;;;;;;;;;disconnection") + -- end) + -- + -- srv_rt:on("receive", function(sck) + -- print(";;;;;;;;;;;;;;;;receive") + -- end) + -- + -- srv_rt:on("sent", function(sck) + -- print(";;;;;;;;;;;;;;;;sent") + -- end) + + + srv_rt:connect(console_port,console_host) + else + print("on ne se reconnecte pas vite 1x...") + end + collectgarbage() +end + + + + +function rt_launch() + -- if http_post~=nil then http_post(influxdb_url,"energy,memory=tmr_socat1_"..yellow_id.." ram="..node.heap()) end + if srv_rt~=nil then + -- if http_post~=nil then http_post(influxdb_url,"energy,memory=srv_rt_no_nil_"..yellow_id.." ram="..node.heap()) end + if console_port ~= srv_rt:getpeer() then + rt_connect() + end + else + rt_connect() + end +end + + +tmr_socat1=tmr.create() +tmr_socat1:alarm(20*1000, tmr.ALARM_AUTO , rt_launch) + + +ztime_connect=tmr.now()/1000000-10 + +rt_launch() + +print("Revers telnet server running...\n") diff --git a/DeepSleep/Pet_tracker_2/README.md b/DeepSleep/Pet_tracker_2/README.md new file mode 100644 index 0000000..ba6e000 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/README.md @@ -0,0 +1,163 @@ +# Mesure de hauteur d'eau dans un réservoir + +zf200627.1330 + + + +## Table of Contents +* [Buts](#buts) +* [Astuces de mesures de la distance au moyen du senseur ultrason](#astuces-de-mesures-de-la-distance-au-moyen-du-senseur-ultrason) + * [Schéma](#schéma) + * [Astuces](#astuces) +* [Installation](#installation) +* [Utilisation](#utilisation) + * [Upload Lua code](#upload-lua-code) + * [Secrets pour le projet](#secrets-pour-le-projet) + * [Rename initz.lua pour le boot automatique](#rename-initzlua-pour-le-boot-automatique) + * [Utilisation de la console du NodeMCU en remote](#utilisation-de-la-console-du-nodemcu-en-remote) + * [Visualisation sur Grafana/InfluxDB](#visualisation-sur-grafanainfluxdb) + + +## Buts + +Petit projet pour mesurer la hauteur d'eau dans un réservoir de 100l au moyen d'un senseur à ultrason utilisé pour de la robotique récréative. + +Le but est de mesurer la distance entre le haut du bidon et la surface de l'eau dans le bidon et ainsi pouvoir en déduire le pourcentage de remplissage du bidon. + +![Image](https://raw.githubusercontent.com/zuzu59/NodeMCU_Lua/master/Mesures/water-level/water-level_ruru_1/img/20200625_163032.jpg) +Senseur à ultrason, très bon marché, permettant de mesurer la distance + + +![Image](https://raw.githubusercontent.com/zuzu59/NodeMCU_Lua/master/Mesures/water-level/water-level_ruru_1/img/20200625_160818.jpg) +NodeMCU autonome, alimenté ici par une batterie, faisant la lecture de la hauteur d'eau et envoyant le résultat dans une DB InfluxDB via le WIFI + + +![Image](https://raw.githubusercontent.com/zuzu59/NodeMCU_Lua/master/Mesures/water-level/water-level_ruru_1/img/20200625_164022.jpg) +Banc test dans le jardin pour vérifier le bon fonctionnement du système + +![Image](https://raw.githubusercontent.com/zuzu59/NodeMCU_Lua/master/Mesures/water-level/water-level_ruru_1/img/grafana2020-06-25.16.54.32.png) +Graphique obtenu lors du banc test avec de l'eau dans le jardin + + +

+On peut voir ici, avec ce projet assez complet, toutes les possibilités offertes de la programmation des NodeMCU en LUA, en mode événementiel.
+Choses qui ne seraient pas possible si on l'avait fait en C++ (mode Arduino), comme par exemple: + +* crontab, horloge pour les mesures +* envoi des données sur la DB InfluxDB +* serveur reverse TELNET, traversant tous les routers sans devoir en modifier la configuration, permettant d'accéder à la console série (USB) du NodeMCU + +Toutes les fonctions sont bien séparées dans des scripts, cela *complexifie* le projet mais ce qui facilite la portabilité entre les projets et aussi sa mise au point. + + + +## Astuces de mesures de la distance au moyen du senseur ultrason + +Dans ce projet il y a 1x NodeMCU qui mesure la hauteur d'eau dans le bidon au moyen d'un senseur à ultrason utilisé pour de la robotique récréative très bon marché, 0.70FS + +https://www.aliexpress.com/item/32477198302.html + +https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf + +Il n'y a pas de *module* NodeMCU pour ce senseur, mais son utilisation en Lua est vraiment très simple, il suffit juste d'envoyer une *pulse* de 10uS sur la pin *trig* et de *connecter* une interruption du NodeMCU sur la pin *echo*.
+Après une simple règle de trois en relation avec la vitesse du son dans l'air et on a la distance en cm. + + +### Schéma + +![Image](https://raw.githubusercontent.com/zuzu59/NodeMCU_Lua/master/Mesures/water-level/water-level_ruru_1/schemas/schema.png) + +Le schéma est vraiment très simple ! + + +### Astuces + +* La seul problématique dans ce projet c'est que le senseur DOIT absolument être alimenté en 5V et que le NodeMCU lui est en 3.3V.
+Il faut donc lui ajouter une petite résistance, R1, d'adaptation du niveau pour le signal pour l'interruption du NodeMCU. + + +## Installation + +Il faut *flasher* le NodeMCU avec ce firmware: + +https://github.com/zuzu59/NodeMCU_Lua/blob/master/Firmware/nodemcu-master-19-modules-2019-12-31-16-40-12-float.bin + + +Avec ces modules: + +https://github.com/zuzu59/NodeMCU_Lua/blob/master/Firmware/nodemcu-master-19-modules-2019-12-31-16-40-12-float.pdf + + +## Utilisation + +### Upload Lua code +Après avoir *flashé* le NodeMCU avec le bon *firmware* il faut télécharger tous les fichiers \*.lua sur le NodeMCU. + + +### Secrets pour le projet +Mais il faut aussi bien *remplir* et charger sur le NodeMCU, le fichier des secrets du projet: +``` +secrets_project.lua +``` + +ainsi que le fichier des secrets pour le WIFI +``` +secrets_wifi.lua +``` +Tout en sachant que les variables utilisées pour les secrets sont utiles pour: + +* **znode_chipid == nnn then**
+C'est l'id du NodeMCU que chaque NodeMCU ont gravé dans leur mémoire, on peut le lire avec cette commande: +``` +=node.chipid() +``` + +* **node_id = "ttt"**
+C'est le nom de *fonction* du NodeMCU qui sera *visible* dans la DB InfluxDB + +* **yellow_id = nn**
+C'est le *numéro* du NodeMCU que l'on indique sur une *petite étiquette jaune collée* sur le NodeMCU. Ce *numéro* permet par la suite de connaitre très facilement le numéro du *port* utilisé pour le *reverse telnet* quand on veut accéder à la console série du NodeMCU + +* **-- thingspeak_url="http://api.thingspeak.com/update?api_key=kkk"**
+Pas utilisé dans ce projet + +* **influxdb_url="http://uuu:8086/write?db=ddd&u=admin&p=ppp"**
+Secrets utilisés pour envoyer des données sur le DB InfluxDB + +* **console_host = "uuu" console_port = 23000+yellow_id**
+Serveur utilisé pour le *tremplin* du reverse telnet utilisé pour accéder à la console série du NodeMCU au moyen d'un *socat*. L'information d'utilisation se trouve dans le fichier 0_tst5_socat.lua + +* **-- zdyndns_host = "hhh" zdyndns_port = nnn**
+Pas utilisé dans ce projet + + +### Rename initz.lua pour le boot automatique +Ne pas oublier après avoir vérifié que tout fonctionne bien de *renommer* le fichier **initz.lua** en **init.lua** afin que quand le NodeMCU puisse démarrer automatiquement le code et bien fonctionner de manière autonome. + + +### Utilisation de la console du NodeMCU en remote + +Très pratique pour le debug, on peut directement modifier le code source Lua du NodeMCU en remote via un *reverse telnet*. Plus d'info dans le fichier 0_tst5_socat.lua. +On peut aussi modifier le code Lua du NodeMCU en remote avec l'utilitaire *luatools.py* + + +### Visualisation sur Grafana/InfluxDB +![Image](https://raw.githubusercontent.com/zuzu59/NodeMCU_Lua/master/Mesures/water-level/water-level_ruru_1/img/grafana2020-06-25.16.54.32.png) +Graphique obtenu lors du banc test avec de l'eau dans le jardin + +La totale en détail + +https://github.com/zuzu59/docker-influxdb-grafana + + + + + +pense bête: + +``` +file.open("hello.lua","w+") +file.writeline([[print("hello nodemcu")]]) +file.writeline([[print(node.heap())]]) +file.close() +``` diff --git a/DeepSleep/Pet_tracker_2/_secrets_project.lua_ b/DeepSleep/Pet_tracker_2/_secrets_project.lua_ new file mode 100644 index 0000000..aa77c40 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/_secrets_project.lua_ @@ -0,0 +1,35 @@ +-- Petit script pour configurer les secrets dans ce projet +-- et que l'on n'aimerait pas être exportés sur Internet (github) +-- Il faut donc modifier le .gitignore avec secrets*.lua +-- il faut le renommer en 'secrets_project.lua' et sera exécuté +-- par 'wifi_init.lua' au moment du boot + +function secrets_project() + print("\n secrets_project.lua zf200625.1146 \n") + + zLED=4 zBTN=3 node_id = "generic" + znode_chipid=node.chipid() print("znode_chipid:",znode_chipid) + + if znode_chipid == iii then node_id = "sonoff_1" zLED=7 end + + if znode_chipid == iii then node_id = "sonoff_2" zLED=7 end + + if znode_chipid == iii then + node_id = "level1" + yellow_id = nn + -- thingspeak_url="http://api.thingspeak.com/update?api_key=kkk" + influxdb_url="http://uuu:8086/write?db=ddd&u=admin&p=ppp" + print("influxdb_url: "..influxdb_url) + console_host = "uuu" console_port = 23000+yellow_id + -- zdyndns_host = "hhh" zdyndns_port = nnn + end + + znode_chipid=nil + print("node_id: "..node_id..", console_port: "..console_port) +end +secrets_project() +secrets_project=nil + +--[[ +=node.chipid() +]] diff --git a/DeepSleep/Pet_tracker_2/_secrets_wifi.lua_ b/DeepSleep/Pet_tracker_2/_secrets_wifi.lua_ new file mode 100644 index 0000000..7b67d80 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/_secrets_wifi.lua_ @@ -0,0 +1,18 @@ +-- Petit script pour configurer les secrets utilisés pour le wifi +-- et que l'on n'aimerait pas être exportés sur Internet (github) +-- Il faut donc modifier le .gitignore avec eus_params* et secret* +-- il faut le renommer en 'secrets_wifi.lua' et sera exécuté +-- par 'wifi_init.lua' une fois pour la configuration du WIFI + +function secrets_wifi() + print("\n secrets_wifi.lua zf191222.2002 \n") + f= "eus_params.lua" if file.exists(f) then p = dofile(f) end + if p ~= nil then + cli_ssid = p.wifi_ssid cli_pwd = p.wifi_password p=nil + else + cli_ssid = "" cli_pwd = "" + end + ap_ssid="sss" ap_pwd="ppp" +end + +secrets_wifi() diff --git a/DeepSleep/Pet_tracker_2/_zlocal_cmd.txt b/DeepSleep/Pet_tracker_2/_zlocal_cmd.txt new file mode 100644 index 0000000..47aae1a --- /dev/null +++ b/DeepSleep/Pet_tracker_2/_zlocal_cmd.txt @@ -0,0 +1,79 @@ +zf200621.2337 + +srv_rt:on("connection", nil) + + + + +=node.heap() +rt_connect() + + fifo1, fifo1l, fifo2, fifo2l = nil, nil, nil, nil + rec = nil + k = nil + l = nil + s = nil + chunk = nil + socket = nil + flushGarbage = nil + sendLine = nil + queueLine = nil + receiveLine = nil + zconnection = nil + disconnect = nil + telnet_listener=nil + srv_rt=nil +=node.heap() + + + + + + + + + + +=node.heap() +dofile("0_rtelnet1.lua") +=node.heap() +telnet_listener=nil +=node.heap() +srv_rt=nil +=node.heap() + + + +for k,v in ipairs(debug.getregistry()) do print(k,v) end + + + +node.stripdebug(3) +node.compile('0_tst4_socat.lua') + +print(srv_rt) +print(srv_rt:getpeer()) +srv_rt:close() + +print(socket) +print(socket:getpeer()) +socket:close() + +verbose=true +node.output(nil) + +http_post(influxdb_url,"energy,value=test1_"..yellow_id.." val=1") print("toto",node.heap(),#t_zurl) + + +=srv_rt:getaddr() +tmr_socat1:unregister() +for k,v in pairs(_G) do print(k,v) end + +total_allocated, estimated_used = node.egc.meminfo() +print(total_allocated, estimated_used) + +print(console_port) + +srv_rt = nil collectgarbage() + + diff --git a/DeepSleep/Pet_tracker_2/_zremote_cmd.txt b/DeepSleep/Pet_tracker_2/_zremote_cmd.txt new file mode 100644 index 0000000..d6d2618 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/_zremote_cmd.txt @@ -0,0 +1,301 @@ +# Quelques commandes remote (luatool) à envoyer avec le plugin Atom-IDE-terminal de l'éditeur Atom +# zf200704.2310 + + +Todo à faire pour ce projet ! + +??? + +# si luatool ne marche pas sur le MAC, il faut lire ceci: +# https://docs.google.com/document/d/1q64uK3IMOgEDdKaIAttbYuFt4GuLQ06k3FLeyfCsWLg/edit#heading=h.bmefcu67uwj0 + +# raccourcis clavier +# CTRL+ALT+ENTER envoie au terminal la ligne de l'éditeur +# SHIT+CTRL+` ouvre le terminal (attention, ne pas oublier de copier le *path* dans le *tree* et le changer) +# ALT+CMD+F bascule entre le terminal et l'éditeur + +# définitions à faire AVANT ! +#export luatool_tty="/dev/cu.wchusbserial1410" + +#export zIP="192.168.0.182" +#export zport="23" + +export zIP="localhost" +export zport="23000" + + +# ouvrir et fermer (ALT+N+.) une session telnet sur le NodeMCU avec l'adresse zIP) +telnet -rN $zIP $zport +~. +--node.restart() +collectgarbage() +=node.heap() +for k,v in pairs(_G) do print(k,v) end + + +################################ +# commandes lua pour ce projet # +################################ +# pour les tests en direct sur la gateway +ssh ubuntu@www.zuzu-test.ml +socat TCP-LISTEN:23069,fork,reuseaddr STDIO +################################ +# pour les tests en remote +killall -9 ssh +ssh ubuntu@www.zuzu-test.ml killall -9 socat + +ssh ubuntu@www.zuzu-test.ml socat TCP-LISTEN:23069,reuseaddr,fork TCP-LISTEN:23000,reuseaddr,bind=127.0.0.1 & +# SHIFT+CMD+K SHIFT+CMD+K ALT+CMD+F + +watch -n 1 'ssh ubuntu@www.zuzu-test.ml netstat -nat |grep 230' + +# ALT+CMD+F CTRL+C ALT+CMD+F + +export zIP="localhost" +export zport="23000" + +ssh -N -L 23000:localhost:23000 ubuntu@www.zuzu-test.ml & + +telnet -rN $zIP $zport + +verbose=false +~. + +=node.heap() +verbose=true +node.restart() + +dofile("head.lua") +zhead("0_ultra_son.lua") + +./luatool.py --ip $zIP:$zport -f boot.lua +./luatool.py --ip $zIP:$zport -f 0_get_power.lua +./luatool.py --ip $zIP:$zport -f 0_cron.lua +./luatool.py --ip $zIP:$zport -f 0_http_post.lua +./luatool.py --ip $zIP:$zport -f 0_tst5_socat.lua +./luatool.py --ip $zIP:$zport -f 0_ultra_son.lua + +./luatool.py --ip $zIP:$zport --zrestart + + += node.bootreason() +# https://nodemcu.readthedocs.io/en/master/modules/node/#nodebootreason + +for k,v in pairs(_G) do print(k,v) end +verbose=true +#zdyn + + + + +./luatool.py --ip $zIP:$zport -f api_sonoff.html +http://192.168.0.182 + +./luatool.py --ip $zIP:$zport -f dir.lua + +telnet -rN $zIP $zport +verbose=false +verbose=true +~. + +=node.heap() +collectgarbage() +=node.heap() + + +dofile("dir.lua") +dir() +for k,v in pairs(_G) do print(k,v) end +dofile("wifi_info.lua") + +node.restart() + +~. +./luatool.py --ip $zIP:$zport -f head.lua +telnet -rN $zIP $zport +dofile("head.lua") +zhead("boot.lua") + + + +verbose=true +verbose=false +zsort_rssi() zshow() + + +#commandes luatool pour ce projet le .137 est à jour avec la nouvelle version du wifi ! +~. +./luatool.py --ip $zIP:$zport -l +./luatool.py --ip $zIP:$zport -f wifi_init.lua + + +./luatool.py --ip $zIP:$zport -f secrets_wifi.lua +./luatool.py --ip $zIP:$zport -f initz.lua -t init.lua +./luatool.py --ip $zIP:$zport -f boot.lua +./luatool.py --ip $zIP:$zport -f boot2.lua +./luatool.py --ip $zIP:$zport -f wifi_init.lua +./luatool.py --ip $zIP:$zport -f set_time.lua +./luatool.py --ip $zIP:$zport -f wifi_info.lua +./luatool.py --ip $zIP:$zport -f c.lua +./luatool.py --ip $zIP:$zport -f cat.lua +./luatool.py --ip $zIP:$zport -f flash_led_xfois.lua +./luatool.py --ip $zIP:$zport -f head.lua + +./luatool.py --ip $zIP:$zport -f b.lua +./luatool.py --ip $zIP:$zport -f web_srv2.lua +./luatool.py --ip $zIP:$zport -f z_index.html + + +./luatool.py --ip $zIP:$zport -f wifi_get_conf.html +./luatool.py --ip $zIP:$zport -f wifi_set_conf.html + + +./luatool.py --ip $zIP:$zport -f z_page1.html +./luatool.py --ip $zIP:$zport -f z_page2.html +./luatool.py --ip $zIP:$zport -f z_page3.html +./luatool.py --ip $zIP:$zport -f z_page4.html + +./luatool.py --ip $zIP:$zport --delete wifi_ap_start.lua +./luatool.py --ip $zIP:$zport --delete wifi_cli_conf.lua +./luatool.py --ip $zIP:$zport --delete wifi_cli_start.lua +./luatool.py --ip $zIP:$zport --delete dir.lua +./luatool.py --ip $zIP:$zport --delete initz.lua +./luatool.py --ip $zIP:$zport --delete wifi_conf.lua + + + +dofile("wifi_info.lua") +--node.restart() +for k,v in pairs(_G) do print(k,v) end +t=12 +t=nil +print(pcall(function () print("2"..t) end)) + + + +# ici c'est maintenant ;-) +~. +./luatool.py --ip $zIP:$zport -f b.lua +#./luatool.py --ip $zIP:$zport --zrestart +telnet -rN $zIP $zport +zsort_rssi() zshow() +print(ztrig_rssi) +ztrig_rssi=-1000 +ztrig_rssi=-90 + + + +# test de la gestion des erreurs +~. +./luatool.py --ip $zIP:$zport -f c.lua +#./luatool.py --ip $zIP:$zport --zrestart +telnet -rN $zIP $zport +status, err = pcall(function () dofile("c.lua") end) if status==false then print("Error: ",err) end +zerr=nil +zerr=1 +ztmr_tst_err:unregister() + + + + +ztmr_tst_err:unregister() +for k,v in pairs(_G) do print(k,v) end + +./luatool.py --ip $zIP --zrestart + + +# faire un cat d'un fichier sur le NodeMCU +dofile("cat.lua") +cat("boot2.lua") + + +# commandes luatool +./luatool.py -h +./luatool.py --ip $zIP:$zport -i +./luatool.py --ip $zIP:$zport -l +./luatool.py --ip $zIP:$zport -f toto.lua -d +./luatool.py --ip $zIP:$zport --delete toto.lua + + + +**************************************************************************** + + +# création des tunnels ssh +ATTENTION: dans un premier terminal ! +Pour Bolo à Ruchonnet: +ssh -t -L 2323:localhost:2323 ubuntu@www.zuzu-test.ml ssh -N -L 2323:192.168.8.102:23 ubuntu@localhost -p 20223 + +Pour le Crêt +ssh -N -L 2323:192.168.0.137:23 admin@z.zufferey.com -p 1822 +ssh -N -L 2323:192.168.0.122:23 admin@z.zufferey.com -p 1822 +ssh -N -L 2323:192.168.0.118:23 admin@z.zufferey.com -p 1822 + +ATTENTION: dans un deuxième terminal ! +export zIP="localhost" +export zport="2323" +telnet -rN $zIP $zport +~. +=node.heap() + +verbose=false +verbose=true + +dofile("dir2.lua") +dir() +filec("head.lua") + +dofile("head.lua") +zhead("dir2.lua") + +dofile("cat.lua") +cat("head.lua") + +~. +--node.restart() +=node.heap() + + +status, err = pcall(function () fonction_a_tester() end) if status==false then print("Error: ",err) end +status, err = pcall(function () toto() end) if status==false then print("Error: ",err) end + + + +Gestion de la passerelle SSH reverse +Puis depuis une autre console on peut faire directement pour aller sur l'OpiZ (ici le 20223): +ssh -t ubuntu@www.zuzu-test.ml ssh ubuntu@localhost -p 20223 + +ou + +1ère console +On établit le tunnel local avec l'OpiZ +ssh -N -L 20223:localhost:20223 ubuntu@www.zuzu-test.ml + + +2ème console, on utilise le tunnel OpiZ en local sur sa machine +pour aller sur l'OpiZ +ssh ubuntu@localhost -p 20223 + +pour copier sa clef SSH depuis un MAC +./ssh-copy-id -i ~/.ssh/id_rsa.pub 'ubuntu@localhost -p 20223' +après on devrait pouvoir se connecter sans devoir entrer son password à chaque fois +ssh ubuntu@localhost -p 20223 + +ou, on établit un tunnel pour telnet sur un NodeMCU +ssh -L 2323:192.168.0.137:23 ubuntu@localhost -p 20223 +puis +telnet -rN localhost 2323 + +ou, on établit un tunnel pour le rpimonotor sur l'OpiZ +ssh -N -L 8888:192.168.0.113:8888 ubuntu@localhost -p 20223 +puis +http://localhost:8888 + +ou, on établit un tunnel proxy Socket 5 pour le modem 4G HUAWEI sur l'OpiZ +ssh -N -D 8080 ubuntu@localhost -p 20223 +puis depuis le browser FireFox avec le plugin FoxyProxy Socket 5 sur localhost port 8080 +http://192.168.8.1 + + + +. diff --git a/DeepSleep/Pet_tracker_2/a.lua b/DeepSleep/Pet_tracker_2/a.lua new file mode 100644 index 0000000..2e94a02 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/a.lua @@ -0,0 +1,40 @@ +-- Scripts pour tester l'écoute des AP WIFI + +print("\n a.lua zf181211.0016 \n") + +--f= "wifi_ap_stop.lua" if file.exists(f) then dofile(f) end +--f= "wifi_cli_conf.lua" if file.exists(f) then dofile(f) end +--f= "wifi_cli_start.lua" if file.exists(f) then dofile(f) end +--f= "telnet_srv.lua" if file.exists(f) then dofile(f) end +--f= "web_ide2.lua" if file.exists(f) then dofile(f) end +--f= "dsleep.lua" if file.exists(f) then dofile(f) end + + +-- print AP list in new format +function a() + ztime() + function listap(t) + print("") + for k,v in pairs(t) do + print(k.." : "..v) + end + print("") + end + wifi.sta.getap(1, listap) +end + +--a() + +--[[ +-- Print AP list that is easier to read +function listap(t) -- (SSID : Authmode, RSSI, BSSID, Channel) + print("\n\t\t\tSSID\t\t\t\t\tBSSID\t\t\t RSSI\t\tAUTHMODE\t\tCHANNEL") + for bssid,v in pairs(t) do + local ssid, rssi, authmode, channel = string.match(v, "([^,]+),([^,]+),([^,]+),([^,]*)") + print(string.format("%32s",ssid).."\t"..bssid.."\t "..rssi.."\t\t"..authmode.."\t\t\t"..channel) + end +end +wifi.sta.getap(1, listap) + + +]] diff --git a/DeepSleep/Pet_tracker_2/a_test_power_wifi.lua b/DeepSleep/Pet_tracker_2/a_test_power_wifi.lua new file mode 100644 index 0000000..a1a9a81 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/a_test_power_wifi.lua @@ -0,0 +1,57 @@ +-- Scripts pour tester la consommation des différents mode du WIFI + +print("\n a_test_power_wifi zf181209.1718 \n") + +f= "wifi_ap_stop.lua" if file.exists(f) then dofile(f) end +f= "wifi_cli_conf.lua" if file.exists(f) then dofile(f) end +f= "wifi_cli_start.lua" if file.exists(f) then dofile(f) end +--f= "telnet_srv.lua" if file.exists(f) then dofile(f) end +--f= "web_ide2.lua" if file.exists(f) then dofile(f) end +--f= "dsleep.lua" if file.exists(f) then dofile(f) end + + +print("mode physique: ", wifi.getphymode()) +print("defaut mode: ", wifi.getdefaultmode()) +print("wifi stat status: ", wifi.sta.status()) + + +-- print AP list in old format (format not defined) +function listap(t) + for k,v in pairs(t) do + print(k.." : "..v) + end +end +wifi.sta.getap(listap) + +-- Print AP list that is easier to read +function listap(t) -- (SSID : Authmode, RSSI, BSSID, Channel) + print("\n"..string.format("%32s","SSID").."\tBSSID\t\t\t\t RSSI\t\tAUTHMODE\tCHANNEL") + for ssid,v in pairs(t) do + local authmode, rssi, bssid, channel = string.match(v, "([^,]+),([^,]+),([^,]+),([^,]+)") + print(string.format("%32s",ssid).."\t"..bssid.."\t "..rssi.."\t\t"..authmode.."\t\t\t"..channel) + end +end +wifi.sta.getap(listap) + + + + +-- print AP list in new format +function listap(t) + for k,v in pairs(t) do + print(k.." : "..v) + end +end +wifi.sta.getap(1, listap) + +-- Print AP list that is easier to read +function listap(t) -- (SSID : Authmode, RSSI, BSSID, Channel) + print("\n\t\t\tSSID\t\t\t\t\tBSSID\t\t\t RSSI\t\tAUTHMODE\t\tCHANNEL") + for bssid,v in pairs(t) do + local ssid, rssi, authmode, channel = string.match(v, "([^,]+),([^,]+),([^,]+),([^,]*)") + print(string.format("%32s",ssid).."\t"..bssid.."\t "..rssi.."\t\t"..authmode.."\t\t\t"..channel) + end +end +wifi.sta.getap(1, listap) + + diff --git a/DeepSleep/Pet_tracker_2/boot.lua b/DeepSleep/Pet_tracker_2/boot.lua new file mode 100644 index 0000000..f3f6149 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/boot.lua @@ -0,0 +1,65 @@ +-- Scripts à charger après le boot pour démarrer son projet + +function boot() + print("\n boot.lua zf200720.1828 \n") + print("On lance le boot...") + collectgarbage() print(node.heap()) + local f + -- f = "0_http_post.lua" if file.exists(f) then dofile(f) end + -- collectgarbage() print(node.heap()) + + -- local _, boot_reason = node.bootreason() + -- zarg_boot= "energy,memory=boot_"..yellow_id.." ram="..node.heap().."\n" + -- zarg_boot=zarg_boot.."energy,value=boot_reason_"..yellow_id.." val="..boot_reason + -- http_post(influxdb_url,zarg_boot) + + -- f = "0_zdyndns.lua" if file.exists(f) then dofile(f) end + -- print(node.heap()) collectgarbage() print(node.heap()) + + -- f="0_btn_flipflop.lua" if file.exists(f) then dofile(f) end + -- collectgarbage() print(node.heap()) + + -- f="0_ultra_son.lua" if file.exists(f) then dofile(f) end + -- collectgarbage() print(node.heap()) + + -- f="0_cron.lua" if file.exists(f) then dofile(f) end + -- collectgarbage() print(node.heap()) + + verbose = true + print("verbose: ",verbose,"\nle boot est lancé...") + gpio.write(zLED, gpio.HIGH) + f=nil boot=nil +end + +-- function debug_rec(zdebug) +-- local sec, usec = rtctime.get() local tm = rtctime.epoch2cal(sec + 2*3600) +-- local ztm = string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]) +-- file.open("00_debug.txt", "a+") file.writeline(ztm.."."..usec..", "..zdebug) file.close() +-- end +-- +-- function rec_boot() +-- sntp.sync(nil, nil, nil, 1) +-- tmr_rec_boot1=tmr.create() +-- tmr_rec_boot1:alarm(1*1000, tmr.ALARM_AUTO, function() +-- print("beep...") +-- if rtctime.get() > 0 then +-- tmr_rec_boot1:unregister() +-- print("Voilà on à l'heure, on peut enregistrer la raison du boot...") +-- local _, zboot_reason, zboot_detail = node.bootreason() +-- debug_rec("boot reason: "..zboot_reason) +-- tmr_rec_boot1=nil rec_boot=nil +-- collectgarbage() print(node.heap()) +-- end +-- end) +-- end + +verbose=true +if rec_boot~=nil then rec_boot() end +boot() +collectgarbage() print(node.heap()) + + +--[[ +verbose = true +verbose = false +]] diff --git a/DeepSleep/Pet_tracker_2/cat.lua b/DeepSleep/Pet_tracker_2/cat.lua new file mode 100644 index 0000000..856a52d --- /dev/null +++ b/DeepSleep/Pet_tracker_2/cat.lua @@ -0,0 +1,18 @@ +-- fonction cat() pour afficher le contenu d'un fichier dans la flash +print("\n cat.lua zf192026.0858 \n") + +function cat(zfile) + print("\n"..zfile.."\n-------------------------------") + + zfilei = file.open(zfile, "r") + i=1 + zline=file.readline() + repeat +-- print(i..": "..string.sub(zline,1,string.len(zline)-1)) + print(string.sub(zline,1,string.len(zline)-1)) + i=i+1 zline=file.readline() + until zline== nil + file.close(zfilei) + + print("-------------------------------") +end diff --git a/DeepSleep/Pet_tracker_2/dir.lua b/DeepSleep/Pet_tracker_2/dir.lua new file mode 100644 index 0000000..86a73d1 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/dir.lua @@ -0,0 +1,31 @@ +-- fonction dir() pour juste afficher les fichiers avec leur taille + +print("\n dir.lua zf191223.1455 \n") + +function dir() + local zdir={} + local pfile = file.list() + for k,v in pairs(pfile) do + zdir[#zdir+1] = k..string.rep(" ",24-string.len(k)).." : "..v + end + table.sort(zdir) for i=1, #zdir do print(zdir[i]) end + size_file=nil chksum_file=nil k=nil +end + +dir() +print("\nusage:") +print(" dir()") + +--[[ +dir() +dirc() +filec("dir2.lua") + +=node.heap() +clear_dir() +=node.heap() + +for k,v in pairs(_G) do print(k,v) end + +status, err = pcall(function () print(zhash("il était une fois trois petits cochons roses...")) end) if status==false then print("Error: ",err) end +]] diff --git a/DeepSleep/Pet_tracker_2/dir3.lua b/DeepSleep/Pet_tracker_2/dir3.lua new file mode 100644 index 0000000..1dd61ea --- /dev/null +++ b/DeepSleep/Pet_tracker_2/dir3.lua @@ -0,0 +1,52 @@ +-- fonction dir_vers() pour afficher toutes les versions de tous les fichiers *.lua sur le NodeMCU ! +-- fonction filec(fichier) pour afficher la version d'un seul fichiers sur le NodeMCU ! + +function dir3() + + print("\n 0_dir3.lua zf200611.1714 \n") + + function file_vers(name_file) + local z="" + if string.find(name_file,"%.lua") then + z=name_file..":" + -- print("fichier: "..name_file) + local f,i1,i2,j1,j2,k,t = f,i1,i2,j1,j2,k,t + f = file.open(name_file, "r") + while true do + local t = f:readline() if t == nil then break end + -- recherche de l'entête de version [print("\n ] + -- ATTENTION, il faut échapper la '(' avec un % et convertir le '\' en char(92) + k='print%("'..string.char(92)..'n ' + i1,j1 = string.find(t,k) + if i1 ~= nil then + k=string.char(92)..'n"%)' + i2,j2 = string.find(t,k,j1) + z=name_file..": "..string.sub(t,j1+1,i2-2) + break + end + end + f:close() + uart.write(0,".") + end + return z + end + + zdir={} list_files={} + local k,v = k,v local pfile = file.list() + for k,v in pairs(pfile) do + zdir[#zdir+1] = file_vers(k) + end + table.sort(zdir) for i=1, #zdir do print(zdir[i]) end + + dir_vers=nil file_vers=nil list_files=nil zdir=nil + dir3=nil + +end +dir3() + +--[[ + +status, err = pcall(function () print(zhash("il était une fois trois petits cochons roses...")) end) if status==false then print("Error: ",err) end + +]] + diff --git a/DeepSleep/Pet_tracker_2/dsleep.lua b/DeepSleep/Pet_tracker_2/dsleep.lua new file mode 100644 index 0000000..14453b3 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/dsleep.lua @@ -0,0 +1,64 @@ +-- Teste le deep sleep ! +-- s'endore pendant xx secondes après xx secondes + +-- ATTENTION: il faut connecter la pin 0 à la pin RESET avec une résistance de 1k ! + +print("\n dsleep.lua zf181211.0018 \n") + +f= "flash_led_xfois.lua" if file.exists(f) then dofile(f) end + +function dsleep_on() + print("timer dsleep on...") + ztmr_SLEEP = tmr.create() + tmr.alarm(ztmr_SLEEP, 10*1000, tmr.ALARM_SINGLE, function () + print("Je dors...") + tmr.delay(100*1000) +-- node.dsleep(4*1000*1000) + rtctime.dsleep(30*1000*1000) + end) +end + +function dsleep_off() + print("timer dsleep off...") + tmr.unregister(ztmr_SLEEP) +end + +function watch_wifi_on() + dsleep_on() + ztmr_watch_wifi_on=tmr.create() + tmr.alarm(ztmr_watch_wifi_on, 1000, tmr.ALARM_AUTO , function() + if wifi.sta.getip() == nil then +-- print("Unconnected... (on)") + else + tmr.stop(ztmr_watch_wifi_on) + print("Connected... (on)") +-- f= "wifi_info.lua" if file.exists(f) then dofile(f) end + watch_wifi_off() + end + end) +end + +function watch_wifi_off() + dsleep_off() + tmr.unregister(ztmr_watch_wifi_on) + ztmr_watch_wifi_off=tmr.create() + tmr.alarm(ztmr_watch_wifi_off, 1000, tmr.ALARM_AUTO , function() + if wifi.sta.getip() == nil then + tmr.stop(ztmr_watch_wifi_off) + print("Unconnected... (off)") + watch_wifi_on() + tmr.unregister(ztmr_watch_wifi_off) + else +-- print("Connected... (off)") + xfois =2 + blink_LED () + end + end) +end + +print("Coucou, je suis réveillé...") +print("Et il est: ") +ztime() + +watch_wifi_on() + diff --git a/DeepSleep/Pet_tracker_2/head.lua b/DeepSleep/Pet_tracker_2/head.lua new file mode 100644 index 0000000..0478b8c --- /dev/null +++ b/DeepSleep/Pet_tracker_2/head.lua @@ -0,0 +1,19 @@ +-- fonction cat() pour afficher les 10 premières lignes d'un fichier dans la flash +print("\n head.lua zf192028.1516 \n") + +function zhead(zfile) + print("\n"..zfile.."\n-------------------------------") + + zfilei = file.open(zfile, "r") + i=1 + zline=file.readline() + repeat +-- print(i..": "..string.sub(zline,1,string.len(zline)-1)) + print(string.sub(zline,1,string.len(zline)-1)) + i=i+1 zline=file.readline() + if i>10 then break end + until zline==nil + file.close(zfilei) + + print("-------------------------------") +end diff --git a/DeepSleep/Pet_tracker_2/initz.lua b/DeepSleep/Pet_tracker_2/initz.lua new file mode 100644 index 0000000..1e30574 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/initz.lua @@ -0,0 +1,86 @@ +--Script de bootstrap, test au moment du boot qui a été la cause de boot. +-- Si la cause est un power on ou une connexion depuis l'IDE, alors +-- le script repair.lua pendant xx secondes avant de continuer +--Source: https://nodemcu.readthedocs.io/en/master/en/modules/node/#nodebootreason + +print("\n init.lua zf200612.1632 \n") + +verbose = true + +function initz() + + function initz_end() + print("initz_end...") + f= "wifi_init.lua" if file.exists(f) then dofile(f) end + f=nil initz=nil second_chance=nil hvbouton=nil initz_end=nil + print(node.heap()) collectgarbage() print(node.heap()) + print("initz_end out...") + end + + function hvbouton() + gpio.trig(zswitch, "none") zswitch=nil + print("hvbouton...") + print(tmr.now()) + if tmr.now() > 5000000 then + file.putcontents("_setup_wifi_", "toto") + print("on a demandé le setup wifi !") + end + initalarme1:unregister() initalarme1=nil second_chance=nil + gpio.write(zLED, gpio.HIGH) zLED=nil + reset_reason="hvbouton" + initz_end() + end + + function second_chance() + print("seconde chance...") + zLED=4 -- NodeMCU + --zLED=7 -- SonOff + gpio.write(zLED, gpio.LOW) gpio.mode(zLED, gpio.OUTPUT) + initalarme1=tmr.create() + initalarme1:alarm(10*1000, tmr.ALARM_SINGLE, function() + gpio.write(zLED, gpio.HIGH) zLED=nil + gpio.trig(zswitch, "none") zswitch=nil + reset_reason="seconde_chance" + initz_end() + end) + zswitch=3 --switch flash ou SonOff + gpio.mode(zswitch, gpio.INT, gpio.PULLUP) + gpio.trig(zswitch, "both", hvbouton) + end + + _, reset_reason = node.bootreason() + print("reset_reason: ",reset_reason) + if reset_reason == 0 then + print("power on") + second_chance() + elseif reset_reason == 4 then + print("node.restart") + initz_end() + elseif reset_reason == 5 then + print("dsleep wake up") + initz_end() + elseif reset_reason == 6 then + print("external reset") + second_chance() + else + print("autre raison") + second_chance() + end +end +initz() + +--[[ +zLED=7 +gpio.mode(zLED, gpio.OUTPUT) +gpio.write(zLED, gpio.LOW) -- actif ! +gpio.write(zLED, gpio.HIGH) + +zBTN=3 +gpio.mode(zBTN, gpio.INPUT) +print(gpio.read(zBTN)) + +zRELAY=6 +gpio.mode(zRELAY, gpio.OUTPUT) +gpio.write(zRELAY, gpio.HIGH) -- actif ! +gpio.write(zRELAY, gpio.LOW) +]] diff --git a/DeepSleep/Pet_tracker_2/luatool.py b/DeepSleep/Pet_tracker_2/luatool.py new file mode 100755 index 0000000..29e183f --- /dev/null +++ b/DeepSleep/Pet_tracker_2/luatool.py @@ -0,0 +1,408 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +version = "0.6.8 zf191225.1428" + +print("luatool.py ver " + version) + +#Améliorations +# Pour voir les améliorations il faut 'chercher' les zzz + + +# +# ESP8266 luatool +# Author e-mail: 4ref0nt@gmail.com +# Site: http://esp8266.ru +# Contributions from: https://github.com/sej7278 +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +# Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import sys +import serial +from time import sleep +import socket +import argparse +from os.path import basename + + +tqdm_installed = True +try: + from tqdm import tqdm +except ImportError, e: + if e.message == 'No module named tqdm': + tqdm_installed = False + else: + raise + + + +class TransportError(Exception): + """Custom exception to represent errors with a transport + """ + def __init__(self, message): + self.message = message + + def __str__(self): + return self.message + +class AbstractTransport: + def __init__(self): + raise NotImplementedError('abstract transports cannot be instantiated.') + + def close(self): + raise NotImplementedError('Function not implemented') + + def read(self, length): + raise NotImplementedError('Function not implemented') + + def writeln(self, data, check=1): + raise NotImplementedError('Function not implemented') + + def writer(self, data): + ##zzz191124 enlève la fin de ligne afin de ne pas perturber la vérification finale + data = data.rstrip('\r\n') + self.writeln("file.writeline([==[" + data + "]==])\r") + + def performcheck(self, expected): + line = '' + char = '' + i = -1 + while char != chr(62): # '>' + char = self.read(1) + if char == '': + raise Exception('No proper answer from MCU') + if char == chr(13) or char == chr(10): # LF or CR + if line != '': + #zzz191124 line = line.strip() + # zzz191021 Affiche ce que l'on a reçu du NodeMCU + if args.verbose: + print("\n\nzread0957: {" + line + "\n}\n") + + if line+'\r' == expected and not args.bar: + sys.stdout.write(" -> ok") + elif line+'\r' != expected: + if line[:4] == "lua:": + sys.stdout.write("\r\n\r\nLua ERROR: %s" % line) + raise Exception('ERROR from Lua interpreter\r\n\r\n') + else: + expected = expected.split("\r")[0] + sys.stdout.write("\r\n\r\nERROR") + sys.stdout.write("\r\n send string : '%s'" % expected) + sys.stdout.write("\r\n expected echo : '%s'" % expected) + sys.stdout.write("\r\n but got answer : '%s'" % line) + sys.stdout.write("\r\n\r\n") + raise Exception('Error sending data to MCU\r\n\r\n') + line = '' + else: + line += char + if char == chr(62) and expected[i] == char: + char = '' + i += 1 + + +class SerialTransport(AbstractTransport): + def __init__(self, port, baud, delay): + self.port = port + self.baud = baud + self.serial = None + self.delay = delay + + try: + self.serial = serial.Serial(port, baud) + except serial.SerialException as e: + raise TransportError(e.strerror) + + self.serial.timeout = 3 + self.serial.interCharTimeout = 3 + + # zzz191021 juste après l'ouverture du port série, on attend le caractère '>' + line = '' + char = '' + i = -1 + while char != chr(62): # '>' + char = self.read(1) + if char == '': + raise Exception('No proper answer from MCU') + line += char + i += 1 + if args.verbose: + print("line: ." + line + ".") + + + def writeln(self, data, check=1): + # zzz191020 on fait une petite pause avant l'envoi de chaque ligne + sleep(self.delay) + if self.serial.inWaiting() > 0: + self.serial.flushInput() + if len(data) > 0 and not args.bar: + sys.stdout.write("\r\n->") + sys.stdout.write(data.split("\r")[0]) + ##zzz191124 attend encore un petit peu avant d'envoyer + sleep(self.delay) + self.serial.write(data) + sleep(self.delay) + # zzz191021 Affiche ce que l'on a envoyé au NodeMCU + if args.verbose: + print("\n\nzwrite0952: {" + data + "\n}\n") + if check > 0: + self.performcheck(data) + elif not args.bar: + sys.stdout.write(" -> send without check") + + def read(self, length): + return self.serial.read(length) + + def close(self): + self.serial.flush() + self.serial.close() + + +class TcpSocketTransport(AbstractTransport): + def __init__(self, host, port): + self.host = host + self.port = port + self.socket = None + + try: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + except socket.error as e: + raise TransportError(e.strerror) + + try: + self.socket.connect((host, port)) + except socket.error as e: + raise TransportError(e.strerror) + # read intro from telnet server (see telnet_srv.lua) + self.socket.recv(50) + + def writeln(self, data, check=1): + if len(data) > 0 and not args.bar: + sys.stdout.write("\r\n->") + sys.stdout.write(data.split("\r")[0]) + self.socket.sendall(data) + if check > 0: + self.performcheck(data) + elif not args.bar: + sys.stdout.write(" -> send without check") + + def read(self, length): + return self.socket.recv(length) + + def close(self): + self.socket.close() + + +def decidetransport(cliargs): + if cliargs.ip: + data = cliargs.ip.split(':') + host = data[0] + if len(data) == 2: + port = int(data[1]) + else: + port = 23 + return TcpSocketTransport(host, port) + else: + return SerialTransport(cliargs.port, cliargs.baud, cliargs.delay) + + +if __name__ == '__main__': + # parse arguments or use defaults + parser = argparse.ArgumentParser(description='ESP8266 Lua script uploader.') + parser.add_argument('--bar', action='store_true', help='Show a progress bar for uploads instead of printing each line') + parser.add_argument('--delay', default=0.025, help='Delay in seconds between each write, default 0.03 sec.', type=float) + parser.add_argument('--delete', default=None, help='Delete a lua/lc file from device.') + parser.add_argument('--ip', default=None, help='Connect via telnet server (--ip IP[:port])') + parser.add_argument('--zrestart', action='store_true', help='Restart the NodeMCU.') + parser.add_argument('-a', '--append', action='store_true', help='Append source file to destination file.') + parser.add_argument('-b', '--baud', default=115200, help='Baudrate, default 115200') + parser.add_argument('-c', '--compile', action='store_true', help='Compile lua to lc after upload') + parser.add_argument('-d', '--dofile', action='store_true', help='Run the Lua script after upload') + parser.add_argument('-e', '--echo', action='store_true', help='Echo output of MCU until script is terminated.') + parser.add_argument('-f', '--src', default='main.lua', help='Source file on computer, default main.lua') + parser.add_argument('-i', '--id', action='store_true', help='Query the modules chip id.') + parser.add_argument('-l', '--list', action='store_true', help='List files on device') + parser.add_argument('-p', '--port', default='/dev/ttyUSB0', help='Device name, default /dev/ttyUSB0') + parser.add_argument('-r', '--restart', action='store_true', help='Restart MCU after upload') + parser.add_argument('-t', '--dest', default=None, help='Destination file on MCU, default to source file name') + parser.add_argument('-v', '--verbose', action='store_true', help="Show progress messages.") + parser.add_argument('-w', '--wipe', action='store_true', help='Delete all lua/lc files on device.') + args = parser.parse_args() + + transport = decidetransport(args) + + if args.bar and not tqdm_installed: + sys.stdout.write("You must install the tqdm library to use the bar feature\n") + sys.stdout.write("To install, at the prompt type: \"pip install tqdm\"\n") + sys.exit(0) + + + if args.list: + # zzz191020 Amélioré la sortie du listing des fichiers + #transport.writeln("print('\\n-----');local l = file.list();for k,v in pairs(l) do print(k..', size:'..v)end;print('-----\\n')\r", 0) + # zzz191225 Amélioré encore la sortie du listing des fichiers (sort file) + transport.writeln("zdir={};pfile = file.list();for k,v in pairs(pfile) do zdir[#zdir+1] = k..string.rep(' ',24-string.len(k))..' : '..v end;table.sort(zdir);print('\\n-----');for i=1, #zdir do print(zdir[i]) end;print('-----\\n');zdir=nil;pfile=nil;k=nil;v=nil;i=nil\r", 0) + + while True: + char = transport.read(1) + if char == '' or char == chr(62): # '' or '>' + break + sys.stdout.write(char) + sys.exit(0) + + if args.id: + transport.writeln("=node.chipid()\r", 0) + id="" + while True: + char = transport.read(1) + if char == '' or char == chr(62): + break + if char.isdigit(): + id += char + print("\n"+id) + sys.exit(0) + + # zzz191020 Ajouté la fonction restart seule + if args.zrestart: + transport.writeln("node.restart()\r") + sys.exit(0) + + if args.wipe: + transport.writeln("local l = file.list();for k,v in pairs(l) do print(k)end\r", 0) + file_list = [] + fn = "" + while True: + char = transport.read(1) + if char == '' or char == chr(62): + break + if char not in ['\r', '\n']: + fn += char + else: + if fn: + file_list.append(fn.strip()) + fn = '' + for fn in file_list[1:]: # first line is the list command sent to device + if args.verbose: + sys.stderr.write("Delete file {} from device.\r\n".format(fn)) + transport.writeln("file.remove(\"" + fn + "\")\r") + sys.exit(0) + + if args.delete: + transport.writeln("file.remove(\"" + args.delete + "\")\r") + sys.exit(0) + + if args.dest is None: + args.dest = basename(args.src) + # zzz191020 Affiche le fichier à envoyer + print("File: " + args.src) + + # open source file for reading + try: + try: + f = open(args.src, "rt") + except: + import os + base_dir = os.path.dirname(os.path.realpath(__file__)) + f = open(os.path.join(base_dir, args.src), "rt") + os.chdir(base_dir) + except: + sys.stderr.write("Could not open input file \"%s\"\n" % args.src) + sys.exit(1) + + # Verify the selected file will not exceed the size of the serial buffer. + # The size of the buffer is 256. This script does not accept files with + # lines longer than 230 characters to have some room for command overhead. + num_lines = 0 + for ln in f: + if len(ln) > 230: + sys.stderr.write("File \"%s\" contains a line with more than 240 " + "characters. This exceeds the size of the serial buffer.\n" + % args.src) + f.close() + sys.exit(1) + num_lines += 1 + + # Go back to the beginning of the file after verifying it has the correct + # line length + f.seek(0) + + # set serial timeout + if args.verbose: + sys.stderr.write("Upload starting\r\n") + + # remove existing file on device + if args.append==False: + if args.verbose: + sys.stderr.write("Stage 1. Deleting old file from flash memory") + transport.writeln("file.open(\"" + args.dest + "\", \"w\")\r") + transport.writeln("file.close()\r") + transport.writeln("file.remove(\"" + args.dest + "\")\r") + else: + if args.verbose: + sys.stderr.write("[SKIPPED] Stage 1. Deleting old file from flash memory [SKIPPED]") + + + # read source file line by line and write to device + if args.verbose: + sys.stderr.write("\r\nStage 2. Creating file in flash memory and write first line") + if args.append: + transport.writeln("file.open(\"" + args.dest + "\", \"a+\")\r") + else: + transport.writeln("file.open(\"" + args.dest + "\", \"w+\")\r") + line = f.readline() + if args.verbose: + sys.stderr.write("\r\nStage 3. Start writing data to flash memory...") + if args.bar: + for i in tqdm(range(0, num_lines)): + #zzz191124 transport.writer(line.strip()) + transport.writer(line) + line = f.readline() + else: + while line != '': + #zzz191124 transport.writer(line.strip()) + transport.writer(line) + line = f.readline() + + # close both files + f.close() + if args.verbose: + sys.stderr.write("\r\nStage 4. Flush data and closing file") + transport.writeln("file.flush()\r") + transport.writeln("file.close()\r") + + # compile? + if args.compile: + if args.verbose: + sys.stderr.write("\r\nStage 5. Compiling") + transport.writeln("node.compile(\"" + args.dest + "\")\r") + transport.writeln("file.remove(\"" + args.dest + "\")\r") + + # restart or dofile + if args.restart: + transport.writeln("node.restart()\r") + + if args.dofile: # never exec if restart=1 + transport.writeln("dofile(\"" + args.dest + "\")\r", 0) + + if args.echo: + if args.verbose: + sys.stderr.write("\r\nEchoing MCU output, press Ctrl-C to exit") + while True: + sys.stdout.write(transport.read(1)) + + # close serial port + transport.close() + + # flush screen + sys.stdout.flush() + sys.stderr.flush() + if not args.bar: + sys.stderr.write("\r\n--->>> All done <<<---\r\n") diff --git a/DeepSleep/Pet_tracker_2/repair.lua b/DeepSleep/Pet_tracker_2/repair.lua new file mode 100644 index 0000000..691c123 --- /dev/null +++ b/DeepSleep/Pet_tracker_2/repair.lua @@ -0,0 +1,16 @@ +-- Scripts de seconde chance pour réparer une boucle dans le restart + +print("\n repair.lua zf181210.1456 \n") + +--f= "wifi_ap_start.lua" if file.exists(f) then dofile(f) end +--f= "telnet_srv.lua" if file.exists(f) then dofile(f) end + +--f= "az_init_led.lua" if file.exists(f) then dofile(f) end + + +--[[ +jobtimer1=tmr.create() +tmr.alarm(jobtimer1, 5*1000, tmr.ALARM_AUTO, function() + print("repair...") +end) +]] diff --git a/DeepSleep/Pet_tracker_2/restart.lua b/DeepSleep/Pet_tracker_2/restart.lua new file mode 100644 index 0000000..85f28eb --- /dev/null +++ b/DeepSleep/Pet_tracker_2/restart.lua @@ -0,0 +1,12 @@ +-- Scripts pour faire un soft reset + +print("\n restart.lua zf181209.1753 \n") + +restarttimer1=tmr.create() +tmr.alarm(restarttimer1, 2*1000, tmr.ALARM_SINGLE, function() + node.restart() +end) + +print("hello zuzu") + + diff --git a/DeepSleep/Pet_tracker_2/schemas/water-level.fzz b/DeepSleep/Pet_tracker_2/schemas/water-level.fzz new file mode 100644 index 0000000000000000000000000000000000000000..75ab8bc1d1c9830cac407145514dbb0059ac9e65 GIT binary patch literal 77042 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 v{Cj@ 30 then + print("pas de wifi :-(") + file.putcontents("_setup_wifi_", "toto") + print("on restart pour le setup wifi") + node.restart() + --tmr_wifi_init2:unregister() tmr_wifi_init2=nil + --wifi.setmode(wifi.SOFTAP,true) + --wifi_init_end() + end + else + wifi_init_end() + end + end) + end +end + +wifi_init() + +--[[ +file.putcontents("_setup_wifi_", "toto") +file.remove("eus_params.lua") +]] diff --git a/zflash-esp-m3.sh b/zflash-esp-m3.sh new file mode 100755 index 0000000..51f88f5 --- /dev/null +++ b/zflash-esp-m3.sh @@ -0,0 +1,44 @@ +#!/bin/bash +#Petit script pour flasher facilement les ESP-M3 avec un firmware + +#ATTENTION: c'est pour ma structure, il faudra donc l'adapter + +#zf200720.1838 + + +#test si l'argument est vide +if [ -z "$1" ] + then + echo -e "\nSyntax: + +Pour le dernier firmware à la mode: +./zflash-esp-m3.sh ../../Firmware/nodemcu-master-16-modules-2019-12-01-22-17-07-float.bin +./zflash-esp-m3.sh ../../Firmware/nodemcu-master-11-modules-2019-12-15-16-45-47-float.bin +./zflash-esp-m3.sh ../../Firmware/nodemcu-master-18-modules-2019-12-17-20-28-32-float.bin +./zflash-esp-m3.sh ../../Firmware/nodemcu-master-12-modules-2019-12-21-11-05-58-float.bin +./zflash-esp-m3.sh ../../Firmware/nodemcu-master-19-modules-2019-12-31-16-40-12-float.bin +./zflash-esp-m3.sh ../../Firmware/nodemcu-master-19-modules-2020-06-17-17-22-55-float.bin +./zflash-esp-m3.sh ../../Firmware/nodemcu-master-19-modules-2020-06-17-18-07-17-float.bin + +Pour l'ancien qui supporte encore le DS18B20: +./zflash-esp-m3.sh ../../Firmware/nodemcu-master-20-modules-2019-06-01-12-50-39-float.bin + + + +" + exit +fi + +echo ---------- start zflash.sh + +cd ./Tools/esptool-master + +python esptool.py flash_id +sleep 2 + +python esptool.py erase_flash +sleep 2 + +python esptool.py write_flash -fm dout 0x00000 $1 +sleep 2 +# screen /dev/cu.wchusbserial1410 115200 diff --git a/zflash-eps01.sh b/zflash-esp01.sh similarity index 63% rename from zflash-eps01.sh rename to zflash-esp01.sh index ecaba3b..9ea9aeb 100755 --- a/zflash-eps01.sh +++ b/zflash-esp01.sh @@ -3,7 +3,7 @@ #ATTENTION: c'est pour ma structure, il faudra donc l'adapter -#zf191231.1912 +#zf200720.1838 #test si l'argument est vide @@ -12,14 +12,14 @@ if [ -z "$1" ] echo -e "\nSyntax: Pour le dernier firmware à la mode: -./zflash-eps01.sh ../../Firmware/nodemcu-master-16-modules-2019-12-01-22-17-07-float.bin -./zflash-eps01.sh ../../Firmware/nodemcu-master-11-modules-2019-12-15-16-45-47-float.bin -./zflash-eps01.sh ../../Firmware/nodemcu-master-18-modules-2019-12-17-20-28-32-float.bin -./zflash-eps01.sh ../../Firmware/nodemcu-master-12-modules-2019-12-21-11-05-58-float.bin -./zflash-eps01.sh ../../Firmware/nodemcu-master-19-modules-2019-12-31-16-40-12-float.bin +./zflash-esp01.sh ../../Firmware/nodemcu-master-16-modules-2019-12-01-22-17-07-float.bin +./zflash-esp01.sh ../../Firmware/nodemcu-master-11-modules-2019-12-15-16-45-47-float.bin +./zflash-esp01.sh ../../Firmware/nodemcu-master-18-modules-2019-12-17-20-28-32-float.bin +./zflash-esp01.sh ../../Firmware/nodemcu-master-12-modules-2019-12-21-11-05-58-float.bin +./zflash-esp01.sh ../../Firmware/nodemcu-master-19-modules-2019-12-31-16-40-12-float.bin Pour l'ancien qui supporte encore le DS18B20: -./zflash-eps01.sh ../../Firmware/nodemcu-master-20-modules-2019-06-01-12-50-39-float.bin +./zflash-esp01.sh ../../Firmware/nodemcu-master-20-modules-2019-06-01-12-50-39-float.bin