Официальный сайт компании Arduino по адресу arduino.cc
ESP32 скачивает файлы с FTP сервера.
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Ну это что хотелось бы. Пока нифига не скачивает. :)
Всем бобра!
Столкнулся я с задачей (нет, чтоб диодами мигать, что ближе к моему уровню! ): есть аудио плеер на ESP32 - он по кругу проигрывает плейлист с SD карты. По Вифи он подключен к модему. Где-то в просторах и-нета есть FTP сервер с плейлистом. И если на FTP добавить файл, то ЕСПшка должна его скачать к себе на SD.
Полез в и-неты, нашел FTP клиент: https://github.com/ldab/ESP32_FTPClient , но этот вариант больше заточен на сохранение файлов на ftp, а скачивать может файлы только до 90кб, как утверждает автор. Ну, я , окрыленный недавними успехами по допиливанию другого прокта ( линк ) под свои нужды , думаю ща я допилю функцию скачивания, и все будет ок. Ага. Там подчинение и наследование, а один метод я вообще найти не могу, хотя пример компилится без проблем. Печаль.
Дай думаю спрошу , мож кто чего посоветует:
1) а может мне ftp клиент нафиг не нужен, и организовать скачивание файлов до 10М можно и иным способом? Сложность, на мой взгляд, в том, что перед скачиванием надо получить список файлов, сравнить его с тем, что на карте, и тогда уже скачивать. Т.е. какой-то скрипт должен этот список высылать по запросу ESP-шки...
2) Правильно ли я понимаю, что скорее всего лимит в 90кБ скорее всего связан с тем, что автор скачивал и хранил файл в оперативке? А по идее его надо скачивать в буфер, а буфер потом скидывать на карту?
Спасибо заранее!
1) может. можно и по http, да или хоть своим протоколом, но да, нужен будет скриптик со списком для сравнения. но это как бы не проблема ))
2) Правильно понимаете. Так и надо. Только буфер - он в той же самой оперативке. А sd-картридеры бывают разные. Вот автор и не стал заморачиваться, написал бы он сохранение файла для одного - так его бы начали долбать "а напиши для другого". А это не совсем правильно, ftp - отдельно, sd - отдельно. А соединяет их вместе пусть уже тот, кто делает проект с тем и другим.
Так что, всё везде правильно поняли.
1) может. можно и по http, да или хоть своим протоколом, но да, нужен будет скриптик со списком для сравнения. но это как бы не проблема ))
Эта строчка - содержимое index.php, который должен лежать рядом с папкой "mp3" с контентом. Если хотите поместить его внутрь папки "mp3", добавьте его к массиву, чтобы себя не выводил: "..", "index.php" ] и делайте scandir( "." )
. Вот автор и не стал заморачиваться, написал бы он сохранение файла для одного - так его бы начали долбать "а напиши для другого".
Для этого достаточно описать интерфейс, и всё. И реализовать первой имплементацией этого интерфейса - сохранение файла с FTP в оперативку. Чуть-чуть доки - и все, кто захотят, смогут реализовать другие имплементации этого интерфейса, и всё. Типа такого:
Для этого достаточно описать интерфейс, и всё. И реализовать первой имплементацией этого интерфейса - сохранение файла с FTP в оперативку.
У меня вот в чем загвоздка: я так понимаю, что строками
client.print(F("RETR "));
client.println(F(filename));
запрашивается у сервера файл на скачивание. Мне же нужна только его часть, которая поместится в буфер, т.к. потом его надо слить на карту, а это - время. Может кто-нить из знающих знает как запросить у сервера кусок файла нужного размера и с нужного адреса? И есть ли такая возможность вообще?
UPD. Видимо я приплыл: среди комманд FTP ничего похожего нет :(((
https://en.wikipedia.org/wiki/List_of_FTP_commands
Читайте про команду REST - Restart transfer from the specified point.
https://en.wikipedia.org/wiki/List_of_FTP_commands
REST - Restart transfer from the specified point.
Ещё ссылку на рфц 3659 дают, а там пример как файл начинают читать с позиции 802816
Кроме этого REST присутствовал в RFC 959 раздел 4.1.3. FTP SERVICE COMMANDS
Ну типа гляжу в книгу вижу фигу. :) Тогда по идее надо запускать с определенного адреса командой REST addr и сразу RETR filename, и когда буфер заполнился - слать комманду ABOR для остановки передачи. Правильно понимаю?
Assume that the transfer of a largish file has previously been interrupted after 802816 octets had been received - Это че за октеты?
UPD. ага - байты и октеты - это синонимы.
Хм . А у REST в аргументах только адрес, и боюсь это адрес не начала файла, иначе бы имя файла тоже было бы в аргументах.
Октет - восемь двоичных единиц информации умещаются в один байт, который может принимать десятичном исчислении значения от 0 до 255
Протрахался я с ftp- клиентом пару дней. Реализацию функции скачивания файла с FTP (изначальный код в посте №5) я переписал так, чтобы полученные байты сразу писались на карту памяти, без буферизации.
И - не работает. :( файлы короче, чем положено. Думал пропуск данных, задрал тактовую частоту SPI со штатных 4МГц до 15МГц - все равно короче. Я потом на карточке по файлу случайно тыцнул - а он играет как положено, просто обрезаный! Тут я и офигел! То есть такой метод стриминга байтов на карточку рабочий, просто обрывается связь. dclient.available() становится нихера не эвэйлебл, и - привет семье! //dclient.timedRead(); - это я проверял как будет без пересылания данных на карту работать. Разницы не заметил. Так что автор не зря жаловался, что ему больше 90кб не удалось скачивать. У меня скачивалось до 3х Мб, но соединение рвалось, и причина тут не в карте памяти или оперативке, а в его библиотеке. Вот такая печаль.
При передачи очередной порции данных, количество байтов ограничен размером окна, принимаемая сторона не отправляет подтверждение, отправитель повторяет отправку и вновь не получает подтверждения и разрывает соединение.
Простое решение - это использовать другой протокол, например http.
В запросе нужно добавить заголовок "Range" и указать какой диапазон байтов требуется получить.
Например:
Добавляю заголовок "Range: bytes=0-0" указываю что хочу получить байты с нулевого по нулевой т.е. хочу один байт, на самом деле хочу узнать лишь длину файла.
В ответе получаю 206-й код (Partial Content) и заголовок "Content-Range: bytes 0-0/42091" и контент в один байт, теперь я знаю что файл длиной 42091 байт.
Затем в цикле выполняю запросы указывая диапазоны байтов, которые я гарантированно получу и смогу их обрабатывать без ограничения по времени.
Запрашиваю очередной кусок байтов длиной 4096, в запросе "Range: bytes=8192-12287", в ответе получаю "Content-Range: bytes 8192-12287/42091" и "Content-Length: 4096" и собственно контент.
Простое решение - это использовать другой протокол, например http.
Да, я это понимаю, но тут проблема иная - я в http полный ноль, как, впрочем, и во всех других сетевых протоколах. Найти ресурс где лаконично представлено все, что мне надо, я не смог. Учить с нуля весь http - было бы здорово, но времени нет. Или с духом не соберусь. Вот и допиливаю что найду. Не всегда оно работает, ессно. Вот товарищ negavoid мне даже содержимое php файла дал, а как его применить - не знаю :( В сети сплошные серврера расписаны, а по клиентам очень мало. Поэтому "Добавляю заголовок "Range: bytes=0-0" для меня пока сродни "берешь лямбда-функцию, и юзаешь ее". :)
Вообще я уже запустил свой девайс по http применив вот это решение. Но там автор не удосужился (вернее ему не надо было) опрос контента организовать. Так я поженил ужа с носорогом: для получения списка файлов подымаю ftp клиент, получаю контент, убиваю фтп клиента, для скачивания подымаю http клиент, скачиваю, убиваю его. :) Через жопу? Однозначно да! :) Но это пока все, на что я способен. У меня ответ на вопрос "вам шашечки , или ехать?" однозначный - ехать! Но как правило уже в дороге я очередной раз понимаю, что с шашечками ехать лучше! :)
Вы на одном ядре выполняете получение байтов с потока и на том-же ядре выполняете запись на карту.
Не пробовали сравнить время получения файла с ftp не выполняя пока запись, и во втором опыте время сохранения такого же количества байт в файл на карту.
Если время сохранения на карту сравнимо со временем получения файла, то получите как раз такой неудовлетворительный результат.
Например: загрузка файла 100 секунд и сохранение тоже 100 секунд, значит на одном ядре получим 200 секунд и сервер передавая очередную порцию данных не дожидается подтверждения.
Можно попробовать выполнять загрузку на нулевом ядре (на нем живут драйверы wifi в том числе), а сохранение на первом и обмениваться через очередь (один таск добавляет в очередь, а другой получает данные из очереди).
Если очередь не будет успевать опустошаться вы сразу это заметите, получив ошибку при добавлении в очередь очередной порции данных.
Не пробовали сравнить время получения файла с ftp не выполняя пока запись, и во втором опыте время сохранения такого же количества байт в файл на карту.
Я отключал запись на карту: строку file.write((uint8_t)dclient.timedRead());я менял на dclient.timedRead();. В таком виде функция работала быстрее чем у автора - просто считывался байт без заполнения буфера и связанных с этим проверок. Результат был одинаковый : скачивание прерывалось после скачанных пары Мб. Поэтому я пришел к выводу, что проблема кроется не в том, что проц не успевает записать данные на карту. С этим как раз все ок, раз иногда больше 3х метров записать удавалось, и кусок файла получался не битый. Тут свет на ситуацию пролил бы лог с сервера, но у нас в поддержке сидят манагеры, а не разработчики, увы.
Про разделение потоков я тоже думал, но так как и первое ядро успевает считывать и скидывать на карту, то смысла перекидывать задачу на нулевое ядро не увидел.
В любом случает спасибо за участие! Может ткнете в ссылку на стоящий ресурс, где достойно описан порядок составления запросов и обработки ответов html?