• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Разработка ‘библиотеки’ малого webсервера на esp8266.

aloika

Active member
А можно задать теоретический вопрос? Если оффтопик, можно куда-нибудь перенести.
Допустим, объявляю я тип-структуру:

Код:
typedef struct t_COND_SWING {
        uint8 enable;
        uint8 speed;
        uint8 sensivity;
        uint16 curr_time;
        uint16 setting_time;
}COND_SWING;
Теперь мне надо создать собственно экземпляр этого типа. Я могу сделать так - начала объявить указатель на эту структуру

Код:
COND_SWING *pcsw = NULL;
а потом создать ее в памяти и заполнить поля:

Код:
pcsw = (COND_SWING *) os_zalloc(sizeof(COND_SWING));
 pcsw->curr_time=15;
pcsw->enable=0;
pcsw->sensivity=2;
pcsw->setting_time=15;
pcsw->speed=4;
А почему я не могу (или могу?) создать экземпляр вообще по-простому:

Код:
COND_SWING pcsw;
Теперь вопросы:
1. Могу я так сделать?
2. Если да, то в чем разница с первым вариантом?
3. Как тогда получать доступ к полям структуры?

Заранее спасибо за ответы.
 

pvvx

Активный участник сообщества
Заранее спасибо за ответы.
1. да
2. с os_zalloc() после использования можно удалить и память освободится
3. pcsw->поле. Если надо обращаться из других процедур, после выделения памяти os_zalloc(), то для указателя на pcsw, надо выделить статическую переменную и сохранить pcsw:
COND_SWING *pcsw;
void xxx(void) {
pcsw = (COND_SWING *) os_zalloc(sizeof(COND_SWING));
if(pcsw == NULL) { os_printf_plus("Нема памяти!"); return; }
pcsw->curr_time=15;
return;
}
4. Уроки по СИ платные :) Откройте тему "Обучение CИ и C++" :)
 

kisoft

New member
aloika, последний вариант более правильный с т.з. С++, поскольку в этом случае вызывается конструктор, если он есть. Доступ к членам структуры pcsw.enable = 1; Если голый С, то оба варианта корректны, но я бы выбрал последний.
 

pvvx

Активный участник сообщества
Вопрос:
Как переключить скорость UART без вывода кракозяб?
Особенно актуально для переключение на UART1 в/после процедуры register_chipv6_phy() - она меняет PLL (CLK CPU с 26 на 40MHz, точнее на то, что записано в esp_init_data_default.bin).

Так-же невозможно пока убрать мусор за "RTC MEM CHECK FAIL!!!\n":
Код:
...
tail 0
chksum 0x32
csum 0x32
RTC MEM CHECK FAIL!!!
<0>ájXŠ    ©    …
PvSDK ver: 0.0.0(b0)
Будет убран только после замены register_chipv6_phy() :)

ЗЫ: В текущей "свалке" вроде ошибка с установкой скорости SPI для Flash. Счас это в стадии переделки...
 

aloika

Active member
Я прямо так сильно стесняюсь и извиняюсь :) , но все же спрошу: pvvx, а websocket'ов не будет?
 

pvvx

Активный участник сообщества
а websocket'ов не будет?
Пока нет разумного применения. Сторонние исходники приведены. Там есть пример разбора уже протокола websocket. В "свалке" даны все "вхождения" и включаются в define, далее надо всего сохранить ключ и проще переключить callback-и tcp_srv_conn на обработчик уже протокола websocket-a.
Реализации websocket на Ардуино не полны. Там ключи практически не используются и много чего скипнуто, что делает их несовместимыми и глючными.
Придумайте что простого с применением websocket'ов, совместимого со 'свалкой' - тогда сразу будут. :)
Пока занят более главным - пытаюсь сделать стабильность сиcтемы путем исправления/замещения кусков либ Espressif на свои. Пока это идет в тестовом варианте. Проверить всё одному сложно и долго... а 'портировать' чужие исходники websocket'ов - дело пары часов с отладкой :) Но пока не до них. Есть масса нерешенных проблем. Не всё наглядно и не все их знают, т.к. пока не описаны и не столкнулись - их как-бы нет :)
Например проблема долгого пинга после "простоя" по TCP у SDK Espressif. Это самая простая для объяснения проблемка, а их ещё много...
 
Последнее редактирование:

aloika

