feature/wifi-provisioning: Added wifi-provisioning component from idf.

Added wifi-provisioning examples and esp_prov tool.
This commit is contained in:
Supreet Deshpande
2019-02-21 15:32:59 +05:30
parent ee8cf35595
commit bfd0647ea8
90 changed files with 8783 additions and 0 deletions

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(custom_config)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := custom_config
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,144 @@
# SoftAP + HTTPD based Provisioning Example featuring Custom configuration
(See the README.md file in the upper level 'examples' directory for more information about examples.)
(Please see the README.md under `softap_prov` example before this.)
`custom_config` example demonstrates the implementation and integration of various IDF components for building a provisioning application.
This is same as `softap_prov` example, with added feature for configuration of some custom data (just like Wi-Fi configuration) during provisioning. The custom data provided during provisioning is simply printed on the serial monitor. The rest of the program functions just like `softap_prov`, ie. the device is configured as Wi-Fi station with supplied AP credentials.
`custom_config` uses the following components :
* `wifi_provisioning` : provides data structures and protocomm endpoint handlers for Wi-Fi configuration
* `protocomm` : for protocol based communication and secure session establishment
* `protobuf` : Google's protocol buffer library for serialization of protocomm data structures
Also, it uses a component provided with this example `custom_provisioning` which provides data structures and protocomm endpoint handlers for custom data configuration
## How to use example
### Hardware Required
Example should be able to run on any commonly available ESP32 development board.
### Application Required
To provision the device running this example, the `esp_prov.py` script needs to be run (found under `$IDF_PATH/tools/esp_prov`). This feature of `esp_prov` should work on all platforms, given the dependencies are satisfied.
### Configure the project
```
make menuconfig
```
* Set serial port under Serial Flasher Options.
* Under Example Configuration set the following :
* SoftAP SSID (Defaults to PROV_<MACID>)
* SoftAP Password (Defaults to PROV_PASS)
* Security Version (default 0)
* Proof of Possession (by default not needed for security version 0)
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (1562) app: SoftAP started
I (1572) app_prov: SoftAP Provisioning started with SSID 'PROV_261FCC', Password 'PROV_PASS'
```
Make sure to connect the client computer to the SoftAP network, whose SSID and Password are displayed in the serial monitor log. On successful connection the monitor log will show :
```
I (519482) tcpip_adapter: softAP assign IP to station,IP is: 192.168.4.2
```
In a separate terminal run the `esp_prov.py` script under `$IDP_PATH/tools/esp_prov` directory (please replace the values corresponding to the parameters `--custom_info` and `--custom_ver` with your desired values for the custom configuration). Assuming default example configuration, the script should be run as follows :
```
python esp_prov.py --ssid myssid --passphrase mypassword --sec_ver 0 --transport softap --softap_endpoint 192.168.4.1:80 --custom_config --custom_info "some string" --custom_ver 4321
```
Above command will perform the provisioning steps, and the monitor log should display something like this :
```
I (92734) app_prov_handler: Custom config received :
Info : some string
Version : 4321
.
.
.
I (634572) app_prov_handler: WiFi Credentials Received :
ssid : myssid
password : mypassword
.
.
.
I (634652) app_prov_handler: WiFi Credentials Applied
I (634652) app_prov: STA Start
.
.
.
I (688270) app_prov_handler: Connecting state
.
.
.
I (637732) app_prov: STA Got IP
I (637732) app: got ip:192.168.43.220
.
.
.
I (654562) app_prov_handler: Connected state
```
After sometime the provisioning app will exit, SoftAP will be turned off and HTTP server will be stopped
```
I (667732) app_prov: Stopping provisioning
I (668732) app_prov: Provisioning stopped
I (668742) app: SoftAP stopped
```
## Troubleshooting
### Provisioning failed
It is possible that the Wi-Fi credentials provided were incorrect, or the device was not able to establish connection to the network, in which the the `esp_prov` script will notify failure (with reason) and the provisioning app will continue running, allowing the user to retry the process. Serial monitor log will display the failure along with disconnect reason :
```
E (39291) app_prov: STA Disconnected
E (39291) app_prov: Disconnect reason : 201
I (39291) app_prov: STA AP Not found
I (42021) app_prov_handler: Disconnected state
```
### Provisioning does not start
If the serial monitor log is different, as shown below :
```
I (539) app_prov: Found ssid myssid
I (539) app_prov: Found password mypassword
I (549) app: Starting WiFi station
```
It means the Wi-Fi credentials were already set by some other application flashed previously to your device. To erase these credentials either do full erase and then flash the example
```
make erase_flash
make -j4 flash monitor
```
Or, enable `Reset Provisioning` option under `Example Configuration` under menuconfig. But this will erase the saved Wi-Fi credentials every time the device boots, so this is not the preferred solution.

View File

@ -0,0 +1,8 @@
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_PRIV_INCLUDEDIRS proto-c)
set(COMPONENT_SRCS "src/custom_config.c"
"proto-c/custom_config.pb-c.c")
set(COMPONENT_PRIV_REQUIRES protobuf-c)
register_component()

View File

@ -0,0 +1,3 @@
COMPONENT_SRCDIRS := src proto-c
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := proto-c

View File

@ -0,0 +1,44 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _CUSTOM_PROV_CONFIG_H_
#define _CUSTOM_PROV_CONFIG_H_
/**
* @brief Custom config data received by device
*/
typedef struct {
char info[128];
int version;
} custom_config_t;
/**
* @brief Internal handler for receiving and responding to protocomm
* requests from master
*
* This is to be passed as priv_data for protocomm request handler
* (refer to `custom_prov_config_data_handler()`) when calling `protocomm_add_endpoint()`.
*/
typedef esp_err_t (*custom_prov_config_handler_t) (const custom_config_t *config);
/**
* @brief Handler for receiving and responding to requests from master
*
* This is to be registered as the `wifi_config` endpoint handler
* (protocomm `protocomm_req_handler_t`) using `protocomm_add_endpoint()`
*/
esp_err_t custom_prov_config_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen,
uint8_t **outbuf, ssize_t *outlen, void *priv_data);
#endif

View File

@ -0,0 +1,229 @@
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
/* Generated from: custom_config.proto */
/* Do not generate deprecated warnings for self */
#ifndef PROTOBUF_C__NO_DEPRECATED
#define PROTOBUF_C__NO_DEPRECATED
#endif
#include "custom_config.pb-c.h"
void custom_config_request__init
(CustomConfigRequest *message)
{
static const CustomConfigRequest init_value = CUSTOM_CONFIG_REQUEST__INIT;
*message = init_value;
}
size_t custom_config_request__get_packed_size
(const CustomConfigRequest *message)
{
assert(message->base.descriptor == &custom_config_request__descriptor);
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t custom_config_request__pack
(const CustomConfigRequest *message,
uint8_t *out)
{
assert(message->base.descriptor == &custom_config_request__descriptor);
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t custom_config_request__pack_to_buffer
(const CustomConfigRequest *message,
ProtobufCBuffer *buffer)
{
assert(message->base.descriptor == &custom_config_request__descriptor);
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
CustomConfigRequest *
custom_config_request__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (CustomConfigRequest *)
protobuf_c_message_unpack (&custom_config_request__descriptor,
allocator, len, data);
}
void custom_config_request__free_unpacked
(CustomConfigRequest *message,
ProtobufCAllocator *allocator)
{
if(!message)
return;
assert(message->base.descriptor == &custom_config_request__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
void custom_config_response__init
(CustomConfigResponse *message)
{
static const CustomConfigResponse init_value = CUSTOM_CONFIG_RESPONSE__INIT;
*message = init_value;
}
size_t custom_config_response__get_packed_size
(const CustomConfigResponse *message)
{
assert(message->base.descriptor == &custom_config_response__descriptor);
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t custom_config_response__pack
(const CustomConfigResponse *message,
uint8_t *out)
{
assert(message->base.descriptor == &custom_config_response__descriptor);
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t custom_config_response__pack_to_buffer
(const CustomConfigResponse *message,
ProtobufCBuffer *buffer)
{
assert(message->base.descriptor == &custom_config_response__descriptor);
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
CustomConfigResponse *
custom_config_response__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (CustomConfigResponse *)
protobuf_c_message_unpack (&custom_config_response__descriptor,
allocator, len, data);
}
void custom_config_response__free_unpacked
(CustomConfigResponse *message,
ProtobufCAllocator *allocator)
{
if(!message)
return;
assert(message->base.descriptor == &custom_config_response__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
static const ProtobufCFieldDescriptor custom_config_request__field_descriptors[2] =
{
{
"info",
1,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_STRING,
0, /* quantifier_offset */
offsetof(CustomConfigRequest, info),
NULL,
&protobuf_c_empty_string,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"version",
2,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_INT32,
0, /* quantifier_offset */
offsetof(CustomConfigRequest, version),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
static const unsigned custom_config_request__field_indices_by_name[] = {
0, /* field[0] = info */
1, /* field[1] = version */
};
static const ProtobufCIntRange custom_config_request__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 2 }
};
const ProtobufCMessageDescriptor custom_config_request__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"CustomConfigRequest",
"CustomConfigRequest",
"CustomConfigRequest",
"",
sizeof(CustomConfigRequest),
2,
custom_config_request__field_descriptors,
custom_config_request__field_indices_by_name,
1, custom_config_request__number_ranges,
(ProtobufCMessageInit) custom_config_request__init,
NULL,NULL,NULL /* reserved[123] */
};
static const ProtobufCFieldDescriptor custom_config_response__field_descriptors[2] =
{
{
"status",
1,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_ENUM,
0, /* quantifier_offset */
offsetof(CustomConfigResponse, status),
&custom_config_status__descriptor,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"dummy",
2,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_INT32,
0, /* quantifier_offset */
offsetof(CustomConfigResponse, dummy),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
static const unsigned custom_config_response__field_indices_by_name[] = {
1, /* field[1] = dummy */
0, /* field[0] = status */
};
static const ProtobufCIntRange custom_config_response__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 2 }
};
const ProtobufCMessageDescriptor custom_config_response__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"CustomConfigResponse",
"CustomConfigResponse",
"CustomConfigResponse",
"",
sizeof(CustomConfigResponse),
2,
custom_config_response__field_descriptors,
custom_config_response__field_indices_by_name,
1, custom_config_response__number_ranges,
(ProtobufCMessageInit) custom_config_response__init,
NULL,NULL,NULL /* reserved[123] */
};
static const ProtobufCEnumValue custom_config_status__enum_values_by_number[2] =
{
{ "ConfigSuccess", "CUSTOM_CONFIG_STATUS__ConfigSuccess", 0 },
{ "ConfigFail", "CUSTOM_CONFIG_STATUS__ConfigFail", 1 },
};
static const ProtobufCIntRange custom_config_status__value_ranges[] = {
{0, 0},{0, 2}
};
static const ProtobufCEnumValueIndex custom_config_status__enum_values_by_name[2] =
{
{ "ConfigFail", 1 },
{ "ConfigSuccess", 0 },
};
const ProtobufCEnumDescriptor custom_config_status__descriptor =
{
PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
"CustomConfigStatus",
"CustomConfigStatus",
"CustomConfigStatus",
"",
2,
custom_config_status__enum_values_by_number,
2,
custom_config_status__enum_values_by_name,
1,
custom_config_status__value_ranges,
NULL,NULL,NULL,NULL /* reserved[1234] */
};

View File

@ -0,0 +1,113 @@
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
/* Generated from: custom_config.proto */
#ifndef PROTOBUF_C_custom_5fconfig_2eproto__INCLUDED
#define PROTOBUF_C_custom_5fconfig_2eproto__INCLUDED
#include <protobuf-c/protobuf-c.h>
PROTOBUF_C__BEGIN_DECLS
#if PROTOBUF_C_VERSION_NUMBER < 1003000
# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
#elif 1003000 < PROTOBUF_C_MIN_COMPILER_VERSION
# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
#endif
typedef struct _CustomConfigRequest CustomConfigRequest;
typedef struct _CustomConfigResponse CustomConfigResponse;
/* --- enums --- */
typedef enum _CustomConfigStatus {
CUSTOM_CONFIG_STATUS__ConfigSuccess = 0,
CUSTOM_CONFIG_STATUS__ConfigFail = 1
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(CUSTOM_CONFIG_STATUS)
} CustomConfigStatus;
/* --- messages --- */
struct _CustomConfigRequest
{
ProtobufCMessage base;
char *info;
int32_t version;
};
#define CUSTOM_CONFIG_REQUEST__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&custom_config_request__descriptor) \
, (char *)protobuf_c_empty_string, 0 }
struct _CustomConfigResponse
{
ProtobufCMessage base;
CustomConfigStatus status;
int32_t dummy;
};
#define CUSTOM_CONFIG_RESPONSE__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&custom_config_response__descriptor) \
, CUSTOM_CONFIG_STATUS__ConfigSuccess, 0 }
/* CustomConfigRequest methods */
void custom_config_request__init
(CustomConfigRequest *message);
size_t custom_config_request__get_packed_size
(const CustomConfigRequest *message);
size_t custom_config_request__pack
(const CustomConfigRequest *message,
uint8_t *out);
size_t custom_config_request__pack_to_buffer
(const CustomConfigRequest *message,
ProtobufCBuffer *buffer);
CustomConfigRequest *
custom_config_request__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void custom_config_request__free_unpacked
(CustomConfigRequest *message,
ProtobufCAllocator *allocator);
/* CustomConfigResponse methods */
void custom_config_response__init
(CustomConfigResponse *message);
size_t custom_config_response__get_packed_size
(const CustomConfigResponse *message);
size_t custom_config_response__pack
(const CustomConfigResponse *message,
uint8_t *out);
size_t custom_config_response__pack_to_buffer
(const CustomConfigResponse *message,
ProtobufCBuffer *buffer);
CustomConfigResponse *
custom_config_response__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void custom_config_response__free_unpacked
(CustomConfigResponse *message,
ProtobufCAllocator *allocator);
/* --- per-message closures --- */
typedef void (*CustomConfigRequest_Closure)
(const CustomConfigRequest *message,
void *closure_data);
typedef void (*CustomConfigResponse_Closure)
(const CustomConfigResponse *message,
void *closure_data);
/* --- services --- */
/* --- descriptors --- */
extern const ProtobufCEnumDescriptor custom_config_status__descriptor;
extern const ProtobufCMessageDescriptor custom_config_request__descriptor;
extern const ProtobufCMessageDescriptor custom_config_response__descriptor;
PROTOBUF_C__END_DECLS
#endif /* PROTOBUF_C_custom_5fconfig_2eproto__INCLUDED */

View File

@ -0,0 +1,11 @@
# Protobuf files for defining custom config-data packet structures
This is an example proto file defining custom configuration related data packet structures, namely -
1. CustomConfigRequest - for sending configuration data consisting of various fields (Info and Version)
2. CustomConfigResponse - for receiving configuration status (fail/success)
Note : These proto files are not automatically compiled during the build process.
Run "make" (Optional) to generate the respective C and Python files. The generated C files are used by protocomm itself to create, delete and manipulate transaction packets. The generated Python files can be used by python based applications for implementing client side interface to protocomm layer.
Compilation requires protoc (Protobuf Compiler) and protoc-c (Protobuf C Compiler) installed. Since the generated files are to remain the same, as long as the proto files are not modified, therefore the generated files are already available under "protocomm/proto-c" and "protocomm/python" directories, and thus running make (and installing the Protobuf compilers) is optional.

View File

@ -0,0 +1,16 @@
syntax = "proto3";
enum CustomConfigStatus {
ConfigSuccess = 0;
ConfigFail = 1;
}
message CustomConfigRequest {
string info = 1;
int32 version = 2;
}
message CustomConfigResponse {
CustomConfigStatus status = 1;
int32 dummy = 2;
}

View File

@ -0,0 +1,7 @@
all: c_proto python_proto
c_proto: *.proto
@protoc-c --c_out=../proto-c/ *.proto
python_proto: *.proto
@protoc --python_out=../python/ *.proto

View File

@ -0,0 +1,150 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: custom_config.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='custom_config.proto',
package='',
syntax='proto3',
serialized_options=None,
serialized_pb=_b('\n\x13\x63ustom_config.proto\"4\n\x13\x43ustomConfigRequest\x12\x0c\n\x04info\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\x05\"J\n\x14\x43ustomConfigResponse\x12#\n\x06status\x18\x01 \x01(\x0e\x32\x13.CustomConfigStatus\x12\r\n\x05\x64ummy\x18\x02 \x01(\x05*7\n\x12\x43ustomConfigStatus\x12\x11\n\rConfigSuccess\x10\x00\x12\x0e\n\nConfigFail\x10\x01\x62\x06proto3')
)
_CUSTOMCONFIGSTATUS = _descriptor.EnumDescriptor(
name='CustomConfigStatus',
full_name='CustomConfigStatus',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='ConfigSuccess', index=0, number=0,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ConfigFail', index=1, number=1,
serialized_options=None,
type=None),
],
containing_type=None,
serialized_options=None,
serialized_start=153,
serialized_end=208,
)
_sym_db.RegisterEnumDescriptor(_CUSTOMCONFIGSTATUS)
CustomConfigStatus = enum_type_wrapper.EnumTypeWrapper(_CUSTOMCONFIGSTATUS)
ConfigSuccess = 0
ConfigFail = 1
_CUSTOMCONFIGREQUEST = _descriptor.Descriptor(
name='CustomConfigRequest',
full_name='CustomConfigRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='info', full_name='CustomConfigRequest.info', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='version', full_name='CustomConfigRequest.version', index=1,
number=2, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=23,
serialized_end=75,
)
_CUSTOMCONFIGRESPONSE = _descriptor.Descriptor(
name='CustomConfigResponse',
full_name='CustomConfigResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='status', full_name='CustomConfigResponse.status', index=0,
number=1, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='dummy', full_name='CustomConfigResponse.dummy', index=1,
number=2, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=77,
serialized_end=151,
)
_CUSTOMCONFIGRESPONSE.fields_by_name['status'].enum_type = _CUSTOMCONFIGSTATUS
DESCRIPTOR.message_types_by_name['CustomConfigRequest'] = _CUSTOMCONFIGREQUEST
DESCRIPTOR.message_types_by_name['CustomConfigResponse'] = _CUSTOMCONFIGRESPONSE
DESCRIPTOR.enum_types_by_name['CustomConfigStatus'] = _CUSTOMCONFIGSTATUS
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
CustomConfigRequest = _reflection.GeneratedProtocolMessageType('CustomConfigRequest', (_message.Message,), dict(
DESCRIPTOR = _CUSTOMCONFIGREQUEST,
__module__ = 'custom_config_pb2'
# @@protoc_insertion_point(class_scope:CustomConfigRequest)
))
_sym_db.RegisterMessage(CustomConfigRequest)
CustomConfigResponse = _reflection.GeneratedProtocolMessageType('CustomConfigResponse', (_message.Message,), dict(
DESCRIPTOR = _CUSTOMCONFIGRESPONSE,
__module__ = 'custom_config_pb2'
# @@protoc_insertion_point(class_scope:CustomConfigResponse)
))
_sym_db.RegisterMessage(CustomConfigResponse)
# @@protoc_insertion_point(module_scope)

View File

@ -0,0 +1,68 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <esp_log.h>
#include <string.h>
#include <esp_err.h>
#include <custom_provisioning/custom_config.h>
#include "custom_config.pb-c.h"
static const char *TAG = "custom_config";
int custom_prov_config_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, void *priv_data)
{
CustomConfigRequest *req;
CustomConfigResponse resp;
custom_prov_config_handler_t app_handler_custom_config = (custom_prov_config_handler_t) priv_data;
req = custom_config_request__unpack(NULL, inlen, inbuf);
if (!req) {
ESP_LOGE(TAG, "Unable to unpack config data");
return ESP_ERR_INVALID_ARG;
}
custom_config_response__init(&resp);
resp.status = CUSTOM_CONFIG_STATUS__ConfigFail;
if (app_handler_custom_config) {
custom_config_t config;
strlcpy(config.info, req->info, sizeof(config.info));
config.version = req->version;
esp_err_t err = app_handler_custom_config(&config);
resp.status = (err == ESP_OK) ? CUSTOM_CONFIG_STATUS__ConfigSuccess :
CUSTOM_CONFIG_STATUS__ConfigFail;
}
custom_config_request__free_unpacked(req, NULL);
resp.dummy = 47; // Set a non zero value of dummy
*outlen = custom_config_response__get_packed_size(&resp);
if (*outlen <= 0) {
ESP_LOGE(TAG, "Invalid encoding for response");
return ESP_FAIL;
}
*outbuf = (uint8_t *) malloc(*outlen);
if (*outbuf == NULL) {
ESP_LOGE(TAG, "System out of memory");
return ESP_ERR_NO_MEM;
}
custom_config_response__pack(&resp, *outbuf);
return ESP_OK;
}

View File

@ -0,0 +1,6 @@
set(COMPONENT_SRCS "app_main.c"
"app_prov.c"
"app_prov_handlers.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,52 @@
menu "Example Configuration"
config SOFTAP_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config SOFTAP_PASS
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config USE_SEC_1
bool
default n
prompt "Use Security Version 1"
help
Security version 1 used Curve25519 key exchange for establishing
secure session between device and client during provisioning
config USE_POP
bool
depends on USE_SEC_1
default n
prompt "Use proof-of-possession"
help
Proof-of-possession can be optionally used to prove that the device is indeed
in possession of the user who is provisioning the device. This proof-of-possession
is internally used to generate the shared secret through key exchange.
config POP
string "Proof-of-possession"
default "abcd1234"
depends on USE_POP
config PROTOCOMM_HTTPD_PORT
int "Protocomm HTTP Port"
default 80
help
Port on which to run Protocomm HTTP based provisioning service
config RESET_PROVISIONED
bool
default n
prompt "Reset provisioned status of the device"
help
This erases the NVS to reset provisioned status of the device on every reboot.
Provisioned status is determined by the WiFi STA configuration, saved on the NVS.
endmenu

View File

@ -0,0 +1,115 @@
/* SoftAP based Custom Provisioning Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event_loop.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <lwip/err.h>
#include <lwip/sys.h>
#include "app_prov.h"
static const char *TAG = "app";
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
/* Invoke Provisioning event handler first */
app_prov_event_handler(ctx, event);
switch(event->event_id) {
case SYSTEM_EVENT_AP_START:
ESP_LOGI(TAG, "SoftAP started");
break;
case SYSTEM_EVENT_AP_STOP:
ESP_LOGI(TAG, "SoftAP stopped");
break;
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
ESP_LOGI(TAG, "got ip:%s",
ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
break;
case SYSTEM_EVENT_AP_STACONNECTED:
ESP_LOGI(TAG, "station:"MACSTR" join, AID=%d",
MAC2STR(event->event_info.sta_connected.mac),
event->event_info.sta_connected.aid);
break;
case SYSTEM_EVENT_AP_STADISCONNECTED:
ESP_LOGI(TAG, "station:"MACSTR"leave, AID=%d",
MAC2STR(event->event_info.sta_disconnected.mac),
event->event_info.sta_disconnected.aid);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
break;
default:
break;
}
return ESP_OK;
}
static void wifi_init_sta()
{
/* Start wifi in station mode with credentials set during provisioning */
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_start() );
}
void app_main()
{
/* Security version */
int security = 0;
/* Proof of possession */
const protocomm_security_pop_t *pop = NULL;
#ifdef CONFIG_USE_SEC_1
security = 1;
#endif
/* Having proof of possession is optional */
#ifdef CONFIG_USE_POP
const static protocomm_security_pop_t app_pop = {
.data = (uint8_t *) CONFIG_POP,
.len = (sizeof(CONFIG_POP)-1)
};
pop = &app_pop;
#endif
/* Initialize networking stack */
tcpip_adapter_init();
/* Set our event handling */
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
/* Check if device is provisioned */
bool provisioned;
if (app_prov_is_provisioned(&provisioned) != ESP_OK) {
ESP_LOGE(TAG, "Error getting device provisioning state");
return;
}
if (provisioned == false) {
/* If not provisioned, start provisioning via soft AP */
ESP_LOGI(TAG, "Starting WiFi SoftAP provisioning");
app_prov_start_softap_provisioning(CONFIG_SOFTAP_SSID, CONFIG_SOFTAP_PASS,
security, pop);
} else {
/* Start WiFi station with credentials set during provisioning */
ESP_LOGI(TAG, "Starting WiFi station");
wifi_init_sta(NULL);
}
}

View File

@ -0,0 +1,410 @@
/* SoftAP based Custom Provisioning Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <esp_log.h>
#include <esp_err.h>
#include <esp_wifi.h>
#include <nvs_flash.h>
#include <nvs.h>
#include <protocomm.h>
#include <protocomm_httpd.h>
#include <protocomm_security0.h>
#include <protocomm_security1.h>
#include <wifi_provisioning/wifi_config.h>
#include <custom_provisioning/custom_config.h>
#include "app_prov.h"
static const char *TAG = "app_prov";
/* Handlers for provisioning endpoints */
extern wifi_prov_config_handlers_t wifi_prov_handlers;
extern custom_prov_config_handler_t custom_prov_handler;
/**
* @brief Data relevant to provisioning application
*/
struct app_prov_data {
protocomm_t *pc; /*!< Protocomm handler */
int security; /*!< Type of security to use with protocomm */
const protocomm_security_pop_t *pop; /*!< Pointer to proof of possession */
esp_timer_handle_t timer; /*!< Handle to timer */
/* State of WiFi Station */
wifi_prov_sta_state_t wifi_state;
/* Code for WiFi station disconnection (if disconnected) */
wifi_prov_sta_fail_reason_t wifi_disconnect_reason;
};
/* Pointer to provisioning application data */
static struct app_prov_data *g_prov;
static esp_err_t app_prov_start_service(void)
{
/* Create new protocomm instance */
g_prov->pc = protocomm_new();
if (g_prov->pc == NULL) {
ESP_LOGE(TAG, "Failed to create new protocomm instance");
return ESP_FAIL;
}
/* Config for protocomm_httpd_start() */
protocomm_httpd_config_t pc_config = {
.data = {
.config = PROTOCOMM_HTTPD_DEFAULT_CONFIG()
}
};
/* Start protocomm server on top of HTTP */
if (protocomm_httpd_start(g_prov->pc, &pc_config) != ESP_OK) {
ESP_LOGE(TAG, "Failed to start protocomm HTTP server");
return ESP_FAIL;
}
/* Set protocomm version verification endpoint for protocol */
protocomm_set_version(g_prov->pc, "proto-ver", "V0.1");
/* Set protocomm security type for endpoint */
if (g_prov->security == 0) {
protocomm_set_security(g_prov->pc, "prov-session", &protocomm_security0, NULL);
} else if (g_prov->security == 1) {
protocomm_set_security(g_prov->pc, "prov-session", &protocomm_security1, g_prov->pop);
}
/* Add endpoint for provisioning to set WiFi STA config */
if (protocomm_add_endpoint(g_prov->pc, "prov-config",
wifi_prov_config_data_handler,
(void *) &wifi_prov_handlers) != ESP_OK) {
ESP_LOGE(TAG, "Failed to set WiFi provisioning endpoint");
protocomm_httpd_stop(g_prov->pc);
return ESP_FAIL;
}
/* Add endpoint for provisioning to set custom config */
if (protocomm_add_endpoint(g_prov->pc, "custom-config",
custom_prov_config_data_handler,
(void *) custom_prov_handler) != ESP_OK) {
ESP_LOGE(TAG, "Failed to set custom provisioning endpoint");
protocomm_httpd_stop(g_prov->pc);
return ESP_FAIL;
}
return ESP_OK;
}
static void app_prov_stop_service(void)
{
/* Remove provisioning endpoint for custom config */
protocomm_remove_endpoint(g_prov->pc, "custom-config");
/* Remove provisioning endpoint for WiFi STA config */
protocomm_remove_endpoint(g_prov->pc, "prov-config");
/* Unset provisioning security */
protocomm_unset_security(g_prov->pc, "prov-session");
/* Unset provisioning version endpoint */
protocomm_unset_version(g_prov->pc, "proto-ver");
/* Stop protocomm server */
protocomm_httpd_stop(g_prov->pc);
/* Delete protocomm instance */
protocomm_delete(g_prov->pc);
}
/* Task spawned by timer callback */
static void stop_prov_task(void * arg)
{
ESP_LOGI(TAG, "Stopping provisioning");
app_prov_stop_service();
esp_wifi_set_mode(WIFI_MODE_STA);
/* Timer not needed anymore */
esp_timer_handle_t timer = g_prov->timer;
esp_timer_delete(timer);
g_prov->timer = NULL;
/* Free provisioning process data */
free(g_prov);
g_prov = NULL;
ESP_LOGI(TAG, "Provisioning stopped");
vTaskDelete(NULL);
}
/* Callback to be invoked by timer */
static void _stop_prov_cb(void * arg)
{
xTaskCreate(&stop_prov_task, "stop_prov", 2048, NULL, tskIDLE_PRIORITY, NULL);
}
/* Event handler for starting/stopping provisioning.
* To be called from within the context of the main
* event handler.
*/
esp_err_t app_prov_event_handler(void *ctx, system_event_t *event)
{
/* For accessing reason codes in case of disconnection */
system_event_info_t *info = &event->event_info;
/* If pointer to provisioning application data is NULL
* then provisioning is not running, therefore return without
* error */
if (!g_prov) {
return ESP_OK;
}
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
ESP_LOGI(TAG, "STA Start");
/* Once configuration is received by protocomm server,
* device is restarted as both AP and Station.
* Once station starts, wait for connection to
* establish with configured host SSID and password */
g_prov->wifi_state = WIFI_PROV_STA_CONNECTING;
break;
case SYSTEM_EVENT_STA_GOT_IP:
ESP_LOGI(TAG, "STA Got IP");
/* Station got IP. That means configuration is successful.
* Schedule timer to stop provisioning app after 30 seconds. */
g_prov->wifi_state = WIFI_PROV_STA_CONNECTED;
if (g_prov && g_prov->timer) {
/* Note that, after restarting the WiFi in Station + AP mode, the
* user gets disconnected from the AP for a while. But at the same
* time, the user app requests for status update from the device
* to verify that the provisioning was successful. Therefore, the
* turning off of the AP must be delayed long enough for the user
* to reconnect and get STA connection status from the device.
* Otherwise, the AP will be turned off before the user can
* reconnect and thus the user app will see connection timed out,
* signaling a failure in provisioning. */
esp_timer_start_once(g_prov->timer, 30000*1000U);
}
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
ESP_LOGE(TAG, "STA Disconnected");
/* Station couldn't connect to configured host SSID */
g_prov->wifi_state = WIFI_PROV_STA_DISCONNECTED;
ESP_LOGE(TAG, "Disconnect reason : %d", info->disconnected.reason);
/* Set code corresponding to the reason for disconnection */
switch (info->disconnected.reason) {
case WIFI_REASON_AUTH_EXPIRE:
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
case WIFI_REASON_BEACON_TIMEOUT:
case WIFI_REASON_AUTH_FAIL:
case WIFI_REASON_ASSOC_FAIL:
case WIFI_REASON_HANDSHAKE_TIMEOUT:
ESP_LOGI(TAG, "STA Auth Error");
g_prov->wifi_disconnect_reason = WIFI_PROV_STA_AUTH_ERROR;
break;
case WIFI_REASON_NO_AP_FOUND:
ESP_LOGI(TAG, "STA AP Not found");
g_prov->wifi_disconnect_reason = WIFI_PROV_STA_AP_NOT_FOUND;
break;
default:
/* If none of the expected reasons,
* retry connecting to host SSID */
g_prov->wifi_state = WIFI_PROV_STA_CONNECTING;
esp_wifi_connect();
}
break;
default:
break;
}
return ESP_OK;
}
esp_err_t app_prov_get_wifi_state(wifi_prov_sta_state_t* state)
{
if (g_prov == NULL || state == NULL) {
return ESP_FAIL;
}
*state = g_prov->wifi_state;
return ESP_OK;
}
esp_err_t app_prov_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t* reason)
{
if (g_prov == NULL || reason == NULL) {
return ESP_FAIL;
}
if (g_prov->wifi_state != WIFI_PROV_STA_DISCONNECTED) {
return ESP_FAIL;
}
*reason = g_prov->wifi_disconnect_reason;
return ESP_OK;
}
esp_err_t app_prov_is_provisioned(bool *provisioned)
{
#ifdef CONFIG_RESET_PROVISIONED
nvs_flash_erase();
#endif
if (nvs_flash_init() != ESP_OK) {
ESP_LOGE(TAG, "Failed to init NVS");
return ESP_FAIL;
}
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
if (esp_wifi_init(&cfg) != ESP_OK) {
ESP_LOGE(TAG, "Failed to init wifi");
return ESP_FAIL;
}
/* Get WiFi Station configuration */
wifi_config_t wifi_cfg;
if (esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_cfg) != ESP_OK) {
*provisioned = false;
return ESP_FAIL;
}
if (strlen((const char*) wifi_cfg.sta.ssid)) {
*provisioned = true;
ESP_LOGI(TAG, "Found ssid %s", (const char*) wifi_cfg.sta.ssid);
ESP_LOGI(TAG, "Found password %s", (const char*) wifi_cfg.sta.password);
}
return ESP_OK;
}
esp_err_t app_prov_configure_sta(wifi_config_t *wifi_cfg)
{
/* Configure WiFi as both AP and Station */
if (esp_wifi_set_mode(WIFI_MODE_APSTA) != ESP_OK) {
ESP_LOGE(TAG, "Failed to set WiFi mode");
return ESP_FAIL;
}
/* Configure WiFi station with host credentials
* provided during provisioning */
if (esp_wifi_set_config(ESP_IF_WIFI_STA, wifi_cfg) != ESP_OK) {
ESP_LOGE(TAG, "Failed to set WiFi configuration");
return ESP_FAIL;
}
/* Restart WiFi */
if (esp_wifi_start() != ESP_OK) {
ESP_LOGE(TAG, "Failed to restart WiFi");
return ESP_FAIL;
}
/* Connect to AP */
if (esp_wifi_connect() != ESP_OK) {
ESP_LOGE(TAG, "Failed to connect WiFi");
return ESP_FAIL;
}
if (g_prov) {
/* Reset wifi station state for provisioning app */
g_prov->wifi_state = WIFI_PROV_STA_CONNECTING;
}
return ESP_OK;
}
static esp_err_t start_wifi_ap(const char *ssid, const char *pass)
{
/* Initialize WiFi with default configuration */
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_err_t err = esp_wifi_init(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to init WiFi : %d", err);
return err;
}
/* Build WiFi configuration for AP mode */
wifi_config_t wifi_config = {
.ap = {
.max_connection = 5,
},
};
strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
wifi_config.ap.ssid_len = strlen(ssid);
if (strlen(pass) == 0) {
memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password));
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
} else {
strncpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
}
/* Start WiFi in AP mode with configuration built above */
err = esp_wifi_set_mode(WIFI_MODE_AP);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set WiFi mode : %d", err);
return err;
}
err = esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set WiFi config : %d", err);
return err;
}
err = esp_wifi_start();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to start WiFi : %d", err);
return err;
}
return ESP_OK;
}
esp_err_t app_prov_start_softap_provisioning(const char *ssid, const char *pass,
int security, const protocomm_security_pop_t *pop)
{
/* If provisioning app data present,
* means provisioning app is already running */
if (g_prov) {
ESP_LOGI(TAG, "Invalid provisioning state");
return ESP_FAIL;
}
/* Allocate memory for provisioning app data */
g_prov = (struct app_prov_data *) calloc(1, sizeof(struct app_prov_data));
if (!g_prov) {
ESP_LOGI(TAG, "Unable to allocate prov data");
return ESP_ERR_NO_MEM;
}
/* Initialise app data */
g_prov->pop = pop;
g_prov->security = security;
/* Create timer object as a member of app data */
esp_timer_create_args_t timer_conf = {
.callback = _stop_prov_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_TASK,
.name = "stop_softap_tm"
};
esp_err_t err = esp_timer_create(&timer_conf, &g_prov->timer);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to create timer");
return err;
}
/* Start WiFi softAP with specified ssid and password */
err = start_wifi_ap(ssid, pass);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to start WiFi AP");
return err;
}
/* Start provisioning service through HTTP */
err = app_prov_start_service();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to start provisioning app");
return err;
}
ESP_LOGI(TAG, "SoftAP Provisioning started with SSID %s, Password %s", ssid, pass);
return ESP_OK;
}

View File

@ -0,0 +1,103 @@
/* SoftAP based Custom Provisioning Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include <esp_event_loop.h>
#include <protocomm_security.h>
#include <wifi_provisioning/wifi_config.h>
#include <custom_provisioning/custom_config.h>
/**
* @brief Get state of WiFi Station during provisioning
*
* @note WiFi is initially configured as AP, when
* provisioning starts. After provisioning data
* is provided by user, the WiFi is reconfigured
* to run as both AP and Station.
*
* @param[out] state Pointer to wifi_prov_sta_state_t variable to be filled
*
* @return
* - ESP_OK : Successfully retrieved wifi state
* - ESP_FAIL : Provisioning app not running
*/
esp_err_t app_prov_get_wifi_state(wifi_prov_sta_state_t* state);
/**
* @brief Get reason code in case of WiFi station
* disconnection during provisioning
*
* @param[out] reason Pointer to wifi_prov_sta_fail_reason_t variable to be filled
*
* @return
* - ESP_OK : Successfully retrieved wifi disconnect reason
* - ESP_FAIL : Provisioning app not running
*/
esp_err_t app_prov_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t* reason);
/**
* @brief Event handler for provisioning app
*
* This is called from the main event handler and controls the
* provisioning application, depeding on WiFi events
*
* @param[in] ctx Event context data
* @param[in] event Event info
*
* @return
* - ESP_OK : Event handled successfully
* - ESP_FAIL : Failed to start server on event AP start
*/
esp_err_t app_prov_event_handler(void *ctx, system_event_t *event);
/**
* @brief Checks if device is provisioned
* *
* @param[out] provisioned True if provisioned, else false
*
* @return
* - ESP_OK : Retrieved provision state successfully
* - ESP_FAIL : Failed to retrieve provision state
*/
esp_err_t app_prov_is_provisioned(bool *provisioned);
/**
* @brief Runs WiFi as both AP and Station
*
* Configures the WiFi station mode to connect to the
* SSID and password specified in config structure,
* and restarts WiFi to run as both AP and station
*
* @param[in] wifi_cfg Pointer to WiFi cofiguration structure
*
* @return
* - ESP_OK : WiFi configured and restarted successfully
* - ESP_FAIL : Failed to set configuration
*/
esp_err_t app_prov_configure_sta(wifi_config_t *wifi_cfg);
/**
* @brief Start provisioning via softAP
*
* Starts the WiFi softAP with specified ssid and pass, provisioning
* security mode and proof of possession (if any).
*
* @param[in] ssid SSID for SoftAP
* @param[in] pass Password for SoftAP
* @param[in] security Security mode
* @param[in] pop Pointer to proof of possession (NULL if not present)
*
* @return
* - ESP_OK : Provisioning started successfully
* - ESP_FAIL : Failed to start
*/
esp_err_t app_prov_start_softap_provisioning(const char *ssid, const char *pass,
int security, const protocomm_security_pop_t *pop);

View File

@ -0,0 +1,140 @@
/* SoftAP based Custom Provisioning Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/* This file is mostly a boiler-plate code that applications can use without much change */
#include <stdio.h>
#include <string.h>
#include <esp_err.h>
#include <esp_log.h>
#include <esp_wifi.h>
#include <tcpip_adapter.h>
#include <wifi_provisioning/wifi_config.h>
#include <custom_provisioning/custom_config.h>
#include "app_prov.h"
static const char* TAG = "app_prov_handler";
/* Provide definition of wifi_prov_ctx_t */
struct wifi_prov_ctx {
wifi_config_t wifi_cfg;
};
static wifi_config_t *get_config(wifi_prov_ctx_t **ctx)
{
return (*ctx ? &(*ctx)->wifi_cfg : NULL);
}
static wifi_config_t *new_config(wifi_prov_ctx_t **ctx)
{
free(*ctx);
(*ctx) = (wifi_prov_ctx_t *) calloc(1, sizeof(wifi_prov_ctx_t));
return get_config(ctx);
}
static void free_config(wifi_prov_ctx_t **ctx)
{
free(*ctx);
*ctx = NULL;
}
/****************** Handler for Custom Configuration *******************/
static esp_err_t custom_config_handler(const custom_config_t *config)
{
ESP_LOGI(TAG, "Custom config received :\n\tInfo : %s\n\tVersion : %d",
config->info, config->version);
return ESP_OK;
}
custom_prov_config_handler_t custom_prov_handler = custom_config_handler;
/****************** Handlers for Wi-Fi Configuration *******************/
static esp_err_t get_status_handler(wifi_prov_config_get_data_t *resp_data, wifi_prov_ctx_t **ctx)
{
/* Initialize to zero */
memset(resp_data, 0, sizeof(wifi_prov_config_get_data_t));
if (app_prov_get_wifi_state(&resp_data->wifi_state) != ESP_OK) {
ESP_LOGW(TAG, "Prov app not running");
return ESP_FAIL;
}
if (resp_data->wifi_state == WIFI_PROV_STA_CONNECTED) {
ESP_LOGI(TAG, "Connected state");
/* IP Addr assigned to STA */
tcpip_adapter_ip_info_t ip_info;
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
char *ip_addr = ip4addr_ntoa(&ip_info.ip);
strcpy(resp_data->conn_info.ip_addr, ip_addr);
/* AP information to which STA is connected */
wifi_ap_record_t ap_info;
esp_wifi_sta_get_ap_info(&ap_info);
memcpy(resp_data->conn_info.bssid, (char *)ap_info.bssid, sizeof(ap_info.bssid));
memcpy(resp_data->conn_info.ssid, (char *)ap_info.ssid, sizeof(ap_info.ssid));
resp_data->conn_info.channel = ap_info.primary;
resp_data->conn_info.auth_mode = ap_info.authmode;
} else if (resp_data->wifi_state == WIFI_PROV_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Disconnected state");
/* If disconnected, convey reason */
app_prov_get_wifi_disconnect_reason(&resp_data->fail_reason);
} else {
ESP_LOGI(TAG, "Connecting state");
}
return ESP_OK;
}
static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data, wifi_prov_ctx_t **ctx)
{
wifi_config_t *wifi_cfg = get_config(ctx);
if (wifi_cfg) {
free_config(ctx);
}
wifi_cfg = new_config(ctx);
if (!wifi_cfg) {
ESP_LOGE(TAG, "Unable to alloc wifi config");
return ESP_FAIL;
}
ESP_LOGI(TAG, "WiFi Credentials Received : \n\tssid %s \n\tpassword %s",
req_data->ssid, req_data->password);
memcpy((char *) wifi_cfg->sta.ssid, req_data->ssid,
strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid)));
memcpy((char *) wifi_cfg->sta.password, req_data->password,
strnlen(req_data->password, sizeof(wifi_cfg->sta.password)));
return ESP_OK;
}
static esp_err_t apply_config_handler(wifi_prov_ctx_t **ctx)
{
wifi_config_t *wifi_cfg = get_config(ctx);
if (!wifi_cfg) {
ESP_LOGE(TAG, "WiFi config not set");
return ESP_FAIL;
}
app_prov_configure_sta(wifi_cfg);
ESP_LOGI(TAG, "WiFi Credentials Applied");
free_config(ctx);
return ESP_OK;
}
wifi_prov_config_handlers_t wifi_prov_handlers = {
.get_status_handler = get_status_handler,
.set_config_handler = set_config_handler,
.apply_config_handler = apply_config_handler,
.ctx = NULL
};

View File

@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#