📜 ⬆️ ⬇️

Office lighting control over Wi-Fi. Part 3: Driving the Light



Today we will finally complete this cycle by connecting the knowledge and developments gained in the first and second parts: we will manage office lights using touch-sensitive buttons via Wi-Fi using the ModBus TCP protocol via the ModBus-DALI gateway.

Summary of the previous series
We learned using the Atmel WINC1500 Wi-Fi module, connects to a third-party access point, and also connect as a client to a TCP server and transfer data to it. The work with touch buttons and sliders was also mastered, and the corresponding video was captured.

Recall the overall concept. The Atmel SAMD21 microcontroller is connected to debugging with a Q-touch expansion board, on which there are three buttons, a slider and a rotor. We will use one button to turn on the lights, another to turn off and a slider to smoothly adjust the brightness. A board with a WINC1500 module is connected to the second port. Our device (or rather, the prototype) will act as a TCP client. The server will be the DALIGW1 - ModBus TCP device, which converts ModBus commands into DALI (Digital Addressable Lighting Interface) protocol commands - a special protocol for digital lighting control. Thus, we will receive the position values ​​of the sensors and form requests in accordance with the Modbus protocol and send via Wi-fi to the gateway.

TCP client

For simplicity, we will not create an access point, scan available networks and ask the user to connect to one of them, but specify the necessary settings for our office LAN directly in the code.
At the beginning of work, you can open one of the projects created earlier (according to previous parts of the article) or a sample project for Qtouch or WINC1500, and add the necessary modules using Wizard, as described in one of our previous articles.
')
Configure the network settings in the main.h file:
#define MAIN_WLAN_SSID "Habrahabr" //   #define MAIN_WLAN_AUTH M2M_WIFI_SEC_WPA_PSK //   #define MAIN_WLAN_PSK "123456789" //    #define MAIN_WIFI_M2M_PRODUCT_NAME "Hello world!\n\r" #define MAIN_WIFI_M2M_SERVER_IP 0xc0a81490 // IP   () #define MAIN_WIFI_M2M_SERVER_PORT (502) //  .  502  ModBus TCP 


In main.c, we set up a library to work with winc1500 in tcp client mode.
  //Initialize the BSP. nm_bsp_init(); // Initialize socket address structure. 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); // Initialize Wi-Fi parameters structure. memset((uint8_t *)¶m, 0, sizeof(tstrWifiInitParam)); // Initialize Wi-Fi driver with data and status callbacks. param.pfAppWifiCb = wifi_cb; ret = m2m_wifi_init(¶m); if (M2M_SUCCESS != ret) { printf("main: m2m_wifi_init call error!(%d)\r\n", ret); while (1); } // Initialize socket module 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); 