Active member
Сторонние исходники приведены. Там есть пример разбора уже протокола websocket. В "свалке" даны все "вхождения" и включаются в define, далее надо всего сохранить ключ и проще переключить callback-и tcp_srv_conn на обработчик уже протокола websocket-a.
Пока есть надежда, что Вы все же сделаете это сами и дадите готовый проверенный результат - не хочется в это самостоятельно влезать, т.к. понимание всего этого у меня более чем скромное и надо во всем разбираться с самого начала. А это мало того, что займет очень много времени, так еще и результат не гарантированный.
Кажется, что Вам-то там что - пару раз метлой махнуть - и готово, а мне тут еще метлу надо выращивать сколько времени... :)

Придумайте что простого с применением websocket'ов, совместимого со 'свалкой' - тогда сразу будут. :)
Ну какой-нибудь частную задачу, демонстрирующую возможности websocket'ов, придумать можно. Например, я когда-то делал что-то типа осциллографа - через вебсокет пересылался массив из 100 чисел (полученных с АЦП) 10 раз в секунду, а на сайте это все отрисовывалось в компоненте teectart: http://www.steema.com/teechart/html5. Т.е. получался "осциллограф" с частотой обновления картинки 10 Гц. Конечно, там были всякие вопросы, связанные с синхронизацией и пр., но вау-эффект был - веб-осциллограф, обалдеть :)

Так что я могу только пообещать такую же демонстрационную штуку сделать, если вебсокеты будут.
 

pvvx

Активный участник сообщества
Например, я когда-то делал что-то типа осциллографа - через вебсокет пересылался массив из 100 чисел (полученных с АЦП) 10 раз в секунду, а на сайте это все отрисовывалось в компоненте teectart: http://www.steema.com/teechart/html5.
Оно и счас работает через XML. Выходит только разновидность...
Тут новая фича выпла - Выводы UART-ов переключаются как угодно, в пределах их сигналов RX/TX/RTS/CTS/DSR/DTR, т.е. даже между UART1 и UART0 :confused: и главное что прямо на ходу, битами в 0x3ff00028...
А Espressif дал только swap RX0/TX0 в одну сторону - BIT2...
 
Последнее редактирование:

aloika

Active member
Оно и счас работает через XML. Выходит только разновидность...
Сейчас оно работает как графопостроитель, т.е. строит отдельные непонятно как выбранные значения.
А тут можно будет, например, смотреть сигнал 50 Гц. Был бы второй АЦП, можно было бы и угол померить между двумя сигналами и все такое прочее.

Вообще основное преимущество websocket'ов я вижу в следующем - можно что-нить передавать по инициативе сервера, т.е. не производить постоянно опрос с клиента. Допустим, некое состояние (к примеру, пина) может меняться довольно редко, раз в 5 минут, к примеру. Но как только оно изменилось, мы сразу хотим на веб-страничке нарисовать включенную лампочку. Через поллинг мы будем 5 минут опрашивать с частотой, скажем, 5-10 Гц, мегабайты перебросим по wi-fi, но результативной и нужной для нас будет только одна из этих многих передач. А в каждой будет XML, заголовки всякие... А с websocket'ами все гораздо рациональнее.

Тут новая фича выпла - Выводы UART-ов переключаются как угодно, в пределах их сигналов RX/TX/RTS/CTS/DSR/DTR, т.е. даже между UART1 и UART0 :confused: и главное что прямо на ходу, битами в 0x3ff00028...
А Espressif дал только swap RX0/TX0 в одну сторону - BIT2...
Это, конечно, здорово, но какое может быть этому практическое применение?
 

pvvx

Активный участник сообщества
Через поллинг мы будем 5 минут опрашивать с частотой, скажем, 5-10 Гц, мегабайты перебросим по wi-fi, но результативной и нужной для нас будет только одна из этих многих передач. А в каждой будет XML, заголовки всякие... А с websocket'ами все гораздо рациональнее.
Для NAT это безразлично - канал надо держать открытым и в websocket тоже пинги есть...
Это, конечно, здорово, но какое может быть этому практическое применение?
2 полноценных UART.
 

lvm1976

New member
pvvx, че то не так
Код:
struct sclntTcp {
    struct tcp_pcb* clnttcp_pcb;
    u8_t * clnttcp_server_addresses;
    u16_t clnttcp_server_port;
    u8_t * clnttcp_url;
};/** The TCP pcb used by the TSP client */

