Управление офисным освещением по Wi-Fi. Часть 1: Wi-Fi модуль Atmel WINC1500
Введение
В данном цикле статей речь пойдет не о DIY разработке а-ля «Умный офис» или «коробочном» продукте, который мы предлагаем купить. Целью цикла является ознакомление читателей с основами работы по трем продуктам небезызвестной компании Atmel:
- Микроконтроллером из серии SAMD21 с ядром Cortex-M0+
- Wi-Fi модулем WINC1500
- Технологией сенсорных кнопок Q-touch
Только вместо привычного мигания светодиодом на отладке, мы для пущей масштабности решили помигать светильниками в нашем офисе через Wi-Fi сеть с использованием промышленного протокола ModBus TCP, а управлять будем при помощи сенсорных кнопок и слайдеров. Использовать для этого будем уже хорошо знакому нашим читателям отладочную плату SAMD21 Xplained Pro, c 2-мя подключенными к ней модулями расширения ATWINC1500-XPRO и ATQT1-XPRO. Подключив модули расширения к отладке получаем следующую конструкцию:
Постановка задачи
У нас в офисе установлены светодиодные управляемые (читай диммируемые) светильники, работающие по протоколу DALI. Для связи с внешним миром система освещения использует шлюз ModBus-DALI нашей разработки. Шлюз может управляться как по интерфейсу RS485 (ModBus RTU), так и через Ethernet (ModBus TCP). Для решения нашей задачи будем использовать последний. Таким образом, наша конструкция подключается к локальной сети по Wi-Fi, микроконтроллер анализирует состояние сенсорных кнопок и слайдера и преобразует их в команды протокола ModBus TCP. Те, в свою очередь, преобразуются в команды DALI и отсылаются на исполнение светильникам.
Примечание: шлюз ModBus DALI не является объектом описания в данном цикле статей, а принимается как данность и единственный способ интеграции в систему управления освещением (СУО).
Подключение Wi-Fi модуля WINC1500
Atmel, как и многие другие производители полупроводниковой техники, пытается найти себе тепленькое местечко в малопонятном, но набирающем силу и популярность финансовом «пузыре» под названием Internet Of Things. Поэтому свои Wi-Fi решения Atmel позиционирует как малопотребляющие и рассчитанные на применение в устройствах интернета вещей. Рассматриваемый в сегодняшней статье модуль WINC1500 рассчитан на работу с хост контроллером по последовательному интерфейсу UART/SPI/I2C. Модуль имеет чип-антенну на борту.
- IEEE®802.11 b/g/n 20MHz (1×1) solution
- Single spatial stream in 2.4GHz ISM band
- Integrated PA and T/R Switch
- Integrated PCB antenna
- Superior Sensitivity and Range via advanced PHY signal processing
- Advanced Equalization and Channel Estimation
- Advanced Carrier and Timing Synchronization
- Wi-Fi Direct and Soft-AP support
- Supports IEEE 802.11 WEP, WPA, WPA2 Security
- Supports China WAPI security
- Superior MAC throughput via hardware accelerated two-level A-MSDU/A-MPDU frame aggregation and block acknowledgement
- On-chip memory management engine to reduce host load
- SPI, UART, and I2C host interfaces
- 2- or 3-wire Bluetooth® coexistence interface
- Operating temperature range of -40°C to +85°C
- I/O operating voltage of 2.7V to 3.6V
- Integrated Flash memory for system software
- Power Save Modes
- Integrated Network IP stack to minimize host CPU requirements
- Network features TCP, UDP, DHCP, ARP, HTTP, SSL, and DNS
- Small footprint host driver (4KB flash — less than 1KB RAM)
Подключение модуля расширения к отладке и предварительная настройка
Отладка SAMD21 Xplained имеет 3 внешних разъема для подключения плат расширения. По умолчанию проект создается из предположения, что плата расширения с WINC1500 подключена к разъему отладки EXT1. Если требуется подключить её к другому разъему нужно в файле conf_winc.h поменять порты:
// ---------- PIN settings ---------
#define CONF_WINC_PIN_RESET PIN_PA27
#define CONF_WINC_PIN_CHIP_ENABLE PIN_PA28
#define CONF_WINC_PIN_WAKE PIN_PB08
// ---------- SPI settings ---------
#define CONF_WINC_USE_SPI (1)
/** SPI pin and instance settings. */
#define CONF_WINC_SPI_MODULE SERCOM2
#define CONF_WINC_SPI_SERCOM_MUX SPI_SIGNAL_MUX_SETTING_D
#define CONF_WINC_SPI_PINMUX_PAD0 PINMUX_PA12C_SERCOM2_PAD0 /* out */
#define CONF_WINC_SPI_PINMUX_PAD1 PINMUX_PA13C_SERCOM2_PAD1 /* sck */
#define CONF_WINC_SPI_PINMUX_PAD2 PINMUX_UNUSED /* cs driven from software */
#define CONF_WINC_SPI_PINMUX_PAD3 PINMUX_PA15C_SERCOM2_PAD3 /* in */
#define CONF_WINC_SPI_CS_PIN PIN_PA14
/** SPI interrupt pin. */
#define CONF_WINC_SPI_INT_PIN PIN_PB09A_EIC_EXTINT9
#define CONF_WINC_SPI_INT_MUX MUX_PB09A_EIC_EXTINT9
#define CONF_WINC_SPI_INT_EIC (9)
Модуль соединен с хост контроллером на отладке по SPI, кроме того к модулю идет порт ввода вывода, которым модуль «дергает» при каком-либо событии (приеме/отправке пакета). В коде настроено аппаратное прерывание при изменении уровня на этом пине. При возникновении прерывания в обработчике увеличивается счетчик событий.
Настройка дебажного вывода в COM-порт
Встроенный отладчик EDBG определяется в диспетчере устройств как отладчик и как виртуальный COM-port, на вывод в который можно настроить один из SERCOM’ов микроконтроллера. Таким образом, можно выводить отладочные сообщения в COM-порт через тот же разъем USB-micro, через который идет программирование и отладка:
static void configure_console(void)
{
struct usart_config usart_conf;
usart_get_config_defaults(&usart_conf);
usart_conf.mux_setting = EDBG_CDC_SERCOM_MUX_SETTING;
usart_conf.pinmux_pad0 = EDBG_CDC_SERCOM_PINMUX_PAD0;
usart_conf.pinmux_pad1 = EDBG_CDC_SERCOM_PINMUX_PAD1;
usart_conf.pinmux_pad2 = EDBG_CDC_SERCOM_PINMUX_PAD2;
usart_conf.pinmux_pad3 = EDBG_CDC_SERCOM_PINMUX_PAD3;
usart_conf.baudrate = 115200;
stdio_serial_init(&cdc_uart_module, EDBG_CDC_MODULE, &usart_conf);
usart_enable(&cdc_uart_module);
}
Callback’и наше всё
Библиотека для работы с модулем WINC1500 построена по принципу Callback’ов, т.е. если мы что-то хотим от модуля (например, получить число доступных Wi-Fi сетей в радиоэфире), то мы вызываем соответствующую функцию (которая посылает нужный запрос в модуль) и модуль уходит «думать». Когда модуль сформирует результат, он изменит состояние пина INT, вызвав тем самым внешнее прерывание в микроконтроллере, обработчик которого увеличивает счетчик событий. При вызове функции m2m_wifi_handle_events (NULL); считывается количество событий и параметры (тип, необходимые данные) каждого события (в нашем примере, это завершение сканирования эфира и готовность выдать число найденных сетей) и вызывается соответствующая функция (Callback), если она инициализирована и зарегистрирована заранее.
Проверка связи с WINC1500 по SPI
Для начала работы необходимо инициализировать модуль, настроить и зарегистрировать необходимые Callback’и. Для чтения параметров модуля callback не нужен, поэтому пока его не регистрируем и не делаем.
int main(void)
{
tstrWifiInitParam settings;
int8_t ret;
// Initialize the board.
system_init();
// Initialize the UART console.
configure_console();
printf(STRING_HEADER);
// Initialize the BSP.
nm_bsp_init();
// Initialize Wi-Fi parameters structure.
memset((uint8_t *)&settings, 0, sizeof(tstrWifiInitParam));
//Initialize Wi-Fi driver with data and status callbacks.
ret = m2m_wifi_init(&settings);
if (M2M_SUCCESS != ret)
{
printf("main: m2m_wifi_init call error!(%d)\r\n", ret);
while (1);
}
// Display WINC1500 chip information.
printf("Chip ID : \r\t\t\t%x\r\n", (unsigned int)nmi_get_chipid());
printf("RF Revision ID : \r\t\t\t%x\r\n", (unsigned int)nmi_get_rfrevid());
printf("Done.\r\n\r\n");
while (1);
return 0;
}
Создание точки доступа
WINC1500 позволяет создавать точку доступа и раздавать подключившимся абонентам IP-адреса по DHCP. Начало main такое же, как в предыдущем примере, но добавляется инициализация структуры, содержащей параметры точки доступа, и создание точки доступа, а также регистрация callback перед инициализацией модуля:
// Initialize Wi-Fi parameters structure.
memset((uint8_t *)&settings, 0, sizeof(tstrWifiInitParam));
// Initialize Wi-Fi driver with data and status callbacks.
settings.pfAppWifiCb = wifi_cb;
ret = m2m_wifi_init(&settings);
if (M2M_SUCCESS != ret)
{
printf("main: m2m_wifi_init call error!(%d)\r\n", ret);
while (1);
}
В режиме точки доступа WINC1500 поддерживается только и WEP шифрование (WPA не поддерживается), либо открытая Wi-Fi сеть. Так же поддерживается только 1 клиент.
// Initialize AP mode parameters structure with SSID, channel and OPEN security type.
memset(&strM2MAPConfig, 0x00, sizeof(tstrM2MAPConfig));
strcpy((char *)&strM2MAPConfig.au8SSID, MAIN_WLAN_SSID);
strM2MAPConfig.u8ListenChannel = MAIN_WLAN_CHANNEL;
strM2MAPConfig.u8SecType = MAIN_WLAN_AUTH;
strM2MAPConfig.au8DHCPServerIP[0] = 192;
strM2MAPConfig.au8DHCPServerIP[1] = 168;
strM2MAPConfig.au8DHCPServerIP[2] = 1;
strM2MAPConfig.au8DHCPServerIP[3] = 1;
#if USE_WEP
strcpy((char *)&strM2MAPConfig.au8WepKey, MAIN_WLAN_WEP_KEY);
strM2MAPConfig.u8KeySz = strlen(MAIN_WLAN_WEP_KEY);
strM2MAPConfig.u8KeyIndx = MAIN_WLAN_WEP_KEY_INDEX;
#endif
/* Bring up AP mode with parameters structure. */
ret = m2m_wifi_enable_ap(&strM2MAPConfig);
if (M2M_SUCCESS != ret)
{
printf("main: m2m_wifi_enable_ap call error!\r\n");
while (1);
}
printf("AP mode started. You can connect to %s.\r\n", (char *)MAIN_WLAN_SSID);
while (1)
{
while (m2m_wifi_handle_events(NULL) != M2M_SUCCESS); // Handle pending events from network controller.
}
// Код callback
static void wifi_cb(uint8_t u8MsgType, void *pvMsg)
{
switch (u8MsgType) {
case M2M_WIFI_RESP_CON_STATE_CHANGED:
{
tstrM2mWifiStateChanged *pstrWifiState = (tstrM2mWifiStateChanged *)pvMsg;
if (pstrWifiState->u8CurrState == M2M_WIFI_CONNECTED) {
} else if (pstrWifiState->u8CurrState == M2M_WIFI_DISCONNECTED) {
printf("Station disconnected\r\n");
}
break;
}
case M2M_WIFI_REQ_DHCP_CONF:
{
uint8_t *pu8IPAddress = (uint8_t *)pvMsg;
printf("Station connected\r\n");
printf("Station IP is %u.%u.%u.%u\r\n",
pu8IPAddress[0], pu8IPAddress[1], pu8IPAddress[2], pu8IPAddress[3]);
break;
}
default:
{
break;
}
}
}
Файл main.h:
/** Security mode */
#define USE_WEP (1) /*< Set to (1) to use WEP, and (0) to use OPEN */
/** AP mode Settings */
#define MAIN_WLAN_SSID "WINC1500_AP" /* < SSID */
#if USE_WEP
#define MAIN_WLAN_AUTH M2M_WIFI_SEC_WEP /* < Security manner */
#define MAIN_WLAN_WEP_KEY "1234567890" /* < Security Key in WEP Mode */
#define MAIN_WLAN_WEP_KEY_INDEX (1)
#else
#define MAIN_WLAN_AUTH M2M_WIFI_SEC_OPEN /* < Security manner */
#endif
#define MAIN_WLAN_CHANNEL (6) /* < Channel number */
Работа в режиме точки доступа, в связи с озвученными ограничениями, не является основным режимом работы WINC1500, а служит в основном для реализации механизма подключения к сторонней точке доступа. Например, если в Вашем устройстве нет интерфейса для того, чтобы задать параметры сети (SSID и пароль), к которой необходимо подключиться, можно сначала создать собственную точку доступа и подключившись к ней передать каким-либо способом данные о целевой сети.
Кстати в модуле уже реализован этот механизм: специальной командой можно помимо старта собственной точки доступа, можно запустить встроенный в модуль WEB-сервер и подключившись к нему браузером задать параметры подключения к целевой сети.
Получение списка доступных сетей
Теперь просканируем эфир на предмет наличия Wi-Fi сетей и выведем их список в отладочный порт.
Для запуска сканирования достаточно вызвать функцию m2m_wifi_request_scan (M2M_WIFI_CH_ALL), сразу после инициализации модуля
/* Initialize Wi-Fi parameters structure. */
memset((uint8_t *)&settings, 0, sizeof(tstrWifiInitParam));
/* Initialize Wi-Fi driver with data and status callbacks. */
settings.pfAppWifiCb = wifi_cb;
ret = m2m_wifi_init(&settings);
if (M2M_SUCCESS != ret)
{
printf("main: m2m_wifi_init call error!(%d)\r\n", ret);
while (1);
}
/* Request scan. */
m2m_wifi_request_scan(M2M_WIFI_CH_ALL);
Но при этом нужно предусмотреть соответствующую обработку событий по сканированию в callback wifi_cb
static void wifi_cb(uint8_t u8MsgType, void *pvMsg)
{
switch (u8MsgType) {
case M2M_WIFI_RESP_SCAN_DONE:
{
tstrM2mScanDone *pstrInfo = (tstrM2mScanDone *)pvMsg;
scan_request_index = 0;
if (pstrInfo->u8NumofCh >= 1)
{
m2m_wifi_req_scan_result(scan_request_index);
scan_request_index++;
}
else
{
m2m_wifi_request_scan(M2M_WIFI_CH_ALL);
}
break;
}
case M2M_WIFI_RESP_SCAN_RESULT:
{
tstrM2mWifiscanResult *pstrScanResult = (tstrM2mWifiscanResult *)pvMsg;
uint16_t demo_ssid_len;
uint16_t scan_ssid_len = strlen((const char *)pstrScanResult->au8SSID);
/* display founded AP. */
printf("[%d] SSID:%s\r\n", scan_request_index, pstrScanResult->au8SSID);
num_founded_ap = m2m_wifi_get_num_ap_found();
if (scan_ssid_len) {
/* check same SSID. */
demo_ssid_len = strlen((const char *)MAIN_WLAN_SSID);
if( (demo_ssid_len == scan_ssid_len) && (!memcmp(pstrScanResult->au8SSID, (uint8_t *)MAIN_WLAN_SSID, demo_ssid_len)))
{
/* A scan result matches an entry in the preferred AP List.
* Initiate a connection request.
*/
printf("Found %s \r\n", MAIN_WLAN_SSID);
m2m_wifi_connect((char *)MAIN_WLAN_SSID,sizeof(MAIN_WLAN_SSID), MAIN_WLAN_AUTH,(void *)MAIN_WLAN_PSK,M2M_WIFI_CH_ALL);
break;
}
}
if (scan_request_index < num_founded_ap)
{
m2m_wifi_req_scan_result(scan_request_index);
scan_request_index++;
}
else
{
printf("can not find AP %s\r\n", MAIN_WLAN_SSID);
m2m_wifi_request_scan(M2M_WIFI_CH_ALL);
}
break;
}
case M2M_WIFI_RESP_CON_STATE_CHANGED:
{
tstrM2mWifiStateChanged *pstrWifiState = (tstrM2mWifiStateChanged *)pvMsg;
if (pstrWifiState->u8CurrState == M2M_WIFI_CONNECTED)
{
m2m_wifi_request_dhcp_client();
}
else if (pstrWifiState->u8CurrState == M2M_WIFI_DISCONNECTED)
{
printf("Wi-Fi disconnected\r\n");
/* Request scan. */
m2m_wifi_request_scan(M2M_WIFI_CH_ALL);
}
break;
}
case M2M_WIFI_REQ_DHCP_CONF:
{
uint8_t *pu8IPAddress = (uint8_t *)pvMsg;
printf("Wi-Fi connected\r\n");
printf("Wi-Fi IP is %u.%u.%u.%u\r\n", pu8IPAddress[0], pu8IPAddress[1], pu8IPAddress[2], pu8IPAddress[3]);
break;
}
default:
{
break;
}
}
}
Отлаживать соединение и передачу данных по UDP/TCP и вообще дебажить очень удобно Геркулесом)
Дебажный вывод результата сканирования:
Подключение по DHCP
По умолчанию WINC подключается по DHCP, и по намекам в разных комментариях и документах Atmel, они хотят прекрыть возможность подключаться со статическим IP.
При подключении по DHCP код callback должен обрабатывать соответствующее событие.
static void wifi_cb(uint8_t u8MsgType, void *pvMsg)
{
switch (u8MsgType) {
case M2M_WIFI_RESP_CON_STATE_CHANGED:
{
tstrM2mWifiStateChanged *pstrWifiState = (tstrM2mWifiStateChanged *)pvMsg;
if (pstrWifiState->u8CurrState == M2M_WIFI_CONNECTED)
{
printf("wifi_cb: M2M_WIFI_RESP_CON_STATE_CHANGED: CONNECTED\r\n");
m2m_wifi_request_dhcp_client();
}
else if (pstrWifiState->u8CurrState == M2M_WIFI_DISCONNECTED)
{
printf("wifi_cb: M2M_WIFI_RESP_CON_STATE_CHANGED: DISCONNECTED\r\n");
wifi_connected = 0;
m2m_wifi_connect((char *)MAIN_WLAN_SSID, sizeof(MAIN_WLAN_SSID), MAIN_WLAN_AUTH, (char *)MAIN_WLAN_PSK, M2M_WIFI_CH_ALL);
}
break;
}
case M2M_WIFI_REQ_DHCP_CONF:
{
uint8_t *pu8IPAddress = (uint8_t *)pvMsg;
wifi_connected = 1;
printf("wifi_cb: M2M_WIFI_REQ_DHCP_CONF: IP is %u.%u.%u.%u\r\n",pu8IPAddress[0], pu8IPAddress[1], pu8IPAddress[2], pu8IPAddress[3]);
break;
}
default:
{
break;
}
}
}
Подключение со статическим адресом
Для подключения со статическим адресом сначала надо запретить автоматическое подключение по dhcp в main после инициализации модуля:
m2m_wifi_enable_dhcp(0);
И соответствующим образом изменить callback:
static void wifi_cb(uint8_t u8MsgType, void *pvMsg)
{
printf("wifi_cb: u8MsgType= %d\n",u8MsgType);
switch (u8MsgType) {
case M2M_WIFI_RESP_CON_STATE_CHANGED:
{
tstrM2mWifiStateChanged *pstrWifiState = (tstrM2mWifiStateChanged *)pvMsg;
if (pstrWifiState->u8CurrState == M2M_WIFI_CONNECTED)
{
printf("wifi_cb: M2M_WIFI_RESP_CON_STATE_CHANGED: CONNECTED\r\n");
tstrM2MIPConfig ip_client;
ip_client.u32StaticIP = _htonl(0xc0a8142d); //corresponds to 192.168.20.45
ip_client.u32DNS = _htonl(0xc0a80266); //corresponds to 192.168.2.102
ip_client.u32SubnetMask =_htonl(0xFFFFFF00); //corresponds to 255.255.255.0
ip_client.u32Gateway = _htonl(0xc0a81401); //corresponds to 192.168.20.1
m2m_wifi_set_static_ip(&ip_client);
wifi_connected = 1;
}
else
{
if (pstrWifiState->u8CurrState == M2M_WIFI_DISCONNECTED)
{
printf("wifi_cb: M2M_WIFI_RESP_CON_STATE_CHANGED: DISCONNECTED\r\n");
wifi_connected = 0;
m2m_wifi_connect((char *)MAIN_WLAN_SSID, sizeof(MAIN_WLAN_SSID), MAIN_WLAN_AUTH, (char *)MAIN_WLAN_PSK, M2M_WIFI_CH_ALL);
}
}
break;
}
default:
break;
}
}
Работа с TCP/IP стеком
Большинство API для работы с TCP/IP стеком через WINC1500 реализованы по аналогии с сокетам Беркли.
Беркли API | WINC API | Server/Client | TCP/UDP | Описание |
---|---|---|---|---|
socket | socket | Both | Both | Создает новый сокет |
connect | connect | Client | TCP | Устанавливает TCP соединение с удаленным сервером |
bind | bind | Server | Both | Биндит сокет с IP адресом и портом |
listen | listen | Server | TCP | Создает TCP сервер |
send | send | Both | Both | Отправляет данные в сокет |
sendto | sendto | Both | UDP | Посылает пакет по UDP |
recv | recv | Both | Both | Получает пакет |
recvfrom | recvfrom | Both | Both | Получает пакет |
close | close | Both | Both | Закрывает соединение и освобождает ресурсы |
gethostbyname | gethostbyname | Both | Both | Получает IP адрес по имени хоста |
setsockopt | setsockopt | Both | Both | Устанавливает опции сокета |
htons/ntohs | htons/ntohs | Both | Both | Меняет порядок байт в 2-байтовых целых из представления хоста в сетевое и наоборот |
htonl/ntohl | htonl/ntohl | Both | Both | Меняет порядок байт в 4-байтовых целых из представления хоста в сетевое и наоборот |
Примечание: API функции Беркли write, read, gethostbyaddr, select, poll, getsockopt в WINC1500 не поддерживаются.
UDP и TCP
Для работы по UDP и TCP необходимо создать соответствующие сокеты, инициализировать их, открыть, написать и зарегистрировать callback для обработки приема/передачи данных. Не будем подробно останавливаться на каждом случае (UDP отправка/прием, TCP клиент/сервер), рассмотрим основные моменты.
Инициализация параметров сокета осуществляется с помощью соответствующей структуры. В ней нужно задать тип подключения, порт, IP сервера (в виде 0xc0a814ff, например для 192.168.20.255)
struct sockaddr_in src_addr;
addr.sin_family = AF_INET;
addr.sin_port = _htons(MAIN_WIFI_M2M_SERVER_PORT);
addr.sin_addr.s_addr = _htonl(MAIN_WIFI_M2M_SERVER_IP);
Инициализируем сокет и callback к нему, а также подключаемся к сети с заданными ранее параметрами в структуре addr:
socketInit();
registerSocketCallback(socket_cb, NULL);
// Connect to router.
m2m_wifi_connect((char *)MAIN_WLAN_SSID, sizeof(MAIN_WLAN_SSID), MAIN_WLAN_AUTH, (char *)MAIN_WLAN_PSK, M2M_WIFI_CH_ALL);
printf("m2m_wifi_connect!\r\n");
В основном цикле создаем сокет и биндим его и реализовываем поддержку соединения (в случае отключения переподключение). И для UDP и для TCP эту процедуры аналогичны за исключением типа подключения (SOCK_DGRAM UDP, SOCK_STREAM TCP).
SOCKET rx_socket = -1;
В while(1):
if (rx_socket < 0)
{
if ((rx_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("main: failed to create RX UDP Client socket error!\r\n");
continue;
}
// Socket bind
bind(rx_socket, (struct sockaddr *)&src_addr, sizeof(struct sockaddr_in));
}
if (wifi_connected == M2M_WIFI_CONNECTED)
{
// Open client socket.
if (rx_socket < 0)
{
if ((rx_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("main: failed to create TCP client socket error!\r\n");
continue;
}
// Connect server
printf("socket_number new connection: %d\r\n", rx_socket);
ret=connect(rx_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
printf("ret value: %d\r\n", ret);
connected = FALSE;
if (ret < 0)
{
close(rx_socket);
rx_socket = -1;
}
}
}
Callback должен обрабатывать события подключения/бинда, приема, передачи данных и для сервера TCP: listen, accept.
Пример callback для одновременной работы то UDP (rx_socket) и TCP (tcp_client_socket):
static void socket_cb(SOCKET sock, uint8_t u8Msg, void *pvMsg)
{
// Check for socket event on RX socket.
if (sock == rx_socket)
{
if (u8Msg == SOCKET_MSG_BIND)
{
tstrSocketBindMsg *pstrBind = (tstrSocketBindMsg *)pvMsg;
if (pstrBind && pstrBind->status == 0)
{
// Prepare next buffer reception.
sock_bind_state = 1;
recv(sock, udp_data_rx, MAIN_WIFI_M2M_BUFFER_SIZE, 0);
}
else
{
printf("socket_cb: bind error!\r\n");
}
}
else if (u8Msg == SOCKET_MSG_RECVFROM)
{
tstrSocketRecvMsg *pstrRx = (tstrSocketRecvMsg *)pvMsg;
if (pstrRx->pu8Buffer && pstrRx->s16BufferSize)
{
delay = 0;
sock_rx_state = pstrRx->s16BufferSize;
printf("socket_cb udp recv!\r\n");
printf("rx packet length= %d\n",pstrRx->s16BufferSize);
}
else
{
if (pstrRx->s16BufferSize == SOCK_ERR_TIMEOUT)
{
// Prepare next buffer reception.
recv(sock, udp_data_rx, MAIN_WIFI_M2M_BUFFER_SIZE, 0);
}
}
}
if (u8Msg == SOCKET_MSG_SENDTO)
{
recv(sock, udp_data_rx, MAIN_WIFI_M2M_BUFFER_SIZE, 0);
sock_tx_state = 1;
}
}
if (sock == tcp_client_socket)
{
switch (u8Msg)
{
// Socket connected
case SOCKET_MSG_CONNECT:
{
tstrSocketConnectMsg *pstrConnect = (tstrSocketConnectMsg *)pvMsg;
if (pstrConnect && pstrConnect->s8Error >= 0)
{
printf("socket_cb tcp connect!\r\n");
tcp_ready_to_send=1;
}
else
{
close(tcp_client_socket);
tcp_client_socket = -1;
}
}
break;
// Message send
case SOCKET_MSG_SEND:
{
printf("socket_cb tcp send!\r\n");
recv(tcp_client_socket, tcp_data_rx, sizeof(tcp_data_rx), 0);
tcp_tx_ready=1;
}
break;
// Message receive
case SOCKET_MSG_RECV:
{
tstrSocketRecvMsg *pstrRecv = (tstrSocketRecvMsg *)pvMsg;
if (pstrRecv && pstrRecv->s16BufferSize > 0)
{
tcp_rx_ready=pstrRecv->s16BufferSize;
}
else
{
close(tcp_client_socket);
tcp_client_socket = -1;
}
}
break;
default:
break;
}
}
}
При этом важно не забывать вызывать в while (1) функцию обработки событий от модуля:
m2m_wifi_handle_events(NULL);
Заключение
Теперь, используя полученные знания мы уже можем подключиться по локальной сети к нашему ModBus-DALI шлюзу. Шлюз является TCP сервером на 502 порту. Но об этом в третьей части цикла, который мы посвятим реализации протокола ModBus TCP поверх нашей локальной сети. Во второй части рассмотрим подключение и использование сенсорных кнопок в качестве интерфейса управления. До новых встреч.