[Программирование, Совершенный код, C++, Отладка, C] Как подключить содержимое любых файлов для использования в коде C / C++
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет, Хабровчане!Это моя первая статья и у меня есть чем поделиться. Возможно мой велосипед не нов и этим способом пользуется каждый, но когда-то давно искал решения, с ходу найти не получилось.О чем речь?Задача состояла в подключении файлов: HTML, JS, CSS; без специальной подготовки. Так же неудобно подключать бинарные файлы (например картинки) конвертируя их в HEX. Так как не хотелось конвертировать в HEX или разделять на строки, искал способ подключения файла в адресное пространство программы.Как обычно это выглядитПример, c разделением строк:
const char text[] =
"<html>" "\r\n"
"<body>Text</body>" "\r\n"
"</html>";
Пример, с HEX (больше подходит для бинарных данных):
const char text[] =
{
0x3C, 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0x0A, 0x3C,
0x62, 0x6F, 0x64, 0x79, 0x3E, 0x54, 0x65, 0x78,
0x74, 0x3C, 0x2F, 0x62, 0x6F, 0x64, 0x79, 0x3E,
0x0A, 0x3C, 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x3E,
0
};
Видел даже такое:
#define TEXT "<html>\r\n<body>Text</body>\r\n</html>"
const char text[] = TEXT;
Все #define располагались в отдельном .h файле и подготавливались скриптом на Python. С аннотацией, что некоторые символы должны бить экранированы \ вручную в исходном файле. Честно немного волосы дыбом встали от такого мазохизма.А хотелось, чтобы файлы можно было спокойно редактировать, просматривать и при компиляции всё само подключалось и было доступно, например так:
extern const char text[];
Оказалось всё просто, несколько строчек в Assembler.Подключаем файл в Arduino IDEДобавляем новую вкладку или создаём файл в папке проекта с названием text.S, там же размещаем файл text.htm.Содержимое файла text.htm:
<html>
<body>Text</body>
</html>
Содержимое файла text.S:
.global text
.section .rodata.myfiles
text:
.incbin "text.htm"
.byte 0
Не забываем нулевой символ \0 в конце, он здесь в строке сам не добавится.Сам скетч:
extern const char text[] PROGMEM;
void setup()
{
Serial.begin(115200);
Serial.println(text);
}
void loop() { }
Компилируем, загружаем и смотрим вывод:
Отлично, когда-то бы я от радости прыгал до потолка, от того что всё получилось.Код работает в AVR8, но например в ESP8266 получим аппаратный сбой. Всё потому, что чтение из Flash доступно по 32 бита и по адресам кратным 32 бит. Чтобы было всё хорошо, каждому файлу требуется делать отступ для кратности, код будет выглядеть так:
.global text
.section .rodata.myfiles
.align 4
text:
.incbin "text.htm"
.byte 0
Загрузить можно в секцию кода: .irom.text, если не хватает места в .rodata.Для STM32 так же рекомендуется выравнивать по 32 бита, но не обязательно.А как записать размер данных во время компиляции? Например, для бинарных данных не получится остановится по нулевому символу. Так же просто:
.global text, text_size
.section .rodata.myfiles
text:
.incbin "text.htm"
text_end:
.byte 0
text_size:
.word (text_end - text)
Объявление:
extern const char text[] PROGMEM;
extern const uint16_t text_size PROGMEM;
Осталось написать макрос, для удобства подключения файлов:
.macro addFile name file
.global \name, \name\()_size
// .align 4
\name:
.incbin "\file"
\name\()_end:
.byte 0
// .align 4
\name\()_size:
.word (\name\()_end - \name)
.endm
.section .rodata.myfiles
addFile text1 1.txt
addFile text2 2.txt
addFile text3 3.txt
И макрос для объявления:
#define ADD_FILE(name) \
extern const char name[] PROGMEM; \
extern const uint16_t name##_size PROGMEM;
ADD_FILE(text1);
ADD_FILE(text2);
ADD_FILE(text3);
void setup()
{
Serial.begin(115200);
Serial.println(text1);
Serial.println(text1_size);
Serial.println(text2);
Serial.println(text2_size);
Serial.println(text3);
Serial.println(text3_size);
}
void loop() { }
Вывод:
Таким образом можно подключить любой файл и представить его любым типом, структурой или массивом.Подключаем любой файл и не только, в GNU toolchainПринцип тот же самый, ни чем не отличается для Arduino. В принципе в Arduino используется тот же toolchain от Atmel.Только здесь у нас в руках Makefile и мы можем до компиляции и сборки запустить какой-нибудь скрипт.Для примера возьму код из готового моего проекта на STM32, где автоматически при компиляции увеличивается версия сборки. Так же включаются в проект WEB-интерфейс для последующего использования в LWIP / HTTPD.Скрипт version.sh:
#!/bin/bash
# Version generator
# running script from pre-build
MAJOR=1
MINOR=0
cd "$(dirname $0)" &>/dev/null
FILE_VERSION="version.txt"
FILE_ASM="version.S"
BUILD=$(head -n1 "$FILE_VERSION" 2>/dev/null)
if [ -z "$BUILD" ]; then
BUILD=0
else
BUILD=$(expr $BUILD + 1)
fi
echo -n "$BUILD" >"$FILE_VERSION"
cat <<EOF >"$FILE_ASM"
/**
* no editing, automatically generated from version.sh
*/
.section .rodata
.global __version_major
.global __version_minor
.global __version_build
__version_major: .word $MAJOR
__version_minor: .word $MINOR
__version_build: .word $BUILD
.end
EOF
cd - &>/dev/null
exit 0
Создаётся файл version.S в который из version.txt загружается номер версии предыдущей сборки.В Makefile добавляется цель pre-build:
#######################################
# pre-build script
#######################################
pre-build:
bash version.sh
В цель all надо дописать pre-build:
all: pre-build $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
Объявление и макросы для printf у меня в macro.h:
extern const uint16_t __version_major;
extern const uint16_t __version_minor;
extern const uint16_t __version_build;
#define FMT_VER "%u.%u.%u"
#define FMT_VER_VAL __version_major, __version_minor, __version_build
В HTTPD из LWIP немного был удивлён когда увидел, что содержимое файлов надо хранить вместе с заголовками HTTP. Чтобы не менять архитектуру, загрузку делал как это организовано в примере fsdata.c. Использовал fsdata_custom.c, для этого установлен флаг HTTPD_USE_CUSTOM_FSDATA.Код в fsdata_custom.c:
#include "lwip/apps/fs.h"
#include "lwip/def.h"
#include "fsdata.h"
#include "macro.h"
extern const struct fsdata_file __fs_root;
#define FS_ROOT &__fs_root
Сборка файлов fsdata_make.S:
.macro addData name file mime
\name\():
.string "/\file\()"
\name\()_data:
.incbin "mime/\mime\().txt"
.incbin "\file\()"
\name\()_end:
.endm
.macro addFile name next
\name\()_file:
.word \next\()
.word \name\()
.word \name\()_data
.word \name\()_end - \name\()_data
.word 1
.endm
.section .rodata.fsdata
.global __fs_root
/* Load files */
addData __index_htm index.htm html
addData __styles_css styles.css css
addData __lib_js lib.js js
addData __ui_js ui.js js
addData __404_htm 404.htm 404
addData __favicon_ico img/favicon.ico ico
addData __logo_png img/logo.png png
/* FSDATA Table */
addFile __logo_png 0
addFile __favicon_ico __logo_png_file
addFile __404_htm __favicon_ico_file
addFile __ui_js __404_htm_file
addFile __lib_js __ui_js_file
addFile __styles_css __lib_js_file
__fs_root:
addFile __index_htm __styles_css_file
.end
В начале каждого файла загружается заголовок, пару примеров из папки mime.Файл html.txt:
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Connection: close
Файл 404.txt:
HTTP/1.1 404 Not found
Content-Type: text/plain; charset=UTF-8
Connection: close
Нужно обратить внимание на пустую строку, чего требует спецификация HTTP для обозначения конца заголовка. Каждая строка должна заканчиваться символом CRLF (\r\n).P.S. Код проекта из ветхого сундука, так что в реализации мог забыть чего ни будь уточнить.В завершенииДолго искал, что изложить в статье полезного. Надеюсь мой опыт пригодится новичку и гуру.Спасибо за внимание, удачных разработок!
===========
Источник:
habr.com
===========
Похожие новости:
- [JavaScript, VueJS] Nuxt.js app от UI-кита до деплоя. Часть 2: Темная тема
- [Разработка под Android] Получаем результат правильно (Часть 1). Activity Result API
- [Разработка веб-сайтов, CSS, HTML, Графический дизайн] Три способа создания клякс с помощью CSS и SVG (перевод)
- [Open source, *nix] FOSS News №59 – дайджест материалов о свободном и открытом ПО за 1-7 марта 2021 года
- [JavaScript, Программирование, HTML, TensorFlow] Отслеживание лиц в реальном времени в браузере с использованием TensorFlow.js. Часть 6 (перевод)
- [Open source, Разработка под Android, Kotlin] Reaction — обработка результатов методов в Kotlin
- [Венчурные инвестиции, Развитие стартапа, Финансы в IT, IT-компании] Новости IT и венчурных инвестиций: новые налоги в России и Франции, кредитка от VK
- [Карьера в IT-индустрии, Лайфхаки для гиков] Исповедь IT-рекрутера на фрилансе
- [Разработка под iOS, Разработка под Android, Управление продажами, Презентации] Онбординг. Зачем нужен и как использовать
- [Разработка под Arduino, DIY или Сделай сам] Мой умный дом на ESP8266, часть 3
Теги для поиска: #_programmirovanie (Программирование), #_sovershennyj_kod (Совершенный код), #_c++, #_otladka (Отладка), #_c, #_stm32, #_arduino, #_lwip, #_httpd, #_esp8266, #_assembler, #_c/c++, #_avr, #_toolchain, #_gnu, #_programmirovanie (
Программирование
), #_sovershennyj_kod (
Совершенный код
), #_c++, #_otladka (
Отладка
), #_c
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 08:38
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет, Хабровчане!Это моя первая статья и у меня есть чем поделиться. Возможно мой велосипед не нов и этим способом пользуется каждый, но когда-то давно искал решения, с ходу найти не получилось.О чем речь?Задача состояла в подключении файлов: HTML, JS, CSS; без специальной подготовки. Так же неудобно подключать бинарные файлы (например картинки) конвертируя их в HEX. Так как не хотелось конвертировать в HEX или разделять на строки, искал способ подключения файла в адресное пространство программы.Как обычно это выглядитПример, c разделением строк: const char text[] =
"<html>" "\r\n" "<body>Text</body>" "\r\n" "</html>"; const char text[] =
{ 0x3C, 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0x0A, 0x3C, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x54, 0x65, 0x78, 0x74, 0x3C, 0x2F, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0A, 0x3C, 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0 }; #define TEXT "<html>\r\n<body>Text</body>\r\n</html>"
const char text[] = TEXT; extern const char text[];
<html>
<body>Text</body> </html> .global text
.section .rodata.myfiles text: .incbin "text.htm" .byte 0 extern const char text[] PROGMEM;
void setup() { Serial.begin(115200); Serial.println(text); } void loop() { } Отлично, когда-то бы я от радости прыгал до потолка, от того что всё получилось.Код работает в AVR8, но например в ESP8266 получим аппаратный сбой. Всё потому, что чтение из Flash доступно по 32 бита и по адресам кратным 32 бит. Чтобы было всё хорошо, каждому файлу требуется делать отступ для кратности, код будет выглядеть так: .global text
.section .rodata.myfiles .align 4 text: .incbin "text.htm" .byte 0 .global text, text_size
.section .rodata.myfiles text: .incbin "text.htm" text_end: .byte 0 text_size: .word (text_end - text) extern const char text[] PROGMEM;
extern const uint16_t text_size PROGMEM; .macro addFile name file
.global \name, \name\()_size // .align 4 \name: .incbin "\file" \name\()_end: .byte 0 // .align 4 \name\()_size: .word (\name\()_end - \name) .endm .section .rodata.myfiles addFile text1 1.txt addFile text2 2.txt addFile text3 3.txt #define ADD_FILE(name) \
extern const char name[] PROGMEM; \ extern const uint16_t name##_size PROGMEM; ADD_FILE(text1); ADD_FILE(text2); ADD_FILE(text3); void setup() { Serial.begin(115200); Serial.println(text1); Serial.println(text1_size); Serial.println(text2); Serial.println(text2_size); Serial.println(text3); Serial.println(text3_size); } void loop() { } Таким образом можно подключить любой файл и представить его любым типом, структурой или массивом.Подключаем любой файл и не только, в GNU toolchainПринцип тот же самый, ни чем не отличается для Arduino. В принципе в Arduino используется тот же toolchain от Atmel.Только здесь у нас в руках Makefile и мы можем до компиляции и сборки запустить какой-нибудь скрипт.Для примера возьму код из готового моего проекта на STM32, где автоматически при компиляции увеличивается версия сборки. Так же включаются в проект WEB-интерфейс для последующего использования в LWIP / HTTPD.Скрипт version.sh: #!/bin/bash
# Version generator # running script from pre-build MAJOR=1 MINOR=0 cd "$(dirname $0)" &>/dev/null FILE_VERSION="version.txt" FILE_ASM="version.S" BUILD=$(head -n1 "$FILE_VERSION" 2>/dev/null) if [ -z "$BUILD" ]; then BUILD=0 else BUILD=$(expr $BUILD + 1) fi echo -n "$BUILD" >"$FILE_VERSION" cat <<EOF >"$FILE_ASM" /** * no editing, automatically generated from version.sh */ .section .rodata .global __version_major .global __version_minor .global __version_build __version_major: .word $MAJOR __version_minor: .word $MINOR __version_build: .word $BUILD .end EOF cd - &>/dev/null exit 0 #######################################
# pre-build script ####################################### pre-build: bash version.sh all: pre-build $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
extern const uint16_t __version_major;
extern const uint16_t __version_minor; extern const uint16_t __version_build; #define FMT_VER "%u.%u.%u" #define FMT_VER_VAL __version_major, __version_minor, __version_build #include "lwip/apps/fs.h"
#include "lwip/def.h" #include "fsdata.h" #include "macro.h" extern const struct fsdata_file __fs_root; #define FS_ROOT &__fs_root .macro addData name file mime
\name\(): .string "/\file\()" \name\()_data: .incbin "mime/\mime\().txt" .incbin "\file\()" \name\()_end: .endm .macro addFile name next \name\()_file: .word \next\() .word \name\() .word \name\()_data .word \name\()_end - \name\()_data .word 1 .endm .section .rodata.fsdata .global __fs_root /* Load files */ addData __index_htm index.htm html addData __styles_css styles.css css addData __lib_js lib.js js addData __ui_js ui.js js addData __404_htm 404.htm 404 addData __favicon_ico img/favicon.ico ico addData __logo_png img/logo.png png /* FSDATA Table */ addFile __logo_png 0 addFile __favicon_ico __logo_png_file addFile __404_htm __favicon_ico_file addFile __ui_js __404_htm_file addFile __lib_js __ui_js_file addFile __styles_css __lib_js_file __fs_root: addFile __index_htm __styles_css_file .end HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8 Connection: close HTTP/1.1 404 Not found
Content-Type: text/plain; charset=UTF-8 Connection: close =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_sovershennyj_kod ( Совершенный код ), #_c++, #_otladka ( Отладка ), #_c |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 08:38
Часовой пояс: UTC + 5