struct sclntTcp *clntTcp;

/******************************************************************************
 * FunctionName : Send_cmd_server
 * посылка GET команды на сервер,  тупо без обратки
 * пример http://192.168.4.1/?gpio1_out=3
 * или http://lvm1976.dyndns.org:1326/?gpio1_out=3
 ******************************************************************************/
void ICACHE_FLASH_ATTR Send_cmd_server(uint8 *cmd_server) {

    if(os_strncmp(&cmd_server, "http://", 7) == 0) cmd_server += 7;
    if (cmpcpystr(clntTcp->clnttcp_server_dns, cmd_server, '\0', ':', 16) == 0) {
        if (cmpcpystr(clntTcp->clnttcp_server_dns, cmd_server, '\0', '/', 32) == 0) {
            LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("Failed to addresses for clntTcp client!\n"));
        }
    }
    if (cmd_server[0] >= '0' && cmd_server[0] <= '9')
        if (cmpcpystr(NULL, cmd_server, '\0', '.', 4) != 0) {
            clntTcp->clnttcp_server_addresses = strtoip(clntTcp->clnttcp_server_dns);
        }
    uint8 buf[5] = "80";
    cmpcpystr(buf, cmd_server, ':', '/', 32);
    clntTcp->clnttcp_server_port = atoi(buf);
    cmd_server = (uint8 *)cmpcpystr(NULL, cmd_server, '\0', '/', 32);
    os_memset(clntTcp->clnttcp_url, 0, sizeof(clntTcp->clnttcp_url));
    os_sprintf(clntTcp->clnttcp_url, cmd_server);
    if (clntTcp == NULL) {
        clntTcp = (struct sclntTcp *)os_zalloc(sizeof(struct sclntTcp));
        if (clntTcp == NULL) {
            LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("Failed to allocate mem for clntTcp client!\n"));
        }
    }
    if(clntTcp->clnttcp_pcb == NULL) {
        clntTcp->clnttcp_pcb = tcp_new();
        if (clntTcp->clnttcp_pcb != NULL) {
            if (clntTcp->clnttcp_server_addresses) dns_gethostbyname(&clntTcp->clnttcp_server_dns,clntTcp->clnttcp_server_addresses, NULL, NULL);
            if (tcp_bind(clntTcp->clnttcp_pcb, (ip_addr_t *)clntTcp->clnttcp_server_addresses, clntTcp->clnttcp_server_port)) {
                tcp_write(clntTcp->clnttcp_pcb, clntTcp->clnttcp_url, sizeof(clntTcp->clnttcp_url), 0);
                tcp_output(clntTcp->clnttcp_pcb);
                tcp_close(clntTcp->clnttcp_pcb);
            }
        }
        else
            LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("Failed to allocate tcp pcb for clntTcp client!\n"));
        os_free(clntTcp);
        clntTcp = NULL;
    }
}
 
Последнее редактирование:

Serjkruch

New member
pvvx, че то не так
if (clntTcp->clnttcp_pcb != NULL) {
if (clntTcp->clnttcp_server_addresses) dns_gethostbyname(&clntTcp->clnttcp_server_dns,clntTcp->clnttcp_server_addresses, NULL, NULL);
if (tcp_bind(clntTcp->clnttcp_pcb, clntTcp->clnttcp_server_addresses, clntTcp->clnttcp_server_port) != NULL) {
tcp_write(clntTcp->clnttcp_pcb, clntTcp->clnttcp_url, sizeof(clntTcp->clnttcp_url), 0);
tcp_output(clntTcp->clnttcp_pcb);
tcp_close(clntTcp->clnttcp_pcb);
}
}
Простите, но после выполнения вызова dns_gethostbyname, она должна вернуть ERR_INPROGRESS. И в вызовы я указал функцию (callback) при получении IP адреса, она вызывается.

Вот как это делается в файле sntp.c
Код:
    err = dns_gethostbyname(buf_sntp_server_addresses,
        &sntp_server_address, sntp_dns_found, NULL);
    if (err == ERR_INPROGRESS) {
        /* DNS request sent, wait for sntp_dns_found being called */
        LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
        return;
    }
 

lvm1976

