Страница 1 из 1
[Железо] Удалённая прошивка AVR через ESP8266, находящихся за NAT-ом
Добавлено: Пн фев 19, 2018 9:12 pm
Ko/|xo3HUk
Доброго всем времени суток!
Появилась у меня такая необходимость - нужно управлять в гараже потолочным вентилятором (щас это называется по модному - дестратификатор
), думаю повесить два датчика температуры у пола и у потолка и по разнице температур включать плавно вентилятор, используя ШИМ. Также хочу получать статистику о температуре и частоте включений вентилятора. В гараже есть только мобильный интернет, достучаться извне нереально. А на первых порах, думаю, придётся часто корректировать программу микроконтроллера. Таскать каждый раз ноут в гараж нет никакого желания, разбирать всю схему и снимать МК, чтобы принести домой и заменить прошивку - лениво, да и не набегаешься, если что пойдёт не так.
Поэтому решил сделать связку Arduino Uno + ESP8266-E. За схему получения прошивки с сервера - взял схему обновления прошивки ESP8266 через OTA (Over-the-Air), только прошивать буду не саму ЕСП-шку, а скачивать прошивку для МК на ЕСП-шку, и после этого прошивать МК самой ЕСП-шкой.
Я не профессиональный программист, всего лишь любитель, к тому же с ЛУА-скриптами столкнулся впервые, так что можно считать этот проект пилотным дебютом
. Заодно решил познакомиться с Гитхабом, поэтому выложил туда все исходники.
Схема работы, примерно, такая:
- 01.png (29.42 КБ) 10628 просмотров
1,2,3,4 - ESP8266 периодически опрашивает сервер на наличие новой прошивки. Смартфон находится за NAT-ом мобильной сети, не имеет белого IP-адреса, поэтому достучаться до него извне невозможно – ОпСоС (МТС в моём случае) блокирует весь входящий траффик
5,6,7,8 – При наличии новой прошивки на сервере – он передаёт её на ESP8266. Если нет новой прошивки – возвращает код и текст ошибки
9 – после получения новой прошивки – ESP8266 прошивает Ардуину через SPI-интерфейс.
Использованная документация, программы и исходники:
Используемое оборудование:
• Ардуино UNO
• ESP8266-E
• LevelShifter для согласования уровней - копеечная платка с Алиэкспресса, типа
такой.
1. Схема подключения
2. Создание прошивки ESP8266
Идём на сайт
https://nodemcu-build.com/ и создаём там прошивку со следующими модулями:
- 03.png (134.18 КБ) 10628 просмотров
(можно взять мою:
)
Получим на e-mail два файла:
nodemcu-master-12-modules-2018-01-10-14-00-50-integer.bin и
nodemcu-master-12-modules-2018-01-10-14-00-50-float.bin. Заливаем
integer через софтину
NODE MCU PyFlasher (не забываем указать «yes, wipe all data»!):
- 04.png (57.51 КБ) 10628 просмотров
После заливки прошивки запускаем ESPlorer (можно скачать здесь:
https://esp8266.ru/esplorer/#download )
Указываем СОМ-порт и скорость и жмём кнопку «Open»
- 05.png (34.27 КБ) 10628 просмотров
После подключения открываем поочерёдно все файлы из репозитория:
https://github.com/SergeyKopylov/ESP826 ... trough_NAT
(желательно init.lua грузить последним, т.к. он запускает все остальные файлы)
и загружаем в ESP-шку:
- 06.png (103.44 КБ) 10628 просмотров
3. Создаём в МажорДоМо сценарий "esp_ota_update" и копируем туда весь код из файла esp_ota_update.php из репозитория
4. Получаем HEX-файл для прошивки:
Для получения HEX-файла прошивки в Arduino IDE:
- 07.png (23.68 КБ) 10628 просмотров
После этого открываем папку с файлом этого проекта и видим, что появились два HEX-файла:
- 08.png (6.74 КБ) 10628 просмотров
Переименовываем hex-файл с бутлоадером (на тот случай, если вдруг захочется впоследствии прошивать ардуину снова из Arduino IDE) в файл с именем «file_name.hex», где file_name – это имя, которое мы назначили данной ESP-шке в скрипте MajorDoMo “esp_ota_update” (в моём случае это будет файл: Garage_Fan.hex):
- 09.png (99.56 КБ) 10628 просмотров
И копируем переименованный файл в папку на сервере, которая прописана в нашем скрипте esp_ota_update:
- 10.png (59.1 КБ) 10628 просмотров
В моём случае это папка «../www/bin/»
Теперь остаётся подождать когда в ESP-шке по таймеру сработает проверка наличия новой прошивки, скачает эту прошивку и зальёт её в МК.
Авторизация
Если на сервере настроена авторизация, то нужно указать логин:пароль в хидере. Например, для логина:пароля «admin:password» - заходим на сайт, к примеру,
http://sitespy.ru/base64
И пишем там admin:password
- 11.png (18.79 КБ) 10628 просмотров
В результате в хидере нужно прописать следующую строку:
Код: Выделить всё
"Authorization: Basic YWRtaW46cGFzc3dvcmQ= \r\n"..
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 8:08 am
Ko/|xo3HUk
Добавлю немного описаний Луа-скриптов.
init.lua
Код: Выделить всё
print ( "Waiting ...")
wifi.setmode(wifi.STATION)
local cfg={}
cfg.ssid="WiFi"
cfg.pwd="wifi_password"
wifi.sta.config(cfg)
cfg = nil
collectgarbage()
tmr.delay(2000)
-- Каждые 5 минут запускаем функцию проверки наличия новой прошивки
local mytimer = tmr.create()
mytimer:register (300000, tmr.ALARM_AUTO, function (t)
if (wifi.sta.getip() ~= nil) then
print ("Запуск проверки наличия новой прошивки")
dofile ( "check_firmware.lua")
else
print("Нет доступной сети WiFi")
end;
end)
mytimer:start (0)
Здесь всё стандартно - этот скрипт запускается сразу же при старте есп-шки. Поэтому здесь организовано подключение к заданной сети "WiFi" с паролем "wifi_password". Имя сети и пароль нужно поменять на свои настройки.
Далее вызывается таймер, который автоматически перезапускается, и считает 300000 мсек = 5 минут. Как только отсчитал заданное время - происходит запуск скрипта "check_firmware.lua" для проверки наличия новой прошивки. Это время я выбирал для проверки работы скриптов. Можно, в принципе, увеличить время, чтобы проверка происходила раз в полчаса-час.
Позже здесь добавлю ещё вызов функции для передачи на сервер температуры и частоты включений вентилятора, пока же реализована только прошивка МК.
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 8:49 am
Ko/|xo3HUk
check_firmware.lua
Код: Выделить всё
--**************************************************************
-- sending a GET request and get the return value
--Feed the system watchdog.
--In general, if you ever need to use this function, you are doing it wrong :) - https://nodemcu.readthedocs.io/en/dev/en/modules/tmr/#tmrwdclr
tmr.wdclr()
local sketch = "sketch_download" --Имя файла на ESP8266, куда будет ложиться прошивка (скетч)
local extension = "no_ext"
if (file.exists(sketch)) then
md5 = crypto.toHex(crypto.fhash("md5",sketch))
else
--файла нету, прописываем 'левый' хэш:
md5 = "NoSketchFilePresent"
end
local remaining, used, total=file.fsinfo()
local majorVer, minorVer, devVer, chipid, flashid, flashsize, flashmode, flashspeed = node.info()
http.get("http://192.168.1.2/objects/?script=esp_ota_update&sketch_req=NewSketchChecking",
"x-esp8266-sta-mac: "..wifi.sta.getmac().."\r\n"..
"x-esp8266-sta-ip: "..wifi.sta.getip().."\r\n"..
"x-esp8266-ap-mac: "..wifi.ap.getmac().."\r\n"..
"x-esp8266-free-space: "..node.heap().."\r\n"..
"x-esp8266-chip-size: "..node.flashsize().."\r\n"..
"x-esp8266-chip-id: "..chipid.."\r\n"..
"x-esp8266-sdk-version: "..majorVer.."."..minorVer.."."..devVer.."\r\n"..
"x-esp8266-mode: "..wifi.getmode().."\r\n"..
"x-esp8266-fs-total: "..total.."\r\n"..
"x-esp8266-fs-used: "..used.."\r\n"..
"x-esp8266-fs-remaining: "..remaining.."\r\n"..
"x-esp8266-sketch-md5: "..md5.."\r\n"..
"x-esp8266-extension: no_ext\r\n"..
"x-file-name: noname\r\n"..
"Connection: keep-alive\r\n"..
"Accept-Charset: utf-8\r\n"..
"Accept-Encoding: \r\n"..
"User-Agent: ESP8266-http-Update\r\n"..
"Host: homeserver\r\n"..
"Authorization: Basic YWRtaW46cGFzc3dvcmQ=\r\n"..
"Accept: */*\r\n\r\n",
function(code, data, headers)
print("code = "..code.."\tdata = "..data)
print("x-file-name = ", headers["x-file-name"])
print("x-esp8266-extension = ", headers["x-esp8266-extension"])
print("content-length = ", headers["content-length"])
if (headers["x-esp8266-extension"] == nil) then
print("Нет новой прошивки или отсутствует связь с сервером")
extension = "no_sketch"
elseif (headers["x-esp8266-extension"] == "hex") then
print("Используемый тип файла прошивки = "..headers["x-esp8266-extension"])
extension = "hex"
-- sketch = "sketch.hex"
elseif (headers["x-esp8266-extension"] == "bin") then
print("Используемый тип файла прошивки = "..headers["x-esp8266-extension"])
extension = "bin"
-- sketch = "sketch.bin"
else
print("Используемый тип файла прошивки - неизвестен = "..headers["x-esp8266-extension"])
extension = "unknown"
end
if (code < 0) then
print("HTTP request failed")
else
if (data == "OK") then
--**************************************************************
-- Download a file
tmr.wdclr()
httpDL = require("httpDL")
collectgarbage()
httpDL.download("192.168.1.1", "80", "objects/?script=esp_ota_update", sketch, md5, function(ret_val)
-- Finished downloading
remaining = nil
flashspeed = nil
flashsize = nil
majorVer = nil
minorVer = nil
flashmode = nil
devVer = nil
md5 = nil
used = nil
flashid = nil
chipid = nil
package.loaded["httpDL"]=nil
httpDL = nil
conn = nil
collectgarbage()
if (ret_val == nil) then
print("ret_val == nil.. Что-то пошло не так...")
elseif (ret_val == "ok") then
if (extension == "bin") then
print("extension = bin")
-- Начинаем прошивку
require("Program_Flash")
--Program_Flash ("sketch.bin")
Program_Flash ("sketch_download")
package.loaded["Program_Flash"]=nil
Program_Flash = nil
elseif (extension == "hex") then
print("extension = hex")
-- Копируем файл прошивки в sketch.hex
local source = file.open(sketch, "r")
local destin = file.open("sketch.hex", "w")
local size = source:seek("end")
source:seek("set",0)
destin:seek("set",0)
destin:write(source:read(size))
destin:close()
source:close()
destin = nil
source = nil
-- Начинаем прошивку
require("Program_Flash")
Program_Flash ("sketch.hex")
package.loaded["Program_Flash"]=nil
Program_Flash = nil
elseif (extension == "no_sketch") then
print("extension = no_sketch")
elseif (extension == "unknown") then
print("extension = unknown")
else
print("Странное значение extension... Что-то пошло не так...")
end
elseif (ret_val == "failed") then
print("ret_val == failed. Downloading was failed! (MD5 does not match)")
else
print("Странное значение ret_val... Что-то пошло не так...")
end
end)
collectgarbage()
--==============================================================
end
end
remaining = nil
flashspeed = nil
flashsize = nil
majorVer = nil
minorVer = nil
flashmode = nil
devVer = nil
md5 = nil
used = nil
flashid = nil
chipid = nil
collectgarbage()
end)
Данный скрипт вычисляет md5 хэш файла "sketch_download", который создаётся при скачивании файла прошивки с сервера. Если хэш уже скачанного файла совпадает с хэшем файла прошивки на сервере - то ничего не выполняем, т.к. файлы совпадают.
Здесь реализован предварительный запрос на сервер, который выполняется функцией "http.get("
http://192.168.1.2/objects/?script=esp_ ... chChecking"". Сервер, видя такой запрос, вызывает сценарий "esp_ota_update" и проверяет что за запрос пришёл - запрос на проверку новой прошивки (sketch_req=NewSketchChecking), либо запрос на скачивание файла прошивки (sketch_req не указывается в запросе), либо запрос на проверку корректности скачанного файла (sketch_req=AfterChecking). За обработку этого запроса отвечает данная часть php-кода сценария "esp_ota_update":
Код: Выделить всё
if ($params['sketch_req']=="AfterChecking") {
DebMes("AfterChecking()"); // После этого в XRay во вкладке debug можно смотреть результат.
AfterChecking($localBinary);
DebMes("==================================================================="); // После этого в XRay во вкладке debug можно смотреть результат.
}
else {
if($_SERVER["HTTP_X_ESP8266_SKETCH_MD5"] != md5_file($localBinary)){
if ($params['sketch_req']=="NewSketchChecking") {
DebMes("Answer()"); // После этого в XRay во вкладке debug можно смотреть результат.
Answer($localBinary, $file_extension);
foreach (getallheaders() as $name => $value)
{
DebMes("Answer. getallheaders -> $name: $value\n"); // После этого в XRay во вкладке debug можно смотреть результат.
}
}
else {
DebMes("sendFile(localBinary)"); // После этого в XRay во вкладке debug можно смотреть результат.
DebMes("file_extension = ".$file_extension); // После этого в XRay во вкладке debug можно смотреть результат.
sendFile($localBinary, $file_extension);
foreach (getallheaders() as $name => $value)
{
DebMes("SendFile. getallheaders -> $name: $value\n"); // После этого в XRay во вкладке debug можно смотреть результат.
}
}
DebMes("==================================================================="); // После этого в XRay во вкладке debug можно смотреть результат.
} else {
//DebMes($_SERVER["SERVER_PROTOCOL"]." 304 Not Modified"); // После этого в XRay во вкладке debug можно смотреть результат.
DebMes($_SERVER["HTTP_X_ESP8266_SKETCH_MD5"]." <- SERVER[HTTP_X_ESP8266_SKETCH_MD5]"); // После этого в XRay во вкладке debug можно смотреть результат.
DebMes(md5_file($localBinary). " <- md5_file(localBinary)"); // После этого в XRay во вкладке debug можно смотреть результат.
DebMes("304 Not Modified"); // После этого в XRay во вкладке debug можно смотреть результат.
DebMes("==================================================================="); // После этого в XRay во вкладке debug можно смотреть результат.
header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified',true, 304);
echo "You have actual sketch, no need to download\n";
foreach (getallheaders() as $name => $value)
{
DebMes("Sketch Not Modified. getallheaders -> $name: $value\n"); // После этого в XRay во вкладке debug можно смотреть результат.
}
}
}
Если нет новой прошивки - то сервер разрывает соединение с кодом 304 и текстом "You have actual sketch, no need to download"
Если есть новая прошивка, то вызывается скрипт httpDL.lua для скачивания файла с сервера
Код: Выделить всё
httpDL.download("192.168.1.1", "80", "objects/?script=esp_ota_update", sketch, md5, function(ret_val)
После скачивания файла проверяется расширение файла. Для прошивки МК можно использовать либо BIN-файл, либо HEX-файл. Изначально я сделал прошивку только BIN-файлами, но это оказалось неудобно, нужно было конвертировать прошивку вручную отдельной софтиной, поэтому реализовал на есп-шке преобразование hex-файла в bin-файл.
Если скачался hex-файл, то копируем файл "sketch_download" в файл "sketch.hex"
После того, как скачалась новая прошивка - её хэш проверяется снова с хэшем на сервере, и если он совпадает - то запускаем скрипт "Program_Flash.lua" для прошивки МК.
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 12:29 pm
smart_g
К чему фраза, что есть только мобильный интернет. А решение - подключение по WIFI. Если добивает в гараж WIFI, то есть решение WIFI-to-Com. Подключить таким образом ардуино и все.
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 12:30 pm
smart_g
А с мобильным интернетом VPN наше все, стучись не хочу.
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 1:21 pm
Ko/|xo3HUk
smart_g писал(а):Если добивает в гараж WIFI...
Если б у меня был гараж в пределах досягаемости WiFi - я б так не заморачивался
До гаража больше километра (прямой видимости нет), wifi не достаёт.
smart_g писал(а):А с мобильным интернетом VPN наше все, стучись не хочу.
Ну ка, расскажите мне, как организовать данное решение? Допустим, я поднял OpenVPN между сервером и мобилой (уже делал это), как дальше мне получить данные с ардуины на домашний сервер?
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 1:51 pm
smart_g
У меня на даче стоит роутер с OpemWRT. WAN по 3G-GPRS. На нем поднят VPN клиент. Вся сеть за роутером доступна мне. Хоть по SSH, хоть шары устройств за роутером. Вот и все. Описаний как это сделать в инете масса.
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 2:01 pm
smart_g
А по поводу ардуины, смотря как она подключена в сетку. VPN это сетевое решение. Можно вообще без ардуины обойтись, все решить на роутере. У меня в роутере стоит I2C-to-1wire переходник, поднят OWFS и MQTT клиент и данные по cron гонятся на сервер.
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 2:03 pm
Ko/|xo3HUk
Понятно. Нужен роутер, у меня его на данный момент нет, так что пока остановлюсь на своём варианте. Он мои задачи выполняет.
Re: Удалённая прошивка AVR через ESP8266, находящихся за NAT
Добавлено: Вт фев 20, 2018 2:06 pm
Ko/|xo3HUk
smart_g писал(а):А по поводу ардуины, смотря как она подключена в сетку.
Вот почему я и выбрал есп-шку - он выполняет роль как модуль связи ардуины с сервером, так и выполняет функцию прошивки этой ардуины. 2 в 1, так сказать.