mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
971 lines
31 KiB
C
971 lines
31 KiB
C
// Because the ESP8266 JS wrapper is assured to be running on an ESP8266 we
|
||
// can assume that inclusion of ESP8266 headers will be acceptable.
|
||
#include <c_types.h>
|
||
#include <user_interface.h>
|
||
#include <mem.h>
|
||
#include <osapi.h>
|
||
#include <ping.h>
|
||
#include <espconn.h>
|
||
#include <espmissingincludes.h>
|
||
#include <driver/uart.h>
|
||
|
||
#define ESP8266_ON_ACCESS_POINTS "#accessPoints"
|
||
|
||
#define _GCC_WRAP_STDINT_H
|
||
typedef long long int64_t;
|
||
|
||
#include "jswrap_esp8266.h"
|
||
#include "jsinteractive.h" // Pull inn the jsiConsolePrint function
|
||
#include "network.h"
|
||
#include "network_esp8266.h"
|
||
#include "jswrap_net.h"
|
||
|
||
// Forward declaration of functions.
|
||
static void scanCB(void *arg, STATUS status);
|
||
static void wifiEventHandler(System_Event_t *event);
|
||
static void ipAddrToString(struct ip_addr addr, char *string);
|
||
static char *nullTerminateString(char *target, char *source, int sourceLength);
|
||
static void setupJsNetwork();
|
||
static void pingRecvCB();
|
||
|
||
static JsVar *jsScanCallback;
|
||
static JsVar *jsWiFiEventCallback;
|
||
|
||
// A callback function to be invoked when we have an IP address.
|
||
static JsVar *jsGotIpCallback;
|
||
|
||
static JsVar *jsPingCallback;
|
||
|
||
// Global data structure for ping request
|
||
static struct ping_option pingOpt;
|
||
|
||
// Let's define the JavaScript class that will contain our `world()` method. We'll call it `Hello`
|
||
/*JSON{
|
||
"type" : "class",
|
||
"class" : "ESP8266WiFi"
|
||
}*/
|
||
|
||
|
||
/**
|
||
* \brief Connect the station to an access point.
|
||
* - `ssid` - The network id of the access point.
|
||
* - `password` - The password to use to connect to the access point.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "connect",
|
||
"generate" : "jswrap_ESP8266WiFi_connect",
|
||
"params" : [
|
||
["ssid","JsVar","The network SSID"],
|
||
["password","JsVar","The password to the access point"],
|
||
["gotIpCallback", "JsVar", "An optional callback invoked when we have an IP"]
|
||
]
|
||
}*/
|
||
void jswrap_ESP8266WiFi_connect(
|
||
JsVar *jsv_ssid, //!< The SSID of the access point to connect.
|
||
JsVar *jsv_password, //!< The password for the access point.
|
||
JsVar *gotIpCallback //!< The Callback function to be called when we are connected.
|
||
) {
|
||
os_printf("> jswrap_ESP8266WiFi_connect\n");
|
||
|
||
// Check that the ssid and password values aren't obviously in error.
|
||
if (jsv_ssid == NULL || !jsvIsString(jsv_ssid)) {
|
||
jsExceptionHere(JSET_ERROR, "No SSID.");
|
||
return;
|
||
}
|
||
if (jsv_password == NULL || !jsvIsString(jsv_password)) {
|
||
jsExceptionHere(JSET_ERROR, "No password.");
|
||
return;
|
||
}
|
||
|
||
// Check that if a callback function was supplied that we actually have a callback function.
|
||
if (gotIpCallback != NULL && !jsvIsUndefined(gotIpCallback) && !jsvIsFunction(gotIpCallback)) {
|
||
gotIpCallback = NULL;
|
||
jsExceptionHere(JSET_ERROR, "A callback function was supplied that is not a function.");
|
||
return;
|
||
}
|
||
if (jsvIsUndefined(gotIpCallback) || jsvIsNull(gotIpCallback)) {
|
||
gotIpCallback = NULL;
|
||
}
|
||
|
||
// Set the global which is the gotIP callback
|
||
if (jsGotIpCallback != NULL) {
|
||
jsvUnLock(jsGotIpCallback);
|
||
jsGotIpCallback = NULL;
|
||
}
|
||
|
||
// What does this do?
|
||
if (gotIpCallback != NULL) {
|
||
jsGotIpCallback = jsvLockAgainSafe(gotIpCallback);
|
||
}
|
||
|
||
// Create strings from the JsVars for the ESP8266 API calls.
|
||
char ssid[33];
|
||
int len = jsvGetString(jsv_ssid, ssid, sizeof(ssid)-1);
|
||
ssid[len]='\0';
|
||
char password[65];
|
||
len = jsvGetString(jsv_password, password, sizeof(password)-1);
|
||
password[len]='\0';
|
||
|
||
os_printf("> - ssid=%s, password=%s\n", ssid, password);
|
||
// Set the WiFi mode of the ESP8266
|
||
wifi_set_opmode_current(STATION_MODE);
|
||
|
||
struct station_config stationConfig;
|
||
memset(&stationConfig, 0, sizeof(stationConfig));
|
||
os_strncpy((char *)stationConfig.ssid, ssid, 32);
|
||
if (password != NULL) {
|
||
os_strncpy((char *)stationConfig.password, password, 64);
|
||
} else {
|
||
os_strcpy((char *)stationConfig.password, "");
|
||
}
|
||
|
||
// Set the WiFi configuration
|
||
wifi_station_set_config(&stationConfig);
|
||
|
||
// Register the event handler for callbacks from ESP8266
|
||
wifi_set_event_handler_cb(wifiEventHandler);
|
||
|
||
wifi_station_connect();
|
||
os_printf("< jswrap_ESP8266WiFi_connect\n");
|
||
} // End of jswrap_ESP8266WiFi_connect
|
||
|
||
|
||
/**
|
||
* \brief Become an access point.
|
||
* When we call this function we are instructing the ESP8266 to set itself up as an
|
||
* access point to allow other WiFi stations to connect to it. In order to be an access
|
||
* point, the ESP8266 needs to know the SSID it should use as well as the password used
|
||
* to allow clients to connect.
|
||
*
|
||
* Parameters:
|
||
* - `jsv_ssid` - The network identity that the access point will advertize itself as.
|
||
* - `jsv_password` - The password a station will need to connect to the
|
||
* access point.
|
||
*
|
||
* Notes:
|
||
* - How about if the password is not supplied, NULL or empty then we set ourselves
|
||
* up using an Open authentication mechanism?
|
||
* - Add support for hidden SSIDs.
|
||
*
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "beAccessPoint",
|
||
"generate" : "jswrap_ESP8266WiFi_beAccessPoint",
|
||
"params" : [
|
||
["jsv_ssid","JsVar","The network SSID"],
|
||
["jsv_password","JsVar","The password to allow stations to connect to the access point"]
|
||
]
|
||
}*/
|
||
void jswrap_ESP8266WiFi_beAccessPoint(
|
||
JsVar *jsv_ssid, //!< The network identity that the access point will advertize itself as.
|
||
JsVar *jsv_password //!< The password a station will need to connect to the access point.
|
||
) {
|
||
// Validate that the SSID and password are somewhat useful.
|
||
if (jsv_ssid == NULL || !jsvIsString(jsv_ssid)) {
|
||
jsExceptionHere(JSET_ERROR, "No SSID.");
|
||
return;
|
||
}
|
||
if (jsv_password == NULL || !jsvIsString(jsv_password)) {
|
||
jsExceptionHere(JSET_ERROR, "No password.");
|
||
return;
|
||
}
|
||
|
||
// Create strings from the JsVars for the ESP8266 API calls.
|
||
char ssid[33];
|
||
int len = jsvGetString(jsv_ssid, ssid, sizeof(ssid)-1);
|
||
ssid[len]='\0';
|
||
char password[65];
|
||
len = jsvGetString(jsv_password, password, sizeof(password)-1);
|
||
password[len]='\0';
|
||
|
||
// Define that we are in Soft AP mode.
|
||
wifi_set_opmode_current(SOFTAP_MODE);
|
||
|
||
// Build our SoftAP configuration details
|
||
struct softap_config softApConfig;
|
||
memset(&softApConfig, 0, sizeof(softApConfig));
|
||
|
||
os_strcpy((char *)softApConfig.ssid, ssid);
|
||
os_strcpy((char *)softApConfig.password, password);
|
||
softApConfig.ssid_len = 0; // Null terminated SSID
|
||
softApConfig.authmode = AUTH_WPA2_PSK;
|
||
softApConfig.ssid_hidden = 0; // Not hidden.
|
||
softApConfig.max_connection = 4; // Maximum number of connections.
|
||
|
||
// Set the WiFi configuration.
|
||
int rc = wifi_softap_set_config_current(&softApConfig);
|
||
if (rc != 1) {
|
||
jsExceptionHere(JSET_ERROR, "Error setting ESP8266 softap config.");
|
||
}
|
||
} // End of jswrap_ESP8266WiFi_beAccessPoint
|
||
|
||
|
||
/**
|
||
* \brief Determine the list of access points available to us.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getAccessPoints",
|
||
"generate" : "jswrap_ESP8266WiFi_getAccessPoints",
|
||
"params" : [
|
||
["callback","JsVar","Function to call back when access points retrieved."]
|
||
]
|
||
}*/
|
||
void jswrap_ESP8266WiFi_getAccessPoints(
|
||
JsVar *callback //!< Function to call back when access points retrieved.
|
||
) {
|
||
os_printf("> ESP8266WiFi_getAccessPoints\n");
|
||
if (callback == NULL || !jsvIsFunction(callback)) {
|
||
jsExceptionHere(JSET_ERROR, "No callback.");
|
||
return;
|
||
}
|
||
|
||
// Save the callback for the scan in the global variable called jsScanCallback.
|
||
jsScanCallback = jsvLockAgainSafe(callback);
|
||
|
||
// Ask the ESP8266 to perform a network scan after first entering
|
||
// station mode. The network scan will eventually result in a callback
|
||
// being executed (scanCB) which will contain the results.
|
||
|
||
// Ensure we are in station mode
|
||
wifi_set_opmode_current(STATION_MODE);
|
||
|
||
// Request a scan of the network calling "scanCB" on completion
|
||
wifi_station_scan(NULL, scanCB);
|
||
|
||
os_printf("< ESP8266WiFi_getAccessPoints\n");
|
||
} // End of jswrap_ESP8266WiFi_getAccessPoints
|
||
|
||
|
||
/**
|
||
* \brief Disconnect the station from the access point.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "disconnect",
|
||
"generate" : "jswrap_ESP8266WiFi_disconnect"
|
||
}*/
|
||
void jswrap_ESP8266WiFi_disconnect() {
|
||
wifi_station_disconnect();
|
||
} // End of jswrap_ESP8266WiFi_disconnect
|
||
|
||
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "restart",
|
||
"generate" : "jswrap_ESP8266WiFi_restart"
|
||
}*/
|
||
void jswrap_ESP8266WiFi_restart() {
|
||
system_restart();
|
||
} // End of jswrap_ESP8266WiFi_restart
|
||
|
||
|
||
/**
|
||
* \brief Register a callback function that will be invoked when a WiFi event is detected.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "onWiFiEvent",
|
||
"generate" : "jswrap_ESP8266WiFi_onWiFiEvent",
|
||
"params" : [
|
||
["callback","JsVar","WiFi event callback"]
|
||
]
|
||
}*/
|
||
void jswrap_ESP8266WiFi_onWiFiEvent(
|
||
JsVar *callback //!< WiFi event callback.
|
||
) {
|
||
// If the callback is null
|
||
if (callback == NULL || jsvIsNull(callback)) {
|
||
if (jsWiFiEventCallback != NULL) {
|
||
jsvUnLock(jsWiFiEventCallback);
|
||
}
|
||
jsWiFiEventCallback = NULL;
|
||
return;
|
||
}
|
||
|
||
if (!jsvIsFunction(callback)) {
|
||
jsExceptionHere(JSET_ERROR, "No callback.");
|
||
return;
|
||
}
|
||
|
||
// We are about to save a new global WiFi even callback handler. If we have previously
|
||
// had one, we need to unlock it so that we don't leak memory.
|
||
if (jsWiFiEventCallback != NULL) {
|
||
jsvUnLock(jsWiFiEventCallback);
|
||
}
|
||
|
||
// Save the global WiFi event callback handler.
|
||
jsWiFiEventCallback = jsvLockAgainSafe(callback);
|
||
|
||
// Register
|
||
wifi_set_event_handler_cb(wifiEventHandler);
|
||
} // End of jswrap_ESP8266WiFi_onWiFiEvent
|
||
|
||
|
||
/**
|
||
* \brief Set whether or not the ESP8266 will perform an auto connect on startup.
|
||
* A value of true means it will while a value of false means it won't.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "setAutoConnect",
|
||
"generate" : "jswrap_ESP8266WiFi_setAutoConnect",
|
||
"params" : [
|
||
["autoconnect","JsVar","True if we wish to auto connect."]
|
||
]
|
||
}*/
|
||
void jswrap_ESP8266WiFi_setAutoConnect(
|
||
JsVar *autoconnect //!< True if we wish to auto connect.
|
||
) {
|
||
os_printf("Auto connect is: %d\n", (int)autoconnect);
|
||
// Check that we have been passed a boolean ... if not, nothing to do here.
|
||
if (!jsvIsBoolean(autoconnect)) {
|
||
return;
|
||
}
|
||
|
||
uint8 newValue = jsvGetBool(autoconnect);
|
||
os_printf("New value: %d\n", newValue);
|
||
os_printf("jswrap_ESP8266WiFi_setAutoConnect -> Something breaks here :-(\n");
|
||
|
||
uart_rx_intr_disable(0);
|
||
wifi_station_set_auto_connect(newValue);
|
||
uart_rx_intr_disable(1);
|
||
os_printf("Autoconnect changed\n");
|
||
} // End of jswrap_ESP8266WiFi_setAutoconnect
|
||
|
||
|
||
/**
|
||
* \brief Retrieve whether or not the ESP8266 will perform an auto connect on startup.
|
||
* A value of 1 means it will while a value of zero means it won't.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getAutoConnect",
|
||
"generate" : "jswrap_ESP8266WiFi_getAutoConnect",
|
||
"return" : ["JsVar","A boolean representing our auto connect status"]
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_getAutoConnect() {
|
||
uint8 result = wifi_station_get_auto_connect();
|
||
return jsvNewFromBool(result);
|
||
} // End of jswrap_ESP8266WiFi_getAutoconnect
|
||
|
||
|
||
/**
|
||
* \brief Retrieve the reset information that is stored when event the ESP8266 resets.
|
||
* The result will be a JS object containing the details.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getRstInfo",
|
||
"generate" : "jswrap_ESP8266WiFi_getRstInfo",
|
||
"return" : ["JsVar","A Restart Object"],
|
||
"return_object" : "Restart"
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_getRstInfo() {
|
||
struct rst_info* info = system_get_rst_info();
|
||
JsVar *restartInfo = jspNewObject(NULL, "Restart");
|
||
jsvUnLock(jsvObjectSetChild(restartInfo, "reason", jsvNewFromInteger(info->reason)));
|
||
jsvUnLock(jsvObjectSetChild(restartInfo, "exccause", jsvNewFromInteger(info->exccause)));
|
||
jsvUnLock(jsvObjectSetChild(restartInfo, "epc1", jsvNewFromInteger(info->epc1)));
|
||
jsvUnLock(jsvObjectSetChild(restartInfo, "epc2", jsvNewFromInteger(info->epc2)));
|
||
jsvUnLock(jsvObjectSetChild(restartInfo, "epc3", jsvNewFromInteger(info->epc3)));
|
||
jsvUnLock(jsvObjectSetChild(restartInfo, "excvaddr", jsvNewFromInteger(info->excvaddr)));
|
||
jsvUnLock(jsvObjectSetChild(restartInfo, "depc", jsvNewFromInteger(info->depc)));
|
||
return restartInfo;
|
||
} // End of jswrap_ESP8266WiFi_getRstInfo
|
||
|
||
|
||
/**
|
||
* \brief Return an object that contains details about the state of the ESP8266.
|
||
* - `sdkVersion` - Version of the SDK.
|
||
* - `cpuFrequency` - CPU operating frequency.
|
||
* - `freeHeap` - Amount of free heap.
|
||
* - `maxCon` - Maximum number of concurrent connections.
|
||
*
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getState",
|
||
"generate" : "jswrap_ESP8266WiFi_getState",
|
||
"return" : ["JsVar","The state of the ESP8266"],
|
||
"return_object" : "ESP8266State"
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_getState() {
|
||
// Create a new variable and populate it with the properties of the ESP8266 that we
|
||
// wish to return.
|
||
JsVar *esp8266State = jspNewObject(NULL, "ESP8266State");
|
||
jsvUnLock(jsvObjectSetChild(esp8266State, "sdkVersion", jsvNewFromString(system_get_sdk_version())));
|
||
jsvUnLock(jsvObjectSetChild(esp8266State, "cpuFrequency", jsvNewFromInteger(system_get_cpu_freq())));
|
||
jsvUnLock(jsvObjectSetChild(esp8266State, "freeHeap", jsvNewFromInteger(system_get_free_heap_size())));
|
||
jsvUnLock(jsvObjectSetChild(esp8266State, "maxCon", jsvNewFromInteger(espconn_tcp_get_max_con())));
|
||
return esp8266State;
|
||
} // End of jswrap_ESP8266WiFi_getState
|
||
|
||
/**
|
||
* \brief Return the value of an integer representation (4 bytes) of IP address
|
||
* as a string.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getAddressAsString",
|
||
"generate" : "jswrap_ESP8266WiFi_getAddressAsString",
|
||
"params" : [
|
||
["address","JsVar","An integer value representing an IP address."]
|
||
],
|
||
"return" : ["JsVar","A String"]
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_getAddressAsString(
|
||
JsVar *address //!< An integer value representing an IP address.
|
||
) {
|
||
if (!jsvIsInt(address)) {
|
||
jsExceptionHere(JSET_ERROR, "No SSID.");
|
||
return NULL;
|
||
}
|
||
uint32 iAddress = (uint32)jsvGetInteger(address);
|
||
return networkGetAddressAsString((uint8 *)&iAddress, 4, 10, '.');
|
||
} // End of jswrap_ESP8266WiFi_getAddressAsString
|
||
|
||
|
||
/**
|
||
* \brief Retrieve the IP information about this network interface and return a JS
|
||
* object that contains its details.
|
||
* The object will have the following properties defined upon it:
|
||
* - `ip` - The IP address of the interface.
|
||
* - `netmask` - The netmask of the interface.
|
||
* - `gw` - The gateway to reach when transmitting through the interface.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getIPInfo",
|
||
"generate" : "jswrap_ESP8266WiFi_getIPInfo",
|
||
"return" : ["JsVar","A IPInfo Object"],
|
||
"return_object" : "IPInfo"
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_getIPInfo() {
|
||
struct ip_info info;
|
||
wifi_get_ip_info(0, &info);
|
||
|
||
JsVar *ipInfo = jspNewObject(NULL, "Restart");
|
||
jsvUnLock(jsvObjectSetChild(ipInfo, "ip", jsvNewFromInteger(info.ip.addr)));
|
||
jsvUnLock(jsvObjectSetChild(ipInfo, "netmask", jsvNewFromInteger(info.netmask.addr)));
|
||
jsvUnLock(jsvObjectSetChild(ipInfo, "gw", jsvNewFromInteger(info.gw.addr)));
|
||
return ipInfo;
|
||
} // End of jswrap_ESP8266WiFi_getIPInfo
|
||
|
||
|
||
/**
|
||
* \brief Query the station configuration and return a JS object that represents the
|
||
* current settings.
|
||
* The object will have the following properties:
|
||
*
|
||
* - `ssid` - The network identity of the access point
|
||
* - `password` - The password to use to connect to the access point
|
||
*
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getStationConfig",
|
||
"generate" : "jswrap_ESP8266WiFi_getStationConfig",
|
||
"return" : ["JsVar","A Station Config"],
|
||
"return_object" : "StationConfig"
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_getStationConfig() {
|
||
struct station_config config;
|
||
wifi_station_get_config(&config);
|
||
JsVar *jsConfig = jspNewObject(NULL, "StationConfig");
|
||
//char ssid[33];
|
||
//nullTerminateString(ssid, (char *)config.ssid, 32);
|
||
jsvUnLock(jsvObjectSetChild(jsConfig, "ssid", jsvNewFromString((char *)config.ssid)));
|
||
//char password[65];
|
||
//nullTerminateString(password, (char *)config.password, 64);
|
||
jsvUnLock(jsvObjectSetChild(jsConfig, "password", jsvNewFromString((char *)config.password)));
|
||
return jsConfig;
|
||
} // End of jswrap_ESP8266WiFi_getStationConfig
|
||
|
||
|
||
/**
|
||
* \brief Determine the list of connected stations and return them.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getConnectedStations",
|
||
"generate" : "jswrap_ESP8266WiFi_getConnectedStations",
|
||
"return" : ["JsVar","An array of connected stations."]
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_getConnectedStations() {
|
||
uint8 stationCount = wifi_softap_get_station_num();
|
||
struct station_info *stationInfo = wifi_softap_get_station_info();
|
||
JsVar *jsArray = jsvNewArray(NULL, 0);
|
||
if (stationInfo != NULL) {
|
||
while (stationInfo != NULL) {
|
||
os_printf("Station IP: %d.%d.%d.%d\n", IP2STR(&(stationInfo->ip)));
|
||
JsVar *jsStation = jsvNewWithFlags(JSV_OBJECT);
|
||
jsvUnLock(jsvObjectSetChild(jsStation, "ip", jsvNewFromInteger(stationInfo->ip.addr)));
|
||
jsvArrayPush(jsArray, jsStation);
|
||
stationInfo = STAILQ_NEXT(stationInfo, next);
|
||
}
|
||
wifi_softap_free_station_info();
|
||
}
|
||
return jsArray;
|
||
} // End of jswrap_ESP8266WiFi_getConnectedStations
|
||
|
||
|
||
/**
|
||
* \brief Get the signal strength.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getRSSI",
|
||
"generate" : "jswrap_ESP8266WiFi_getRSSI",
|
||
"return" : ["JsVar","An integer representing the signal strength."]
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_getRSSI() {
|
||
int rssi = wifi_station_get_rssi();
|
||
return jsvNewFromInteger(rssi);
|
||
} // End of jswrap_ESP8266WiFi_getRSSI
|
||
|
||
|
||
|
||
/**
|
||
* \brief Initialize the ESP8266 environment.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "init",
|
||
"generate" : "jswrap_ESP8266WiFi_init"
|
||
}*/
|
||
void jswrap_ESP8266WiFi_init() {
|
||
os_printf("> jswrap_ESP8266WiFi_init\n");
|
||
netInit_esp8266_board();
|
||
setupJsNetwork();
|
||
networkState = NETWORKSTATE_ONLINE;
|
||
os_printf("< jswrap_ESP8266WiFi_init\n");
|
||
} // End of jswrap_ESP8266WiFi_init
|
||
|
||
|
||
/**
|
||
* Return the ESP8266 connection status.
|
||
* This is an integer value defined as:
|
||
* - 0 - STATION_IDLE
|
||
* - 1 - STATION_CONNECTING
|
||
* - 2 - STATION_WRONG_PASSWORD
|
||
* - 3 - STATION_NO_AP_FOUND
|
||
* - 4 - STATION_CONNECT_FAIL
|
||
* - 5 - STATION_GOT_IP
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "getConnectStatus",
|
||
"generate" : "jswrap_ESP8266WiFi_getConnectStatus",
|
||
"return" : ["JsVar","A connection status"]
|
||
}
|
||
|
||
Retrieve the connection status. The return is an object that contains:
|
||
|
||
* status - The status code from ESP8266
|
||
* statusMsg - The description of the code
|
||
|
||
*/
|
||
JsVar *jswrap_ESP8266WiFi_getConnectStatus() {
|
||
// Ask ESP8266 for the connection status
|
||
uint8 status = wifi_station_get_connect_status();
|
||
|
||
// Create a JS variable to return
|
||
JsVar *var = jsvNewWithFlags(JSV_OBJECT);
|
||
|
||
// Populate the return JS variable with a property called "status"
|
||
JsVar *jsStatus = jsvNewFromInteger(status);
|
||
//jsvUnLock(jsStatus);
|
||
jsvUnLock(jsvObjectSetChild(var, "status", jsStatus));
|
||
|
||
// Populate the return JS variable with a property called "statusMsg"
|
||
char *statusMsg;
|
||
switch(status) {
|
||
case STATION_IDLE:
|
||
statusMsg = "STATION_IDLE";
|
||
break;
|
||
case STATION_CONNECTING:
|
||
statusMsg = "STATION_CONNECTING";
|
||
break;
|
||
case STATION_WRONG_PASSWORD:
|
||
statusMsg = "STATION_WRONG_PASSWORD";
|
||
break;
|
||
case STATION_NO_AP_FOUND:
|
||
statusMsg = "STATION_NO_AP_FOUND";
|
||
break;
|
||
case STATION_CONNECT_FAIL:
|
||
statusMsg = "STATION_CONNECT_FAIL";
|
||
break;
|
||
case STATION_GOT_IP:
|
||
statusMsg = "STATION_GOT_IP";
|
||
break;
|
||
default:
|
||
statusMsg = "*** Unknown ***";
|
||
}
|
||
JsVar *jsStatusMsg = jsvNewFromString(statusMsg);
|
||
//jsvUnLock(jsStatusMsg);
|
||
jsvUnLock(jsvObjectSetChild(var, "statusMsg", jsStatusMsg));
|
||
//jsvUnLock(var);
|
||
return var;
|
||
} // End of jswrap_ESP8266WiFi_getConnectStatus
|
||
|
||
|
||
/**
|
||
* Test: Perform a socket connection to a partner system.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "socketConnect",
|
||
"generate" : "jswrap_ESP8266WiFi_socketConnect",
|
||
"params" : [
|
||
["options","JsVar","Some kind of options."],
|
||
["callback","JsVar","Some kind of callback."]
|
||
],
|
||
"return" : ["JsVar","A connection object"]
|
||
}*/
|
||
JsVar *jswrap_ESP8266WiFi_socketConnect(
|
||
JsVar *options, //!< Some kind of options.
|
||
JsVar *callback //!< Some kind of callback.
|
||
) {
|
||
os_printf("Network state = %d\n", networkState);
|
||
JsVar *ret = jswrap_net_connect(options, callback, ST_NORMAL);
|
||
return ret;
|
||
} // End of jswrap_ESP8266WiFi_socketConnect
|
||
|
||
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "socketEnd",
|
||
"generate" : "jswrap_ESP8266WiFi_socketEnd",
|
||
"params" : [
|
||
["socket","JsVar","The socket to be closed."],
|
||
["data","JsVar","Optional data to be sent before close."]
|
||
]
|
||
}*/
|
||
void jswrap_ESP8266WiFi_socketEnd(
|
||
JsVar *socket, //!< The socket to be closed.
|
||
JsVar *data //!< Optional data to be sent before close.
|
||
) {
|
||
jswrap_net_socket_end(socket, data);
|
||
} // End of jswrap_ESP8266WiFi_socketEnd
|
||
|
||
|
||
/**
|
||
* \brief Perform a network ping request.
|
||
* The parameter can be either a String or a numeric IP address.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "ping",
|
||
"generate" : "jswrap_ESP8266WiFi_ping",
|
||
"params" : [
|
||
["ipAddr","JsVar","A string or integer representation of an IP address."],
|
||
["pingCallback", "JsVar", "Optional callback function."]
|
||
]
|
||
}*/
|
||
void jswrap_ESP8266WiFi_ping(
|
||
JsVar *ipAddr, //!< A string or integer representation of an IP address.
|
||
JsVar *pingCallback //!< Optional callback function.
|
||
) {
|
||
// If the parameter is a string, get the IP address from the string
|
||
// representation.
|
||
if (jsvIsString(ipAddr)) {
|
||
char ipString[20];
|
||
int len = jsvGetString(ipAddr, ipString, sizeof(ipString)-1);
|
||
ipString[len] = '\0';
|
||
pingOpt.ip = networkParseIPAddress(ipString);
|
||
if (pingOpt.ip == 0) {
|
||
jsExceptionHere(JSET_ERROR, "Not a valid IP address.");
|
||
return;
|
||
}
|
||
} else
|
||
// If the parameter is an integer, treat it as an IP address.
|
||
if (jsvIsInt(ipAddr)) {
|
||
pingOpt.ip = jsvGetInteger(ipAddr);
|
||
} else
|
||
// The parameter was neither a string nor an IP address and hence we don't
|
||
// know how to get the IP address of the partner to ping so throw an
|
||
// exception.
|
||
{
|
||
jsExceptionHere(JSET_ERROR, "IP address must be string or integer.");
|
||
return;
|
||
}
|
||
|
||
if (jsvIsUndefined(pingCallback) || jsvIsNull(pingCallback)) {
|
||
if (jsPingCallback != NULL) {
|
||
jsvUnLock(jsPingCallback);
|
||
}
|
||
jsPingCallback = NULL;
|
||
} else if (!jsvIsFunction(pingCallback)) {
|
||
jsExceptionHere(JSET_ERROR, "Callback is not a function.");
|
||
return;
|
||
} else {
|
||
if (jsPingCallback != NULL) {
|
||
jsvUnLock(jsPingCallback);
|
||
}
|
||
jsPingCallback = pingCallback;
|
||
jsvLockAgainSafe(jsPingCallback);
|
||
}
|
||
|
||
// We now have an IP address to ping ... so ping.
|
||
memset(&pingOpt, 0, sizeof(pingOpt));
|
||
pingOpt.count = 5;
|
||
pingOpt.recv_function = pingRecvCB;
|
||
ping_start(&pingOpt);
|
||
} // End of jswrap_ESP8266WiFi_ping
|
||
|
||
|
||
/**
|
||
* \brief Dump the data in the socket.
|
||
*/
|
||
/*JSON{
|
||
"type" : "staticmethod",
|
||
"class" : "ESP8266WiFi",
|
||
"name" : "dumpSocket",
|
||
"generate" : "jswrap_ESP8266WiFi_dumpSocket",
|
||
"params" : [
|
||
["socketId","JsVar","The socket to be dumped."]
|
||
]
|
||
}*/
|
||
|
||
void jswrap_ESP8266WiFi_dumpSocket(
|
||
JsVar *socketId //!< The socket to be dumped.
|
||
) {
|
||
esp8266_dumpSocket(jsvGetInteger(socketId)-1);
|
||
} // End of jswrap_ESP8266WiFi_dumpSocket
|
||
|
||
/**
|
||
* \brief Null terminate a string.
|
||
*/
|
||
static char *nullTerminateString(char *target, char *source, int sourceLength) {
|
||
os_strncpy(target, source, sourceLength);
|
||
target[sourceLength-1] = '\0';
|
||
return target;
|
||
}
|
||
|
||
/**
|
||
*
|
||
*/
|
||
static void setupJsNetwork() {
|
||
JsNetwork net;
|
||
networkCreate(&net, JSNETWORKTYPE_ESP8266_BOARD);
|
||
networkSet(&net);
|
||
} // End of setupJsNetwork
|
||
|
||
|
||
/**
|
||
* \brief Handle receiving a response from a ping reply.
|
||
* If a callback function has been supplied we invoked that callback by queuing it for future
|
||
* execution. A parameter is supplied to the callback which is a JavaScript object that contains:
|
||
* - totalCount
|
||
* - totalBytes
|
||
* - totalTime
|
||
* - respTime
|
||
* - seqNo
|
||
* - timeoutCount
|
||
* - bytes
|
||
* - error
|
||
*/
|
||
static void pingRecvCB(void *pingOpt, void *pingResponse) {
|
||
struct ping_resp *pingResp = (struct ping_resp *)pingResponse;
|
||
os_printf("Received a ping response!\n");
|
||
if (jsPingCallback != NULL) {
|
||
JsVar *jsPingResponse = jspNewObject(NULL, "PingResponse");
|
||
jsvUnLock(jsvObjectSetChild(jsPingResponse, "totalCount", jsvNewFromInteger(pingResp->total_count)));
|
||
jsvUnLock(jsvObjectSetChild(jsPingResponse, "totalBytes", jsvNewFromInteger(pingResp->total_bytes)));
|
||
jsvUnLock(jsvObjectSetChild(jsPingResponse, "totalTime", jsvNewFromInteger(pingResp->total_time)));
|
||
jsvUnLock(jsvObjectSetChild(jsPingResponse, "respTime", jsvNewFromInteger(pingResp->resp_time)));
|
||
jsvUnLock(jsvObjectSetChild(jsPingResponse, "seqNo", jsvNewFromInteger(pingResp->seqno)));
|
||
jsvUnLock(jsvObjectSetChild(jsPingResponse, "timeoutCount", jsvNewFromInteger(pingResp->timeout_count)));
|
||
jsvUnLock(jsvObjectSetChild(jsPingResponse, "bytes", jsvNewFromInteger(pingResp->bytes)));
|
||
jsvUnLock(jsvObjectSetChild(jsPingResponse, "error", jsvNewFromInteger(pingResp->ping_err)));
|
||
JsVar *params[1];
|
||
params[0] = jsPingResponse;
|
||
jsiQueueEvents(NULL, jsPingCallback, params, 1);
|
||
} // End of we have a callback function
|
||
} // End of pingRecvCB
|
||
|
||
|
||
/**
|
||
* \brief Callback function that is invoked at the culmination of a scan.
|
||
*/
|
||
static void scanCB(void *arg, STATUS status) {
|
||
/**
|
||
* Create a JsVar that is an array of JS objects where each JS object represents a
|
||
* retrieved access point set of information. The structure of a record will be:
|
||
* o authMode
|
||
* o isHidden
|
||
* o rssi
|
||
* o channel
|
||
* o ssid
|
||
* When the array has been built, invoke the callback function passing in the array
|
||
* of records.
|
||
*/
|
||
|
||
os_printf(">> scanCB\n");
|
||
// Create the Empty JS array that will be passed as a parameter to the callback.
|
||
JsVar *accessPointArray = jsvNewArray(NULL, 0);
|
||
struct bss_info *bssInfo;
|
||
|
||
bssInfo = (struct bss_info *)arg;
|
||
// skip the first in the chain <20> it is invalid
|
||
bssInfo = STAILQ_NEXT(bssInfo, next);
|
||
while(bssInfo != NULL) {
|
||
// Add a new object to the JS array that will be passed as a parameter to
|
||
// the callback. The ESP8266 bssInfo structure contains the following:
|
||
// ---
|
||
// uint8 bssid[6]
|
||
// uint8 ssid[32]
|
||
// uint8 channel
|
||
// sint8 rssi <20> The received signal strength indication
|
||
// AUTH_MODE authmode
|
||
// Open = 0
|
||
// WEP = 1
|
||
// WPA_PSK = 2
|
||
// WPA2_PSK = 3
|
||
// WPA_WPA2_PSK = 4
|
||
// uint8 is_hidden
|
||
// sint16 freq_offset
|
||
// ---
|
||
// Create, populate and add a child ...
|
||
JsVar *currentAccessPoint = jspNewObject(NULL, "AccessPoint");
|
||
jsvUnLock(jsvObjectSetChild(currentAccessPoint, "rssi", jsvNewFromInteger(bssInfo->rssi)));
|
||
jsvUnLock(jsvObjectSetChild(currentAccessPoint, "channel", jsvNewFromInteger(bssInfo->channel)));
|
||
jsvUnLock(jsvObjectSetChild(currentAccessPoint, "authMode", jsvNewFromInteger(bssInfo->authmode)));
|
||
jsvUnLock(jsvObjectSetChild(currentAccessPoint, "isHidden", jsvNewFromBool(bssInfo->is_hidden)));
|
||
// The SSID may **NOT** be NULL terminated ... so handle that.
|
||
char ssid[sizeof(bssInfo->ssid) + 1];
|
||
os_strncpy((char *)ssid, (char *)bssInfo->ssid, sizeof(bssInfo->ssid));
|
||
ssid[sizeof(ssid)-1] = '\0';
|
||
jsvUnLock(jsvObjectSetChild(currentAccessPoint, "ssid", jsvNewFromString(ssid)));
|
||
|
||
// Add the new record to the array
|
||
jsvArrayPush(accessPointArray, currentAccessPoint);
|
||
|
||
os_printf(" - ssid: %s\n", bssInfo->ssid);
|
||
bssInfo = STAILQ_NEXT(bssInfo, next);
|
||
} // End of loop over the records.
|
||
|
||
// We have now completed the scan callback, so now we can invoke the JS callback.
|
||
JsVar *params[1];
|
||
params[0] = accessPointArray;
|
||
jsiQueueEvents(NULL, jsScanCallback, params, 1);
|
||
jsvUnLock(jsScanCallback);
|
||
os_printf("<< scanCB\n");
|
||
} // End of scanCB
|
||
|
||
|
||
/**
|
||
* \brief Invoke the JavaScript callback to notify the program that an ESP8266
|
||
* WiFi event has occurred.
|
||
*/
|
||
static void sendWifiEvent(uint32 eventType, JsVar *details) {
|
||
// We need to check that we actually have an event callback handler because
|
||
// it might have been disabled/removed.
|
||
if (jsWiFiEventCallback != NULL) {
|
||
// Build a callback event.
|
||
JsVar *params[2];
|
||
params[0] = jsvNewFromInteger(eventType);
|
||
params[1] = details;
|
||
jsiQueueEvents(NULL, jsWiFiEventCallback, params, 2);
|
||
}
|
||
|
||
if (jsGotIpCallback != NULL && eventType == EVENT_STAMODE_GOT_IP) {
|
||
JsVar *params[2];
|
||
params[0] = jsvNewFromInteger(eventType);
|
||
params[1] = details;
|
||
jsiQueueEvents(NULL, jsGotIpCallback, params, 2);
|
||
// Once we have registered the callback, we can unlock and release
|
||
// the variable as we are only calling it once.
|
||
//jsvUnLock(jsGotIpCallback);
|
||
//jsGotIpCallback = NULL;
|
||
}
|
||
} // End of sendWifiEvent
|
||
|
||
|
||
/**
|
||
* \brief ESP8266 WiFi Event handler.
|
||
* This function is called by the ESP8266
|
||
* environment when significant events happen related to the WiFi environment.
|
||
* The event handler is registered with a call to wifi_set_event_handler_cb()
|
||
* that is provided by the ESP8266 SDK.
|
||
*/
|
||
static void wifiEventHandler(System_Event_t *event) {
|
||
switch(event->event) {
|
||
// We have connected to an access point.
|
||
case EVENT_STAMODE_CONNECTED:
|
||
os_printf("Event: EVENT_STAMODE_CONNECTED\n");
|
||
sendWifiEvent(event->event, jsvNewNull());
|
||
break;
|
||
|
||
// We have disconnected or been disconnected from an access point.
|
||
case EVENT_STAMODE_DISCONNECTED:
|
||
os_printf("Event: EVENT_STAMODE_DISCONNECTED\n");
|
||
JsVar *details = jspNewObject(NULL, "EventDetails");
|
||
jsvObjectSetChild(details, "reason", jsvNewFromInteger(event->event_info.disconnected.reason));
|
||
char ssid[33];
|
||
memcpy(ssid, event->event_info.disconnected.ssid, event->event_info.disconnected.ssid_len);
|
||
ssid[ event->event_info.disconnected.ssid_len] = '\0';
|
||
sendWifiEvent(event->event, details);
|
||
break;
|
||
|
||
// The authentication information at the access point has changed.
|
||
case EVENT_STAMODE_AUTHMODE_CHANGE:
|
||
os_printf("Event: EVENT_STAMODE_AUTHMODE_CHANGE\n");
|
||
sendWifiEvent(event->event, jsvNewNull());
|
||
break;
|
||
|
||
// We have been allocated an IP address.
|
||
case EVENT_STAMODE_GOT_IP:
|
||
os_printf("Event: EVENT_STAMODE_GOT_IP\n");
|
||
sendWifiEvent(event->event, jsvNewNull());
|
||
break;
|
||
case EVENT_SOFTAPMODE_STACONNECTED:
|
||
os_printf("Event: EVENT_SOFTAPMODE_STACONNECTED\n");
|
||
sendWifiEvent(event->event, jsvNewNull());
|
||
break;
|
||
case EVENT_SOFTAPMODE_STADISCONNECTED:
|
||
os_printf("Event: EVENT_SOFTAPMODE_STADISCONNECTED\n");
|
||
sendWifiEvent(event->event, jsvNewNull());
|
||
break;
|
||
default:
|
||
os_printf("Unexpected event: %d\n", event->event);
|
||
sendWifiEvent(event->event, jsvNewNull());
|
||
break;
|
||
}
|
||
} // End of wifiEventHandler
|
||
|
||
/**
|
||
* \brief Write an IP address as a dotted decimal string.
|
||
*/
|
||
// Note: This may be a duplicate ... it appears that we may have an existing function
|
||
// in network.c which does exactly this and more!!
|
||
//
|
||
static void ipAddrToString(struct ip_addr addr, char *string) {
|
||
os_sprintf(string, "%d.%d.%d.%d", ((char *)&addr)[0], ((char *)&addr)[1], ((char *)&addr)[2], ((char *)&addr)[3]);
|
||
} // End of ipAddrToString
|
||
|
||
// End of file
|