New member
Простите, но после выполнения вызова dns_gethostbyname, она должна вернуть ERR_INPROGRESS. И в вызовы я указал функцию (callback) при получении IP адреса, она вызывается.
а это разве обязательно? я ее тупо для поиска ip адреса использовал
 

pvvx

Активный участник сообщества
Простите, но после выполнения вызова dns_gethostbyname, она должна вернуть ERR_INPROGRESS. И в вызовы я указал функцию (callback) при получении IP адреса, она вызывается.
Но не проверяется, занят ли dns поиском другого ip. т.е вдруг у него уже назначена другая callback из другой задачи, а "стека задач" у него нет :)
 

Serjkruch

New member
Но не проверяется, занят ли dns поиском другого ip. т.е вдруг у него уже назначена другая callback из другой задачи, а "стека задач" у него нет :)
Вы имели ввиду что где-то в коде кто-то уже использует этот метод и поэтому мы должны вызывать её постоянно, до тех пор пока не будет ответа ERR_OK?
Что то не могу понять. Тут написано:
Код:
 * Returns immediately with one of err_t return codes:
 * - ERR_OK if hostname is a valid IP address string or the host
 *   name is already in the local names table.
 * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
 *   for resolution if no errors are present.
 * - ERR_ARG: dns client not initialized or invalid hostname
Значит ли это что функция dns_gethostbyname вернёт статус немедленно и без колбека её нужно вызвать ещё раз, пока не вернётся ERR_OK?
 

pvvx

Активный участник сообщества
Что то не могу понять.
...
Значит ли это что функция dns_gethostbyname вернёт статус немедленно и без колбека её нужно вызвать ещё раз, пока не вернётся ERR_OK?
Не разбирался я с имеющимся dns пока. Ничего сказать не могу и высказал предположение, которое надо проверить, чтобы избежать ошибок в дальнейшем.
Dns то китайcкий, от Espressif... в последних "свалках" используется их покоцанная либа liblwip.a
Начальный в workspace\Web_Base4\app\lwip\core\dns.c
Там всё ок.
dns_gethostbyname(found) -> dns_enqueue(found) ->...

lwipopts.h: /** DNS maximum number of entries to maintain locally. */
[HASHTAG]#define[/HASHTAG] DNS_TABLE_SIZE 4
Т.е. предел 4 одновременных запроса.
----
Причина использования покоцанной либы liblwip.a - она дает меньше используемой RAM, чем трансляция исходников Lwip. Но есть различия в размере занимаемой IRAM. Сколько занимает в flash - это безразлично - там места много. С этим и надо доразобраться и почистить исходники Lwip.
Найденные различия указал в Web_Base4\app\lwip\iram.txt
Ещё IRAM выжимается в Web_Base4\app\lwip\core\timers.c - там китайцы забыли у static поставить ICACHE_FLASH_ATTR, т.к. ставили атрибуты в include\lwip\timers.h и к static они не пошли...
Но главное - надо что-то выкинуть в исходниках, т.к. лишнее и жрет память. При этом трогать lwipopts.h нельзя - будет несовместим с китай-SDK
И выход тут один - дореверсить SDK минимум до WiFi процедур. Это и есть главная задача "свалки" - всё остальное = второстепенно.
Такие вещи как websocket - это вообще "десятый" вопрос :)

