Files
2022-11-29 11:53:20 -05:00

279 lines
9.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
// This is the description of the Improv Wi-Fi protocol using a serial port.
// The device needs to be connected to the computer via a USB/UART serial port.
// The protocol has two actors: the Improv service running on the gadget and the Improv client.
// The Improv service will receive Wi-Fi credentials from the client via the serial connection.
// The Improv client asks for the current state and sends the Wi-Fi credentials.
// =========================================================================================
// Packet format
// ======================================
// Byte Purpose
// ---- -------------------------------
// 1-6 Header will equal IMPROV
// 7 Version CURRENT VERSION = 1
// 8 Type (see below)
// 9 Length
// 10...X Data
// X + 10 Checksum
// =========================================================================================
// Packet types
// ======================================
// Type Description Direction
// ---- ------------ -----------------
// 0x01 Current state Device to Client
// 0x02 Error state Device to Client
// 0x03 RPC Command Device to Client
// 0x04 RPC Result Client to Device
typedef enum {
IMPROV_PACKET_TYPE_CURRENT_STATE = 0x01,
IMPROV_PACKET_TYPE_ERROR_STATE = 0x02,
IMPROV_PACKET_TYPE_RPC = 0x03,
IMPROV_PACKET_TYPE_RPC_RESPONSE = 0x04
} ImprovSerialType_t;
// =========================================================================================
// Packet: Current State
// ======================================
// Type: 0x01
// Direction: Device to Client
// --------------------------------------
// The data of this packet is a single byte and contains the current status of the provisioning
// service. It is to be written to any listening clients for instant feedback.
// Byte Description
// 1 current state
// The current state can be the following values:
// Value State Purpose
// ----- ------------------ -----------------------------------------
// 0x02 Ready (Authorized) Ready to accept credentials.
// 0x03 Provisioning Credentials received, attempt to connect.
// 0x04 Provisioned Connection successful.
typedef enum {
IMPROV_STATE_READY_AUTHORIZED = 0x02,
IMPROV_STATE_PROVISIONING = 0x03,
IMPROV_STATE_PROVISIONED = 0x04,
} ImprovState_t;
// =========================================================================================
// Packet: Error state
// ======================================
// Type: 0x02
// Direction: Device to client
// --------------------------------------
// The data of this packet is a single byte and contains the current status of the
// provisioning service. Whenever it changes the device needs to sent it to any listening
// clients for instant feedback.
// Byte Description
// 1 error state
// Error state can be the following values:
// Value State Purpose
// ----- ------------------ -----------------------------------------
// 0x00 No error This shows there is no current error state.
// 0x01 Invalid RPC packet RPC packet was malformed/invalid.
// 0x02 Unknown RPC command The command sent is unknown.
// 0x03 Unable to connect The credentials have been received and an attempt to connect
// to the network has failed.
// 0xFF Unknown Error
typedef enum {
IMPROV_ERROR_NONE = 0x00,
IMPROV_ERROR_INVALID_RPC = 0x01,
IMPROV_ERROR_UNKNOWN_RPC = 0x02,
IMPROV_ERROR_UNABLE_TO_CONNECT = 0x03,
IMPROV_ERROR_NOT_AUTHORIZED = 0x04,
IMPROV_ERROR_UNKNOWN = 0xFF,
} ImprovError_t;
// =========================================================================================
// Packet: RPC Command
// Type: 0x03
// Direction: Client to device
// --------------------------------------
// This packet type is used for the client to send commands to the device. When an RPC
// command is sent, the device should sent an update to the client to set the error state to
// 0 (no error). The response will either be an RPC result packet or an error state update.
// Byte Description
// ----- ---------------------
// 1 Command (see below)
// 2 Data length
// 3...X Data
typedef enum {
IMPROV_CMD_UNKNOWN = 0x00,
IMPROV_CMD_WIFI_SETTINGS = 0x01,
IMPROV_CMD_GET_CURRENT_STATE = 0x02,
IMPROV_CMD_GET_DEVICE_INFO = 0x03,
IMPROV_CMD_GET_WIFI_NETWORKS = 0x04,
IMPROV_CMD_BAD_CHECKSUM = 0xFF,
} ImprovCommand_t;
// ======================================
// RPC Command: Send Wi-Fi settings
// Submit Wi-Fi credentials to the Improv Service to attempt to connect to.
// Type: 0x03
// Command ID: 0x01
// Byte Description
// ----- ----------------
// 1 command (0x01)
// 2 data length
// 3 ssid length
// 4...X ssid bytes
// X password length
// X...Y password bytes
// Example: SSID = MyWirelessAP, Password = mysecurepassword
// 01 1E 0C {MyWirelessAP} 10 {mysecurepassword}
// This command will generate an RPC result. The first entry in the list is an URL to
// redirect the user to.
// If there is no URL, omit the entry or add an empty string.
// ======================================
// RPC Command: Request current state
// Sends a request for the device to send the current state of improv to the client.
// Type: 0x03
// Command ID: 0x02
// Byte Description
// ----- ----------------
// 1 command (0x02)
// 2 data length (0)
// This command will trigger at least one packet, the Current State (see above) and if
// already provisioned,
// the same response you would get if device provisioning was successful (see below).
// ======================================
// RPC Command: Request device information
// Sends a request for the device to send information about itself.
// Type: 0x03
// Command ID: 0x03
// Byte Description
// ----- ----------------
// 1 command (0x02)
// 2 data length (0)
// This command will trigger one packet, the Device Information formatted as a RPC result.
// This result will contain at least 4 strings.
// Order of strings: Firmware name, firmware version, hardware chip/variant, device name.
// Example: ESPHome, 2021.11.0, ESP32-C3, Temperature Monitor.
// ======================================
// RPC Command: Request scanned Wi-Fi networks
// Sends a request for the device to send the Wi-Fi networks it sees.
// Type: 0x03
// Command ID: 0x04
// Byte Description
// ----- ----------------
// 1 command (0x02)
// 2 data length (0)
// This command will trigger at least one RPC Response. Each response will contain at
// least 3 strings.
// Order of strings: Wi-Fi SSID, RSSI, Auth required.
// Example: MyWirelessNetwork, -60, YES.
// The final response (or the first if no networks are found) will have 0 strings in the body.
// =========================================================================================
// Packet: RPC Result
// ======================================
// Type: 0x04
// Direction: Device to client
// --------------------------------------
// This packet type contains the response to an RPC command. Results are returned as a list
// of strings. An empty list is allowed.
// Byte Description
// ----- ----------------
// 1 Command being responded to (see above)
// 2 Data length
// 3 Length of string 1
// 4...X String 1
// X Length of string 2
// X...Y String 2
// ... etc
static const uint8_t CAPABILITY_IDENTIFY = 0x01;
static const uint8_t IMPROV_SERIAL_VERSION = 1;
#ifndef FREE_AND_NULL
#define FREE_AND_NULL(x) if(x) { free(x); x=NULL; }
#endif
#ifndef ENUM_TO_STRING
#define ENUM_TO_STRING(g) \
case g: \
return STR(g); \
break;
#endif
typedef struct {
ImprovCommand_t command;
char * ssid;
char * password;
} ImprovCommandStruct_t;
typedef struct {
char * ssid;
char * rssi;
char * auth_req; // YES/NO
} ImprovAPListStruct_t;
#define IMPROV_AP_STRUCT_NUM_STR 3
typedef struct {
char * firmware_name;
char * firmware_version;
char * hardware_chip_variant;
char * device_name;
char * nullptr;
} ImprovDeviceInfoStruct_t;
#define IMPROV_DEVICE_INFO_NUM_STRINGS 4
typedef bool (*improv_command_callback_t)(ImprovCommandStruct_t *cmd);
typedef void (*on_error_callback_t)(ImprovError_t error);
typedef bool (*improv_send_callback_t)(uint8_t * buffer, size_t length);
typedef struct {
int index ;
improv_command_callback_t callback ;
} callback_table_t;
void improv_parse_data(ImprovCommandStruct_t * improv_command, const uint8_t *data, size_t length, bool check_checksum) ;
bool improv_parse_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,improv_command_callback_t callback, on_error_callback_t on_error);
bool parse_improv_serial_line( const uint8_t *buffer);
void improv_set_send_callback(improv_send_callback_t callback );
bool improv_set_callback(ImprovCommand_t command, improv_command_callback_t callback );
bool improv_wifi_list_allocate(size_t num_entries);
bool improv_wifi_list_add(const char * ssid, int8_t rssi, bool auth_req );
bool improv_wifi_list_send( );
size_t improv_wifi_get_wifi_list_count();
bool improv_send_device_info( const char * firmware_name, const char * firmware_version, const char * hardware_chip_variant, const char * device_name);
uint8_t * improv_build_response(ImprovSerialType_t command, const char * datum, size_t len, size_t * out_len);
uint8_t * improv_build_rpc_response(ImprovCommand_t command, const char ** results, size_t num_strings, size_t * out_len);
bool improv_send_current_state(ImprovState_t state);
bool improv_send_error(ImprovError_t error);
const char * improv_get_error_desc(ImprovError_t error);
const char * improv_get_command_desc(ImprovCommand_t command);
bool improv_send_device_url( ImprovCommand_t from_command, const char * url);
// Improv Wi-Fi Contact GitHub
// Improv is an initiative by ESPHome & Home Assistant.
// Development funded by Nabu Casa.