+| Supported Targets | ESP32 | ESP32S2 | ESP32C3 | +| ----------------- | ----- | ------- | ------- |
ESP32-MeshKit-Light is a smart lighting solution based on ESP-WIFI-MESH. The ESP-MeshKit solution features network configuration, upgrade, local control, device association, etc.
ESP32-MeshKit-Light consists of light bulbs with integrated ESP32 chips. The kit will help you better understand ESP-WIFI-MESH features and how to further develop ESP-Meshkit-Light. Before reading this document, please refer to ESP32-MeshKit Guide.
Note: This demo is not limited to ESP32-MeshKit-Light. It can also be used for an ESP32 module connected to an external LED.
The board integrated into ESP32-MeshKit-Light supports 5 types of PWM IO interfaces. The Light's color temperature (CW) and hue (RGB) can be adjusted with the output power of 9 W and 3.5 W respectively.
No. | Name | Type | Description |
---|---|---|---|
1, 7 | GND | P | Ground |
2 | CHIP_PU | I | Chip enabling (High: On); module internal pull-up; alternative for external enabling |
3 | GPIO32 | I/O | RTC 32K_XP (32.768 kHz crystal oscillator input); alternative for function expansion |
4 | GPIO33 | I/O | RTC 32K_XN (32.768 kHz crystal oscillator output); alternative for function expansion |
5 | GPIO0 | I/O | IC internal pull-up; alternative for function expansion |
6 | VDD3.3V | P | Power supply, 3V3 |
8 | GPIO4 | O | PWM_R output control |
9 | GPIO16 | O | PWM_G output control; alternate UART interface (URXD); GPIO16 and GPIO17 can not be used in modules that are integrated with PSRAM |
10 | GPIO5 | O | PWM_B output control; alternate UART interface (UTXD) |
11 | GPIO23 | O | PWM_BR output control |
12 | GPIO19 | O | PWM_CT output control |
13 | GPIO22 | I/O | Shared by PWM; alternative for function expansion |
14 | GPIO15 | I | IC internal pull-up; alternative for function expansion |
15 | GIPO2 | O | IC internal pull-down; alternative for function expansion |
16 | UORXD | I/O | UART interface for debugging and receive end in software downloading |
17 | UOTXD | I/O | UART interface for debugging and transmit end in software downloading |
19 | ANT | I/O | External antenna output |
18, 20 | GND | P | RF ground |
To bring ESP32-MeshKit-Light into network configuration mode, turn it off and on for three consecutive times.
Light Color | Status |
---|---|
Yellow (breathing) | Waiting to be configured, in network configuration mode. |
Orange (breathing) | Connecting to the router to verify the network configuration information received from ESP-Mesh App. |
Green (breathing) | Router information verified successfully and is being sent to other whitelisted devices |
White (solid) | Networked successfully |
Light blue (breathing for 3 seconds) | Starting to upgrade |
Blue (breathing) | Upgraded successfully and waiting to restart |
Red (solid) | Abnormal reboot |
ESP-WIFI-MESH is a networking protocol built atop the Wi-Fi protocol. ESP-WIFI-MESH allows numerous devices (henceforth referred to as nodes) spread over a large physical area (both indoors and outdoors) to be interconnected under a single WLAN (Wireless Local-Area Network). ESP-WIFI-MESH is self-organizing and self-healing meaning the network can be built and maintained autonomously.
Light project realizes the following features:
- Building an ESP-WIFI-MESH Network: involves selecting a root node, then forming downstream connections layer by layer until all nodes have joined the network.
- Mesh Network Configuration: sends network configuration information to ESP-WIFI-MESH devices in a convenient and efficient manner.
- Mesh Upgrade: implements efficient upgrading of ESP-WIFI-MESH devices via automatic retransmission of failed fragments, data compression, reverting to an earlier version and firmware check.
- Communicating via LAN: controls ESP-WIFI-MESH network devices through App, including: device discovery, control, upgrade, etc. Prerequisite: the mobile phone and mesh network are on the same LAN.
For your better understanding of the implementation of the Light project, this section provides a detailed analysis of the code used in this project.
examples/development_kit/light/
├── CMakeLists.txt /* Cmake compiling parameters for the demo */
├── components /* Contains the components used by the demo */
│ └── light_driver /* light driver component */
│ └── light_handle /* light status handle component */
│ └── mesh_utils /* mesh utils component */
├── main /* Stores the main `.c` and `.h` application code files for this demo */
│ ├── Kconfig.projbuild /* Demo configuration file */
│ └── light.c /* main application codes, more info below */
├── Makefile /* Make compiling parameters for the demo */
├── partitions.csv /* Partition table file */
├── README_cn.md /* Quick start guide */
├── README.md /* Quick start guide */
├── sdkconfig /* Current parameters of `make menuconfig` */
├── sdkconfig.defaults /* Default parameters of `make menuconfig` */
└── sdkconfig.old /* Previously saved parameters of `make menuconfig` */
light.c
contains the following main application code, which is necessary to implement ESP-WIFI-MESH.- Code to initialize Wi-Fi stack
- Code to initialize ESP-WIFI-MESH stack
- Code to initialize ESP-NOW
- Code to initialize LED driver
- Code to configure LAN communication
- Code to initialize trigger handler
Once ESP32 system is initialized, app_main
will be called. The following code block shows the main implementation of app_main
function.
void app_main()
{
……
……
……
/**
* @brief Continuous power off and restart more than three times to reset the device
*/
if (restart_count_get() >= LIGHT_RESTART_COUNT_RESET) {
MDF_LOGW("Erase information saved in flash");
mdf_info_erase(MDF_SPACE_NAME);
}
/**
* @brief 1.Initialize event loop, receive event
* 2.Initialize wifi with station mode
* 3.Initialize espnow(ESP-NOW is a kind of connectionless WiFi communication protocol)
*/
MDF_ERROR_ASSERT(mdf_event_loop_init(event_loop_cb));
MDF_ERROR_ASSERT(wifi_init());
MDF_ERROR_ASSERT(mespnow_init());
/**
* @brief Light driver initialization
*/
MDF_ERROR_ASSERT(light_driver_init(&driver_config));
/**
* @brief 1.Get Mwifi initialization configuration information and Mwifi AP configuration information from nvs flash.
* 2.If there is no network configuration information in the nvs flash,
* obtain the network configuration information through the blufi or mconfig chain.
* 3.Indicate the status of the device by means of a light
*/
if (mdf_info_load("init_config", &init_config, sizeof(mwifi_init_config_t)) == MDF_OK
&& mdf_info_load("ap_config", &ap_config, sizeof(mwifi_config_t)) == MDF_OK) {
if (restart_is_exception()) {
light_driver_set_rgb(255, 0, 0); /**< red */
} else {
light_driver_set_switch(true);
}
} else {
light_driver_breath_start(255, 255, 0); /**< yellow blink */
MDF_ERROR_ASSERT(get_network_config(&init_config, &ap_config, LIGHT_TID, LIGHT_NAME));
MDF_LOGI("mconfig, ssid: %s, password: %s, mesh_id: " MACSTR,
ap_config.router_ssid, ap_config.router_password,
MAC2STR(ap_config.mesh_id));
}
/**
* @brief Configure MLink (LAN communication module)
*/
MDF_ERROR_ASSERT(esp_wifi_get_mac(ESP_IF_WIFI_STA, sta_mac));
snprintf(name, sizeof(name), "light_%02x%02x", sta_mac[4], sta_mac[5]);
MDF_ERROR_ASSERT(mlink_add_device(LIGHT_TID, name, CONFIG_LIGHT_VERSION));
MDF_ERROR_ASSERT(mlink_add_characteristic(LIGHT_CID_STATUS, "on", CHARACTERISTIC_FORMAT_INT, CHARACTERISTIC_PERMS_RWT, 0, 3, 1));
……
MDF_ERROR_ASSERT(mlink_add_characteristic_handle(mlink_get_value, mlink_set_value));
/**
* @brief Initialize trigger handler
* while characteristic of device reaching conditions, will trigger the corresponding action.
*/
MDF_ERROR_ASSERT(mlink_trigger_init());
/**
* @brief Initialize espnow_to_mwifi_task for forward esp-now data to the wifi mesh network.
* esp-now data from button or other device.
*/
xTaskCreate(espnow_to_mwifi_task, "espnow_to_mwifi", 1024 * 3, NULL, 1, NULL);
/**
* @brief Add a request handler, handling request for devices on the LAN.
*/
MDF_ERROR_ASSERT(mlink_set_handle("show_layer", light_show_layer));
/**
* @brief Initialize and start esp-mesh network according to network configuration information.
*/
MDF_ERROR_ASSERT(mwifi_init(&init_config));
MDF_ERROR_ASSERT(mwifi_set_config(&ap_config));
MDF_ERROR_ASSERT(mwifi_start());
/**
* @brief Handling data between wifi mesh devices.
*/
xTaskCreate(node_handle_task, "node_handle", 8 * 1024,
NULL, CONFIG_MDF_TASK_DEFAULT_PRIOTY, NULL);
/**
* @brief Periodically print system information.
*/
TimerHandle_t timer = xTimerCreate("show_system_info", 10000 / portTICK_RATE_MS,
true, NULL, show_system_info_timercb);
xTimerStart(timer, 0);
}
Including the following code:
mdf_event_loop_init(event_loop_cb)
: initializes event handler callback function, and all events will be sent to this function.wifi_init()
: initializes Wi-Fi stack.mespnow_init()
: initializes ESP-NOW.light_driver_init(&driver_config)
: initializes LED driver.get_network_config(&init_config, &ap_config)
: gets network configuration information.mlink_add_device(LIGHT_TID, name, CONFIG_LIGHT_VERSION)
: adds a device to LAN communication module.mlink_trigger_init()
: initializes trigger handler module.mwifi_init(&init_config)
: initializes ESP-WIFI-MESH stack.mwifi_set_config(&ap_config)
: sets parameters for ESP-WIFI-MESH.mwifi_start()
: enables ESP-WIFI-MESH.xTimerCreate("show_system_info", 10000 / portTICK_RATE_MS, true, NULL, show_system_info_timercb)
: creates a Timer to print system information periodically.
This section introduces the code used to initialize Wi-Fi stack in details.
static mdf_err_t wifi_init()
{
mdf_err_t ret = nvs_flash_init();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
MDF_ERROR_ASSERT(nvs_flash_erase());
ret = nvs_flash_init();
}
MDF_ERROR_ASSERT(ret);
tcpip_adapter_init();
MDF_ERROR_ASSERT(esp_event_loop_init(NULL, NULL));
MDF_ERROR_ASSERT(esp_wifi_init(&cfg));
MDF_ERROR_ASSERT(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
MDF_ERROR_ASSERT(esp_wifi_set_mode(WIFI_MODE_STA));
MDF_ERROR_ASSERT(esp_wifi_set_ps(WIFI_PS_NONE));
MDF_ERROR_ASSERT(esp_mesh_set_6m_rate(false));
MDF_ERROR_ASSERT(esp_wifi_start());
return MDF_OK;
}
Including the following code:
nvs_flash_init()
: initializes nvs flash before enabling Wi-Fi.tcpip_adapter_init()
: initializes TCP/IP stack.esp_wifi_init(&cfg)
: initializes Wi-Fi.esp_wifi_start()
: starts Wi-Fi according to current configuration information.
You can get such information in three ways:
- Load from nvs flash
- Get from Mconfig-BluFi
- Get from Mconfig-Chain
In this section we are going to introduce the last two methods:
- Get from Mconfig-BluFi.
Including the following code:
mconfig_blufi_security_init
: initializes BluFi encryption.esp_bt_controller_init
andesp_bt_controller_enable
: initialize and enable Bluetooth Controller.esp_bluedroid_init
andesp_bluedroid_enable
: initialize and enable Bluetooth Host.esp_ble_gap_register_callback
: registers GAP callback function.esp_blufi_register_callbacks
: registers BluFi callback function.esp_blufi_profile_init
: initializes BluFi profile.
- Get from Mconfig-Chain
Mconfig-Chain splits devices into two types communicating with each other via ESP-NOW:
- Master: a device that initiates a connection and sends network configuration information to a slave device.
- Slave: a device that accepts a connection request from a master and sends the request for network configuration information to a master.
Slave:
Including the following code:
esp_wifi_set_promiscuous_rx_cb(wifi_sniffer_cb)
: registers sniffer callback function, and listens for IEEE802.11 Wi-Fi packets nearby.scan_mesh_device
: scans surrounding masters.mespnow_write(MESPNOW_TRANS_PIPE_MCONFIG, dest_addr, espnow_data, espnow_size, portMAX_DELAY)
: sends the request for network configuration information to a master.mespnow_read(MESPNOW_TRANS_PIPE_MCONFIG, dest_addr, espnow_data, &espnow_size, 1000 / portTICK_RATE_MS)
: receives encrypted network configuration information from the master.mespnow_read(MESPNOW_TRANS_PIPE_MCONFIG, src_addr, whitelist_compress_data, (size_t *)&whitelist_compress_size, 10000 / portTICK_RATE_MS)
: receives compressed and encrypted whitelist from the master.mconfig_queue_write(&chain_data->mconfig_data, 0)
: sends network configuration information to the queue and marks the completion of the network configuration of the device.
Master:
Including the following code:
esp_wifi_set_vendor_ie(true, WIFI_VND_IE_TYPE_BEACON, WIFI_VND_IE_ID_1, &ie_data)
: sets IEEE802.11 vendor information element into beacon frames to identify this device as the master in mesh network configuration chain.mespnow_read(MESPNOW_TRANS_PIPE_MCONFIG, src_addr, espnow_data, &espnow_size, MCONFIG_CHAIN_EXIT_DELAY / portTICK_RATE_MS)
: receives network configuration request from a slave.mconfig_device_verify(mconfig_data->whitelist_data, mconfig_data->whitelist_size, src_addr, pubkey_pem)
: checks whether this slave is whitelisted, if not, the device cannot connect to this ESP-WIFI-MESH network.mespnow_write(MESPNOW_TRANS_PIPE_MCONFIG, src_addr, espnow_data, (MCONFIG_RSA_CIPHERTEXT_SIZE - MCONFIG_RSA_PLAINTEXT_MAX_SIZE) + sizeof(mconfig_chain_data_t), portMAX_DELAY)
: sends encrypted network configuration information to the slave device.mespnow_write(MESPNOW_TRANS_PIPE_MCONFIG, src_addr, whitelist_compress_data, whitelist_compress_size, portMAX_DELAY);
: sends compressed and encrypted whitelist to the slave device.
Including the following code:
mlink_add_device(LIGHT_TID, name, CONFIG_LIGHT_VERSION)
: adds a device.mlink_add_characteristic(LIGHT_CID_STATUS, "on", CHARACTERISTIC_FORMAT_INT, CHARACTERISTIC_PERMS_RWT, 0, 3, 1)
: adds device characteristic information.mlink_add_characteristic_handle(mlink_get_value, mlink_set_value)
: adds characteristic handler function for a device.
Including the following code:
mlink_trigger_init()
: initializes trigger handler.xTaskCreate(trigger_handle_task, "trigger_handle", 1024 * 3, NULL, 1, NULL)
: creates trigger handler task.mlink_trigger_handle(MLINK_COMMUNICATE_MESH)
: conducts corresponding operation according to the trigger which is configured by App or by callingmlink_trigger_add()
.
Including the following code:
mwifi_init(&init_config)
: initializes ESP-WIFI-MESH.mwifi_set_config(&ap_config)
: sets ESP-WIFI-MESH configuration information.mwifi_start()
: launches ESP-WIFI-MESH.
mdf_event_loop_init(event_loop_cb)
: registers event callback function, and send all the events to this function.- An event indicates the current status of the device, for instance, the device as a root node gets IP address will trigger
MDF_EVENT_MWIFI_ROOT_GOT_IP
event.
static mdf_err_t event_loop_cb(mdf_event_loop_t event, void *ctx)
{
MDF_LOGI("event_loop_cb, event: 0x%x", event);
mdf_err_t ret = MDF_OK;
switch (event) {
case MDF_EVENT_MWIFI_PARENT_DISCONNECTED:
MDF_LOGI("Parent is disconnected on station interface");
if (!esp_mesh_is_root()) {
break;
}
ret = mlink_notice_deinit();
MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_notice_deinit", mdf_err_to_name(ret));
ret = mlink_httpd_stop();
MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_httpd_stop", mdf_err_to_name(ret));
break;
case MDF_EVENT_MWIFI_ROOT_GOT_IP: {
MDF_LOGI("Root obtains the IP address");
ret = mlink_notice_init();
MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_notice_init", mdf_err_to_name(ret));
uint8_t sta_mac[MWIFI_ADDR_LEN] = {0x0};
MDF_ERROR_ASSERT(esp_wifi_get_mac(ESP_IF_WIFI_STA, sta_mac));
ret = mlink_notice_write("http", strlen("http"), sta_mac);
MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_httpd_write", mdf_err_to_name(ret));
ret = mlink_httpd_start();
MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_httpd_start", mdf_err_to_name(ret));
if (!g_root_write_task_handle) {
xTaskCreate(root_write_task, "root_write", 4 * 1024,
NULL, CONFIG_MDF_TASK_DEFAULT_PRIOTY, &g_root_write_task_handle);
}
if (!g_root_read_task_handle) {
xTaskCreate(root_read_task, "root_read", 8 * 1024,
NULL, CONFIG_MDF_TASK_DEFAULT_PRIOTY, &g_root_read_task_handle);
}
break;
}
case MDF_EVENT_MLINK_SYSTEM_REBOOT:
MDF_LOGW("Restart PRO and APP CPUs");
esp_restart();
break;
case MDF_EVENT_MLINK_SET_STATUS:
if (!g_event_group_trigger) {
g_event_group_trigger = xEventGroupCreate();
}
xEventGroupSetBits(g_event_group_trigger, EVENT_GROUP_TRIGGER_HANDLE);
break;
case MDF_EVENT_MESPNOW_RECV:
if ((int)ctx == MESPNOW_TRANS_PIPE_CONTROL) {
xEventGroupSetBits(g_event_group_trigger, EVENT_GROUP_TRIGGER_RECV);
}
break;
default:
break;
}
return MDF_OK;
}
xTaskCreate(request_handle_task, "request_handle", 8 * 1024, NULL, CONFIG_MDF_TASK_DEFAULT_PRIOTY, NULL);
: creates node data processing task.xTaskCreate(root_write_task, "root_write", 4 * 1024, NULL, CONFIG_MDF_TASK_DEFAULT_PRIOTY, &g_root_write_task_handle);
: newly created root node transmits ESP-WIFI-MESH data packets to external IP network, and to various target addresses according to the data types.xTaskCreate(root_read_task, "root_read", 8 * 1024, NULL, CONFIG_MDF_TASK_DEFAULT_PRIOTY, &g_root_read_task_handle)
: newly created root node transmits data packets from external IP network to ESP-WIFI-MESH.
- Root node transmits data packets from external IP network to ESP-WIFI-MESH:
- first identify whether this node is the root;
- read data from external IP network;
- send data to the devices listed in address list.
static void root_read_task(void *arg)
{
mdf_err_t ret = MDF_OK;
mlink_httpd_t *httpd_data = NULL;
mwifi_data_type_t data_type = {
.compression = true,
.communicate = MWIFI_COMMUNICATE_MULTICAST,
};
MDF_LOGI("root_read_task is running");
while (mwifi_is_connected() && esp_mesh_get_layer() == MESH_ROOT) {
if (httpd_data) {
MDF_FREE(httpd_data->addrs_list);
MDF_FREE(httpd_data->data);
MDF_FREE(httpd_data);
}
ret = mlink_httpd_read(&httpd_data, portMAX_DELAY);
MDF_ERROR_CONTINUE(ret != MDF_OK || !httpd_data, "<%s> mwifi_root_read", mdf_err_to_name(ret));
MDF_LOGD("Root receive, addrs_num: %d, addrs_list: " MACSTR ", size: %d, data: %.*s",
httpd_data->addrs_num, MAC2STR(httpd_data->addrs_list),
httpd_data->size, httpd_data->size, httpd_data->data);
memcpy(&data_type.custom, &httpd_data->type, sizeof(mlink_httpd_type_t));
ret = mwifi_root_write(httpd_data->addrs_list, httpd_data->addrs_num,
&data_type, httpd_data->data, httpd_data->size, true);
MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> mwifi_root_write", mdf_err_to_name(ret));
}
MDF_LOGW("root_read_task is exit");
if (httpd_data) {
MDF_FREE(httpd_data->addrs_list);
MDF_FREE(httpd_data->data);
MDF_FREE(httpd_data);
}
g_root_read_task_handle = NULL;
vTaskDelete(NULL);
}
- Node processes ESP-WIFI-MESH data:
- first identify whether this node has been connected to ESP-WIFI-MESH network;
- read the target address into its own data packet;
- check whether this data packet is a firmware upgrade packet, if so, the device will conduct firmware upgrade;
- check whether this packet belongs to LAN communication module, if so, the node will conduct corresponding operation;
- check whether this packet is from non-root node devices, if so, the node transmits this packet to root node.
void request_handle_task(void *arg)
{
mdf_err_t ret = MDF_OK;
uint8_t *data = NULL;
size_t size = MWIFI_PAYLOAD_LEN;
mwifi_data_type_t data_type = {0x0};
uint8_t src_addr[MWIFI_ADDR_LEN] = {0x0};
for (;;) {
if (!mwifi_is_connected()) {
vTaskDelay(100 / portTICK_PERIOD_MS);
continue;
}
size = MWIFI_PAYLOAD_LEN;
MDF_FREE(data);
ret = mwifi_read(src_addr, &data_type, &data, &size, portMAX_DELAY);
MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> Receive a packet targeted to self over the mesh network",
mdf_err_to_name(ret));
if (data_type.upgrade) {
ret = mupgrade_handle(src_addr, data, size);
MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> mupgrade_handle", mdf_err_to_name(ret));
continue;
}
MDF_LOGI("Node receive, addr: " MACSTR ", size: %d, data: %.*s", MAC2STR(src_addr), size, size, data);
mlink_httpd_type_t *httpd_type = (mlink_httpd_type_t *)&data_type.custom;
ret = mlink_handle(src_addr, httpd_type, data, size);
MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> mlink_handle", mdf_err_to_name(ret));
if (httpd_type->from == MLINK_HTTPD_FROM_DEVICE) {
data_type.protocol = MLINK_PROTO_NOTICE;
ret = mwifi_write(NULL, &data_type, "status", strlen("status"), true);
MDF_ERROR_CONTINUE(ret != MDF_OK, "<%s> mlink_handle", mdf_err_to_name(ret));
}
}
MDF_FREE(data);
vTaskDelete(NULL);
}