Вот один из примеров первостепенных вопросов:
В текущей "свалке" вроде ошибка с установкой скорости SPI для Flash. В ROM-BIOS есть полная замена void sflash_something(uint32 mode) и надо заменить его на процедуру в ROM-BIOS:
Код:
#include "c_types.h"
#include "esp8266.h"
/*
ROM:40004644 spi_flash_attach:
ROM:40004644                 addi            a1, a1, 0xF0
ROM:40004647                 s32i.n          a0, a1, 0
ROM:40004649                 call0           SelectSpiFunction
ROM:4000464C                 movi.n          a2, 5
ROM:4000464E                 movi.n          a3, 4
ROM:40004650                 call0           SPIFlashCnfig
ROM:40004653                 movi.n          a2, 5
ROM:40004655                 call0           SPIReadModeCnfig
ROM:40004658                 l32i.n          a0, a1, 0
ROM:4000465A                 addi            a1, a1, 0x10
ROM:4000465D                 ret.n
*/
void SPIFlashCnfig(uint32_t parm1, uint32_t parm2)
{
    uint32 a6 = 0;
    uint32 a2;
    HWREG(SPI0_BASE,0x1C) |= 4;
    if(parm1 == 0) a6 = 0x1000000; // SPI_QIO_MODE
    else if(parm1 == 1) a6 = 0x100000; // SPI_QOUT_MODE
    else if(parm1 == 2) a6 = 0x800000; // SPI_DIO_MODE
    else if(parm1 == 3) a6 = 0x4000; // SPI_DOUT_MODE
    else if(parm1 == 4) a6 = 0x2000; // SPI_FASTRD_MODE
    if(parm2 < 2) {
        a2 = 0x100;
        HWREG(SPI0_BASE,0x08) |= 0x1000; // ???
        HWREG(IOMUX_BASE,0) |= a2;
    }
    else {
        a2 = (((parm2 - 1) << 8) + parm2 + (((parm2 >> 1) - 1) << 4) - 1);
        HWREG(SPI0_BASE,0x08) &= 0xFFFFEFFF;
        HWREG(IOMUX_BASE,0) &= 0xEFF;
    }
    a2 |= a6;
    a2 |= 0x288000; // SPI_RESANDRES | SPI_SHARE_BUS | SPI_WP_REG
    HWREG(SPI0_BASE,0x08) |= a2;
    HWREG(SPI0_BASE,0) = 0x100000;
    while(HWREG(SPI0_BASE,0) != 0);
}
скорректировав параметры. Таким образом уменьшается задействованная IRAM...
Ну и подобные... т.к. есть желание перейти к оверлеям в IRAM, а для этого её надо высвободить по макс.
Например код первого "загрузчика" с инициализацией либов вообще переноситься в область оверлеев, а по исполнению освобождается, т.к. он больше не нужен.
И эти задачи требуют переписывания всех include, чтобы избавиться от навязанного Espressif бардака с ними. Описания ROM-BIOS отдельно от либов SDK и т.д.
В итоге задача "мало памяти" переходи в задачу "мало времени" :)
А на и так редко задаваемые вопросы никто и никогда не отвечает.
 
Последнее редактирование:

lvm1976

New member
Но не проверяется, занят ли dns поиском другого ip. т.е вдруг у него уже назначена другая callback из другой задачи, а "стека задач" у него нет :)
я не программист просто копирайт , там вырвал туда вставил завелось хорошо, нет ищу причину, на мой взгляд было достаточно чтобы просто послать команду без обработки ответа, но что то не срослось
 

pvvx

Активный участник сообщества
Зачем в esp_init_data_default.bin байты, вроде с 50 по 57 в phy_gpio_cfg(), задают конфигурацию GPIO в SDK? (в основном GPIO1, GPIO2, GPIO3 и GPIO16)
Кто даст таблицу?
 

AnonymUser

New member
Спасибо pvvx за такую работу)
и собственно вопрос до обывателей форума, сразу предупреждаю что в C ноль,но методом копи-паст и научного тыка пальцем в небо что то выходит...
как воспользоваться нетбиос(вроде правильно), в файле web_int_callbacks.c нашел строки

Код:
if(syscfg.cfg.b.netbios_ena) tcp_strcpy(netbios_name);
    else {
        if(ip == 0) tcp_strcpy_fd("esp8266.ru");
        else tcp_puts(IPSTR, IP2STR(&ip));
    };
изменил условие if(ip == 0) на if(ip == 0x01010101) // да, да, я задал другой ип на есп...
но увы на ип отвечает, а по доменному имени нет
 
Последнее редактирование:

pvvx

Активный участник сообщества
как воспользоваться нетбиос
NETBIOS. Имя берется от WiFi AP и для AP добавляется первая буква "a", для ST - "s". Пример (по первому старту): 'http://sesp8266' и 'http://aesp8266'. Но NETBIOS в основном для Windows... и для свистков возможно надо указать "Включить NetBios через TCP/IP":
Посмотреть вложение 373
if(ip == 0) tcp_strcpy_fd("esp8266.ru");
Это когда не определить текущий ip, т.к. нет соединения к AP в режиме ST и т.д. :) И эта esp8266.ru выдается в HTML, чтобы использовать как ссылку и c NetBios никак не связано.
Т.к. задача у get_new_url() - угадать какой будет адрес модуля при смене конфигурации WiFi и эта ветка отработать практически не может, но если всё пропало и ... :)
 
Последнее редактирование:
Сверху Снизу