Do not forget about our favorite callbacks. We register functions of event handling on a network.
Callback code
 static void socket_cb(SOCKET sock, uint8_t u8Msg, void *pvMsg) { switch (u8Msg) { // Socket connected case SOCKET_MSG_CONNECT: { tstrSocketConnectMsg *pstrConnect = (tstrSocketConnectMsg *)pvMsg; if (pstrConnect && pstrConnect->s8Error >= 0) { printf("socket_cb: connect success!\r\n"); if (button_pressed!=0) { send(tcp_client_socket, &data_to_send, 12, 0); printf("socket_number after connection: %d\r\n", tcp_client_socket); } close(tcp_client_socket); delay_ms(50); } else { printf("socket_cb: connect error!\r\n"); close(tcp_client_socket); tcp_client_socket = -1; } } break; // Message send case SOCKET_MSG_SEND: { printf("socket_cb: send success!\r\n"); recv(tcp_client_socket, gau8SocketTestBuffer, sizeof(gau8SocketTestBuffer), 0); } break; // Message receive case SOCKET_MSG_RECV: { tstrSocketRecvMsg *pstrRecv = (tstrSocketRecvMsg *)pvMsg; if (pstrRecv && pstrRecv->s16BufferSize > 0) { printf("socket_cb: recv success!\r\n"); printf("TCP Client Test Complete!\r\n"); } else { printf("socket_cb: recv error!\r\n"); close(tcp_client_socket); tcp_client_socket = -1; } } break; default: break; } } 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"); 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]); printf("wifi_cb: M2M_WIFI_REQ_DHCP_CONF: getaway IP is %u.%u.%u.%u\r\n", pu8IPAddress[4], pu8IPAddress[5], pu8IPAddress[6], pu8IPAddress[7]); printf("wifi_cb: M2M_WIFI_REQ_DHCP_CONF: DNS IP is %u.%u.%u.%u\r\n", pu8IPAddress[8], pu8IPAddress[9], pu8IPAddress[10], pu8IPAddress[11]); printf("wifi_cb: M2M_WIFI_REQ_DHCP_CONF: mask is %u.%u.%u.%u\r\n", pu8IPAddress[12], pu8IPAddress[13], pu8IPAddress[14], pu8IPAddress[15]); break; } default: break; } } 



In practice, this is all the code needed to work with winc as a client tcp.

Qtouch

All the code described in the second part of our article for handling buttons and slider is completely transferred, you just need to add the formation and sending of the tcp parcels.

Putting it all together

The ModBus protocol is simple, open and passionately loved in automation systems, so you can find lots of information about it on the Internet. That's what the wiki says. We, as already mentioned, will use its TCP modification designed for operation in local networks.
Since we have a training task, we will not implement the beautiful ModBus library (through separate functions, indicating the register, data, etc.), but simply form the corresponding set of bytes and send them to an open socket. In our case, the Modbus package will change only in the part of the data recorded in the register (in accordance with the protocol of work with our gateway). Use the write command in one register - command code 0x06.
Package Fields:

The purpose of this article was not to immerse the reader into the specifics of the DALI protocol, therefore I will simply list the commands used with a brief explanation:

Note: “scientists argue” about how correctly the ModBus master (which is our debugging role) to communicate with the slaves - close the socket after the end of the current communication session or keep it open. In our example, we went the first way, assuming that other masters may also be on the bus who wish to connect to our gateway. But there’s probably no right approach, and it all depends on the developer’s worldview.

That's all. The final part came out not particularly voluminous, but the whole main stuffing was in the first two parts. I hope it was interesting.
Summary code main
 int main(void) { uint8_t button1_state; uint8_t button2_state; uint8_t slider_state; uint8_t slider_position; tstrWifiInitParam param; int8_t ret; struct sockaddr_in addr; // Initialize and configure system and generic clocks. // Use conf_clocks.h to configure system and generic clocks. system_init(); // Enable global interrupts. system_interrupt_enable_global(); //Initialize delay service. delay_init(); //Initialize timer. (RTC actually) timer_init(); //Initialize QTouch library and configure touch sensors. touch_sensors_init(); // Configure port pins configure_port_pins(); // Turn off all extension board LEDs port_pin_set_output_level(LED_0_PIN, 1); port_pin_set_output_level(LED_1_PIN, 1); port_pin_set_output_level(LED_2_PIN, 1); port_pin_set_output_level(LED_3_PIN, 1); port_pin_set_output_level(LED_4_PIN, 1); port_pin_set_output_level(LED_5_PIN, 1); port_pin_set_output_level(LED_6_PIN, 1); port_pin_set_output_level(LED_7_PIN, 1); port_pin_set_output_level(LED_8_PIN, 1); port_pin_set_output_level(LED_9_PIN, 1); port_pin_set_output_level(LED_R_PIN, 1); port_pin_set_output_level(LED_G_PIN, 1); port_pin_set_output_level(LED_B_PIN, 1); PWM_Count = 0; NVMCTRL->CTRLB.bit.SLEEPPRM = 3; system_set_sleepmode(SYSTEM_SLEEPMODE_STANDBY); // Initialize the UART console. configure_console(); printf(STRING_HEADER); //Initialize the BSP. nm_bsp_init(); // Initialize socket address structure. 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); // Initialize Wi-Fi parameters structure. memset((uint8_t *)¶m, 0, sizeof(tstrWifiInitParam)); // Initialize Wi-Fi driver with data and status callbacks. param.pfAppWifiCb = wifi_cb; ret = m2m_wifi_init(¶m); if (M2M_SUCCESS != ret) { printf("main: m2m_wifi_init call error!(%d)\r\n", ret); while (1); } // Initialize socket module 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); while (1) { // Goto STANDBY sleep mode, unless woken by timer or PTC interrupt. system_sleep(); // Start touch sensor measurement, if touch_time.time_to_measure_touch flag is set by timer. touch_sensors_measure(); if (measure_tick<INACTIVITY_DELAY) { measure_tick++; } if ((p_mutlcap_measure_data->measurement_done_touch == 1u)) { p_mutlcap_measure_data->measurement_done_touch = 0u; // Get touch sensor states button1_state = GET_MUTLCAP_SENSOR_STATE(0); button2_state = GET_MUTLCAP_SENSOR_STATE(1); rotor_state = GET_MUTLCAP_SENSOR_STATE(2); slider_state = GET_MUTLCAP_SENSOR_STATE(3); if (button1_state) { if(button_pressed!=1) { port_pin_set_output_level(LED_8_PIN, 0); button_pressed=1; form_modbus_packet(0x05,DALI_OFF ); tcp_client_socket = socket(AF_INET, SOCK_STREAM, 0); ret=connect(tcp_client_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); } } else { port_pin_set_output_level(LED_8_PIN, 1); if (button_pressed==1) { button_pressed=0; } } if (button2_state) { if(button_pressed!=2) { port_pin_set_output_level(LED_9_PIN, 0); button_pressed=2; form_modbus_packet(0x05,DALI_RECALL_MAX_LEVEL); tcp_client_socket = socket(AF_INET, SOCK_STREAM, 0); ret=connect(tcp_client_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); } } else { port_pin_set_output_level(LED_9_PIN, 1); if (button_pressed==2) { button_pressed=0; } } // Clear all slider controlled LEDs port_pin_set_output_level(LED_0_PIN, 1); port_pin_set_output_level(LED_1_PIN, 1); port_pin_set_output_level(LED_2_PIN, 1); port_pin_set_output_level(LED_3_PIN, 1); port_pin_set_output_level(LED_4_PIN, 1); port_pin_set_output_level(LED_5_PIN, 1); port_pin_set_output_level(LED_6_PIN, 1); port_pin_set_output_level(LED_7_PIN, 1); // If slider is activated if(slider_state) { // Parse slider position slider_position = GET_MUTLCAP_ROTOR_SLIDER_POSITION(1); // slider_position = slider_position >> 5u; printf("slider_position= %d\n",slider_position); if (measure_tick==INACTIVITY_DELAY) { button_pressed=4; form_modbus_packet(0x05,DALI_ON_AND_STEP_UP); tcp_client_socket = socket(AF_INET, SOCK_STREAM, 0); ret=connect(tcp_client_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); delay_ms(500); } else { if (button_pressed==4) { button_pressed=0; } if ((button_pressed!=3)&&(previous_slider_position!=slider_position)) { button_pressed=3; brightness=slider_position;//<<5u; if (brightness==255) { brightness=254; } printf("brightness= %d \n",brightness); form_modbus_packet(0x04,brightness); tcp_client_socket = socket(AF_INET, SOCK_STREAM, 0); ret=connect(tcp_client_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); previous_slider_position=slider_position; } else { if (button_pressed==3) { button_pressed=0; } } } measure_tick=0; switch(slider_position) { case 0: port_pin_set_output_level(LED_0_PIN, 0); break; case 1: port_pin_set_output_level(LED_0_PIN, 0); port_pin_set_output_level(LED_1_PIN, 0); break; case 2: port_pin_set_output_level(LED_0_PIN, 0); port_pin_set_output_level(LED_1_PIN, 0); port_pin_set_output_level(LED_2_PIN, 0); break; case 3: port_pin_set_output_level(LED_0_PIN, 0); port_pin_set_output_level(LED_1_PIN, 0); port_pin_set_output_level(LED_2_PIN, 0); port_pin_set_output_level(LED_3_PIN, 0); break; case 4: port_pin_set_output_level(LED_0_PIN, 0); port_pin_set_output_level(LED_1_PIN, 0); port_pin_set_output_level(LED_2_PIN, 0); port_pin_set_output_level(LED_3_PIN, 0); port_pin_set_output_level(LED_4_PIN, 0); break; case 5: port_pin_set_output_level(LED_0_PIN, 0); port_pin_set_output_level(LED_1_PIN, 0); port_pin_set_output_level(LED_2_PIN, 0); port_pin_set_output_level(LED_3_PIN, 0); port_pin_set_output_level(LED_4_PIN, 0); port_pin_set_output_level(LED_5_PIN, 0); break; case 6: port_pin_set_output_level(LED_0_PIN, 0); port_pin_set_output_level(LED_1_PIN, 0); port_pin_set_output_level(LED_2_PIN, 0); port_pin_set_output_level(LED_3_PIN, 0); port_pin_set_output_level(LED_4_PIN, 0); port_pin_set_output_level(LED_5_PIN, 0); port_pin_set_output_level(LED_6_PIN, 0); break; case 7: port_pin_set_output_level(LED_0_PIN, 0); port_pin_set_output_level(LED_1_PIN, 0); port_pin_set_output_level(LED_2_PIN, 0); port_pin_set_output_level(LED_3_PIN, 0); port_pin_set_output_level(LED_4_PIN, 0); port_pin_set_output_level(LED_5_PIN, 0); port_pin_set_output_level(LED_6_PIN, 0); port_pin_set_output_level(LED_7_PIN, 0); break; default: port_pin_set_output_level(LED_0_PIN, 1); port_pin_set_output_level(LED_1_PIN, 1); port_pin_set_output_level(LED_2_PIN, 1); port_pin_set_output_level(LED_3_PIN, 1); port_pin_set_output_level(LED_4_PIN, 1); port_pin_set_output_level(LED_5_PIN, 1); port_pin_set_output_level(LED_6_PIN, 1); port_pin_set_output_level(LED_7_PIN, 1); break; } } }//measurement done flag m2m_wifi_handle_events(NULL); if (wifi_connected == M2M_WIFI_CONNECTED) { // Open client socket. if (tcp_client_socket < 0) { if ((tcp_client_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", tcp_client_socket); ret=connect(tcp_client_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); printf("ret value: %d\r\n", ret); if (ret < 0) { close(tcp_client_socket); tcp_client_socket = -1; } } } }//while(1) }//main 

Source: https://habr.com/ru/post/276007/


All Articles