diff --git a/libs/network/esp8266/jswrap_esp8266.c b/libs/network/esp8266/jswrap_esp8266.c new file mode 100644 index 000000000..bb9da0a52 --- /dev/null +++ b/libs/network/esp8266/jswrap_esp8266.c @@ -0,0 +1,949 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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, JsVar *jsv_password, JsVar *gotIpCallback) { + // 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; + } + 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'; + + jsiConsolePrintf("jswrap_ESP8266WiFi_connect: %s - %s\r\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 + wifi_set_event_handler_cb(wifiEventHandler); + + wifi_station_connect(); +} // 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. + ) { + jsiConsolePrint("> ESP8266WiFi_getAccessPoints\n"); + if (callback == NULL || !jsvIsFunction(callback)) { + jsExceptionHere(JSET_ERROR, "No callback."); + return; + } + + // Save the callback for the scan + jsScanCallback = jsvLockAgainSafe(callback); + + // Ask the ESP8266 to perform a network scan after first entering + // station mode. This will result in an eventual callback which is where + // 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); + + jsiConsolePrint("< 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. + */ + + // 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 … 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 – 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); +} // 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 happend 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) { + case EVENT_STAMODE_CONNECTED: + os_printf("Event: EVENT_STAMODE_CONNECTED\n"); + sendWifiEvent(event->event, jsvNewNull()); + break; + 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; + case EVENT_STAMODE_AUTHMODE_CHANGE: + os_printf("Event: EVENT_STAMODE_AUTHMODE_CHANGE\n"); + sendWifiEvent(event->event, jsvNewNull()); + break; + 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 diff --git a/libs/network/esp8266/jswrap_esp8266.h b/libs/network/esp8266/jswrap_esp8266.h new file mode 100644 index 000000000..c7d4be153 --- /dev/null +++ b/libs/network/esp8266/jswrap_esp8266.h @@ -0,0 +1,34 @@ +/* + * jswrap_ESP8266WiFi.h + * + * Created on: Aug 26, 2015 + * Author: kolban + */ + +#ifndef LIBS_NETWORK_ESP8266_JSWRAP_ESP8266_H_ +#define LIBS_NETWORK_ESP8266_JSWRAP_ESP8266_H_ +#include "jsvar.h" + +void jswrap_ESP8266WiFi_connect(JsVar *jsv_ssid, JsVar *jsv_password, JsVar *gotIpCallback); +void jswrap_ESP8266WiFi_getAccessPoints(JsVar *callback); +void jswrap_ESP8266WiFi_disconnect(); +void jswrap_ESP8266WiFi_restart(); +JsVar *jswrap_ESP8266WiFi_getRstInfo(); +JsVar *jswrap_ESP8266WiFi_getIPInfo(); +void jswrap_ESP8266WiFi_setAutoConnect(JsVar *autoconnect); +JsVar *jswrap_ESP8266WiFi_getAutoConnect(); +JsVar *jswrap_ESP8266WiFi_getStationConfig(); +void jswrap_ESP8266WiFi_onWiFiEvent(JsVar *callback); +JsVar *jswrap_ESP8266WiFi_getAddressAsString(JsVar *address); +void jswrap_ESP8266WiFi_init(); +JsVar *jswrap_ESP8266WiFi_getConnectStatus(); +JsVar *jswrap_ESP8266WiFi_socketConnect(JsVar *options, JsVar *callback); +void jswrap_ESP8266WiFi_socketEnd(JsVar *socket, JsVar *data); +void jswrap_ESP8266WiFi_ping(JsVar *ipAddr, JsVar *pingCallback); +void jswrap_ESP8266WiFi_beAccessPoint(JsVar *jsv_ssid, JsVar *jsv_password); +JsVar *jswrap_ESP8266WiFi_getConnectedStations(); +JsVar *jswrap_ESP8266WiFi_getRSSI(); +JsVar *jswrap_ESP8266WiFi_getState(); +void jswrap_ESP8266WiFi_dumpSocket(JsVar *socketId); + +#endif /* LIBS_NETWORK_ESP8266_JSWRAP_ESP8266_H_ */ diff --git a/libs/network/esp8266/network_esp8266.c b/libs/network/esp8266/network_esp8266.c new file mode 100644 index 000000000..5e91e59c8 --- /dev/null +++ b/libs/network/esp8266/network_esp8266.c @@ -0,0 +1,1187 @@ +/* +5 * network_esp8266_board.c + + * + * Created on: Aug 29, 2015 + * Author: kolban + */ + +/** + * This file contains the implementation of the ESP8266_BOARD network interfaces at the TCP/IP + * level. + * + * Design notes + * ------------ + * We maintain an array of socketData structures. The number of such structures is defined in the + * MAX_SOCKETS define. The private variable that contains the array is called "socketArray". + * Each one of these array instances represents a possible socket that we can use. + * + * Within the code, this allows us to reference a socket instance by an integer. For example, + * socket 0 is the 1st instance in the array. + * + * Associated with the array are accessor functions: + * + * o getNextFreeSocket - Return the next free socket or -1 if there are no free sockets. + * o getSocketData - Get the socketData structure corresponding to the socket integer. + * o resetSocket - Reset the state of the socket and clean it up if needed. + * o releaseSocket - Release the socket and return it to the pool of unused sockets. + * + */ +// ESP8266 specific includes +#include +#include +#include +#include +#include +#include + +#define _GCC_WRAP_STDINT_H +typedef long long int64_t; + +#include "network_esp8266.h" +#include "esp8266_board_utils.h" + +/** + * \brief The maximum number of concurrently open sockets we support. + * We should probably pair this with the ESP8266 concept of the maximum number of sockets + * that an ESP8266 instance can also support. + */ +#define MAX_SOCKETS (10) + +#define LOG os_printf + +static struct socketData *getSocketData(int s); + +static int getServerSocketByLocalPort(unsigned short port); +static void setSocketInError(int socketId, char *msg, int code); +static void dumpEspConn(struct espconn *pEspConn); +static int getNextFreeSocket(); +static void doClose(int socketId); +static void releaseSocket(int socketId); +static void resetSocket(int sckt); + +static void esp8266_callback_connectCB_inbound(void *arg); +static void esp8266_callback_connectCB_outbound(void *arg); +static void esp8266_callback_disconnectCB(void *arg); +static void esp8266_callback_sentCB(void *arg); +static void esp8266_callback_writeFinishedCB(void *arg); +static void esp8266_callback_recvCB(void *arg, char *pData, unsigned short len); +static void esp8266_callback_reconnectCB(void *arg, sint8 err); + +/** + * \brief A data structure that represents a memory buffer. + * A memory buffer is an object that represents a sized piece of memory. Given a + * memory buffer object, we know how big it is and can set or get data from it. + */ +struct memoryBuffer { + /** + * \brief The size of data associated with this buffer. + */ + size_t length; + + /** + * \brief A pointer to the memory associated with this buffer. This should be + * NULL if `length` is 0. + */ + uint8 *buf; +}; + +static uint8 *memoryBuffer_read(struct memoryBuffer *pMemoryBuffer, size_t readSize); +static uint8 *memoryBuffer_append(struct memoryBuffer *pMemoryBuffer, uint8 *pNewData, size_t length); +static void memoryBuffer_delete(struct memoryBuffer *pMemoryBuffer); +static int memoryBuffer_getSize(struct memoryBuffer *pMemoryBuffer); + +/** + * \brief The potential states for a socket. + * See the socket state diagram. + */ +enum SOCKET_STATE { + SOCKET_STATE_UNUSED, //!< The socket is unused + SOCKET_STATE_IDLE, //!< The socket is idle + SOCKET_STATE_CONNECTING, //!< The socket is connecting + SOCKET_STATE_TRANSMITTING, //!< The socket is transmitting + SOCKET_STATE_CLOSING, //!< The socket is closing + SOCKET_STATE_ERROR //!< The socket is in error +}; + +/** + * void initSocketData() + * int getNextFreeSocket() + * void releaseSocket(int s) + * struct socketData *getSocketData(int s) + */ + + + +/** + * Here are some notes on the accepted list algorithms. + * + * tail - Where old entries are removed + * head - Where new entries are added + * + * When tail = head - the list is empty + * + * When a new entry is added: + * error: mod(head+1) == tail // List full + * *head = new entry + * head = mod(head+1) + * + * When an old entry is removed + * error: tail == head // List empty + * retrieved entry = *tail + * tail = mod(tail+1) + * + * + * 0 1 2 3 4 5 6 7 8 + * [ ][ ][ ][ ][ ][ ][ ][ ][ ] + */ + +/** + * \brief The maximum number of accepted sockets that can be remembered. + */ +#define MAX_ACCEPTED_SOCKETS (10) + +/** + * \brief The core socket structure. + * The structure is initialized by resetSocket. + */ +struct socketData { + int socketId; //!< The id of THIS socket. + enum SOCKET_STATE state; //!< What is the socket state? + bool isConnected; //!< Is this socket connected? + bool isServer; //!< Are we a server? + bool shouldClose; //!< Should this socket close when it can? + + struct espconn *pEspconn; //!< The ESPConn structure. + + struct memoryBuffer txMemoryBuffer; //!< Data to be transmitted. + uint8 *currentTx; //!< Data currently being transmitted. + uint8 *rxBuf; //!< Data received (inbound). + size_t rxBufLen; //!< The length of data in the buffer ready for consumption. + + char *errorMsg; //!< Error message. + int errorCode; //!< Error code. + + /** + * \brief A list of accepted sockets. + * This array contains the storage of a list of sockets that have been accepted by this + * server socket but have not yet been delivered to Espruino. A `head` and `tail` + * pair of indices are also associated. + */ + int acceptedSockets[MAX_ACCEPTED_SOCKETS]; + + /** + * \brief The head of the list of accepted sockets. + * The index into `acceptedSockets` where the next accepted socket will be placed. + */ + uint8 acceptedSocketsHead; + + /** + * \brief The tail of the list of accepted sockets. + * The index into `acceptedSockets` where the next accepted socket will be retrieved. + */ + uint8 acceptedSocketsTail; + +}; + + +/** + * \brief An array of socket data structures. + */ +static struct socketData socketArray[MAX_SOCKETS]; + + +/** + * \brief An array of `esp_tcp` data structures. + */ +static esp_tcp tcpArray[MAX_SOCKETS]; + + +/** + * \brief An array of `struct espconn` data structures. + */ +static struct espconn espconnArray[MAX_SOCKETS]; + + +/** + * \brief Write the details of a socket to the debug log. + * The data associated with the socket is dumped to the debug log. + */ +void esp8266_dumpSocket( + int socketId //!< The ID of the socket data structure to be logged. + ) { + struct socketData *pSocketData = getSocketData(socketId); + LOG("Dump of socket=%d\n", socketId); + LOG(" - isConnected=%d", pSocketData->isConnected); + LOG(", isServer=%d", pSocketData->isServer); + LOG(", acceptedSockets=["); + int s=pSocketData->acceptedSocketsTail; + while(s != pSocketData->acceptedSocketsHead) { + LOG(" %d", pSocketData->acceptedSockets[s]); + s = (s+1)%MAX_ACCEPTED_SOCKETS; + } + LOG("]"); + LOG(", rxBufLen=%d", pSocketData->rxBufLen); + LOG(", tx length=%d", memoryBuffer_getSize(&(pSocketData->txMemoryBuffer))); + LOG(", currentTx=0x%d", (int)pSocketData->currentTx); + char *stateMsg; + switch(pSocketData->state) { + case SOCKET_STATE_CLOSING: + stateMsg = "SOCKET_STATE_CLOSING"; + break; + case SOCKET_STATE_CONNECTING: + stateMsg = "SOCKET_STATE_CONNECTING"; + break; + case SOCKET_STATE_ERROR: + stateMsg = "SOCKET_STATE_ERROR"; + break; + case SOCKET_STATE_IDLE: + stateMsg = "SOCKET_STATE_IDLE"; + break; + case SOCKET_STATE_TRANSMITTING: + stateMsg = "SOCKET_STATE_TRANSMITTING"; + break; + case SOCKET_STATE_UNUSED: + stateMsg = "SOCKET_STATE_UNUSED"; + break; + default: + stateMsg = "Unexpected state!!"; + break; + } + LOG(", state=%s", stateMsg); + LOG(", errorCode=%d", pSocketData->errorCode); + + // Print the errorMsg if it has anything to say + if (pSocketData->errorMsg != NULL && strlen(pSocketData->errorMsg) > 0) { + LOG(", errorMsg=\"%s\"", pSocketData->errorMsg); + } + + LOG("\n"); +} // End of dumpSocket + + +/** + * \brief Dump a struct espconn (for debugging purposes). + */ +static void dumpEspConn( + struct espconn *pEspConn //!< + ) { + char ipString[20]; + LOG("Dump of espconn: 0x%x\n", (int)pEspConn); + if (pEspConn == NULL) { + return; + } + switch(pEspConn->type) { + case ESPCONN_TCP: + LOG(" - type = TCP\n"); + LOG(" - local address = %d.%d.%d.%d [%d]\n", + pEspConn->proto.tcp->local_ip[0], + pEspConn->proto.tcp->local_ip[1], + pEspConn->proto.tcp->local_ip[2], + pEspConn->proto.tcp->local_ip[3], + pEspConn->proto.tcp->local_port); + LOG(" - remote address = %d.%d.%d.%d [%d]\n", + pEspConn->proto.tcp->remote_ip[0], + pEspConn->proto.tcp->remote_ip[1], + pEspConn->proto.tcp->remote_ip[2], + pEspConn->proto.tcp->remote_ip[3], + pEspConn->proto.tcp->remote_port); + break; + case ESPCONN_UDP: + LOG(" - type = UDP\n"); + LOG(" - local_port = %d\n", pEspConn->proto.udp->local_port); + LOG(" - local_ip = %d.%d.%d.%d\n", + pEspConn->proto.tcp->local_ip[0], + pEspConn->proto.tcp->local_ip[1], + pEspConn->proto.tcp->local_ip[2], + pEspConn->proto.tcp->local_ip[3]); + LOG(" - remote_port = %d\n", pEspConn->proto.udp->remote_port); + LOG(" - remote_ip = %d.%d.%d.%d\n", + pEspConn->proto.tcp->remote_ip[0], + pEspConn->proto.tcp->remote_ip[1], + pEspConn->proto.tcp->remote_ip[2], + pEspConn->proto.tcp->remote_ip[3]); + break; + default: + LOG(" - type = Unknown!! 0x%x\n", pEspConn->type); + } + switch(pEspConn->state) { + case ESPCONN_NONE: + LOG(" - state=NONE"); + break; + case ESPCONN_WAIT: + LOG(" - state=WAIT"); + break; + case ESPCONN_LISTEN: + LOG(" - state=LISTEN"); + break; + case ESPCONN_CONNECT: + LOG(" - state=CONNECT"); + break; + case ESPCONN_WRITE: + LOG(" - state=WRITE"); + break; + case ESPCONN_READ: + LOG(" - state=READ"); + break; + case ESPCONN_CLOSE: + LOG(" - state=CLOSE"); + break; + default: + LOG(" - state=unknown!!"); + break; + } + LOG(", link_cnt=%d", pEspConn->link_cnt); + LOG(", reverse=0x%x\n", (unsigned int)pEspConn->reverse); +} // End of dumpEspConn + + +/** + * \brief Get the next free socket. + * Look for the first free socket in the array of sockets and return the first one + * that is available after first flagging it as now in use. If no available + * socket can be found, return -1. + */ +static int getNextFreeSocket() { + for (int i=0; i=0 && sstate != SOCKET_STATE_UNUSED && + pSocketData->isServer == true && + pSocketData->pEspconn->proto.tcp->local_port == port) + { + return socketArrayIndex; + } + pSocketData++; + } // End of for each socket + return -1; +} // End of getServerSocketByLocalPort + + +/** + * \brief Reset the socket to its clean and unused state. + */ +static void resetSocket( + int sckt //!< + ) { + struct socketData *pSocketData = getSocketData(sckt); + + memoryBuffer_delete(&pSocketData->txMemoryBuffer); + + pSocketData->pEspconn = &espconnArray[sckt]; + pSocketData->state = SOCKET_STATE_UNUSED; + pSocketData->rxBuf = NULL; + pSocketData->rxBufLen = 0; + pSocketData->isServer = false; + pSocketData->isConnected = false; + pSocketData->shouldClose = false; + pSocketData->socketId = sckt; + pSocketData->errorMsg = ""; + pSocketData->errorCode = 0; + + pSocketData->acceptedSocketsHead = 0; // Set the head to 0 + pSocketData->acceptedSocketsTail = 0; // Set the tail to 9. + + struct espconn *pEspconn = pSocketData->pEspconn; + pEspconn->type = ESPCONN_TCP; + pEspconn->state = ESPCONN_NONE; + pEspconn->proto.tcp = &tcpArray[sckt]; + pEspconn->reverse = NULL; + + + espconn_regist_connectcb(pEspconn, esp8266_callback_connectCB_outbound); + espconn_regist_disconcb(pEspconn, esp8266_callback_disconnectCB); + espconn_regist_reconcb(pEspconn, esp8266_callback_reconnectCB); + espconn_regist_sentcb(pEspconn, esp8266_callback_sentCB); + espconn_regist_recvcb(pEspconn, esp8266_callback_recvCB); + espconn_regist_write_finish(pEspconn, esp8266_callback_writeFinishedCB); +} // End of resetSocket + + +/** + * \brief Release the socket and return it to the free pool. + */ +static void releaseSocket( + int socketId //!< The socket id of the socket to be released. + ) { + os_printf("> releaseSocket: %d\n", socketId); + assert(socketId >=0 && socketIdstate != SOCKET_STATE_UNUSED); + + if (memoryBuffer_getSize(&pSocketData->txMemoryBuffer) > 0) { + os_printf(" - Oh oh ... attempt to close socket while the TX memoryBuffer is not empty!\n"); + } + if (pSocketData->rxBuf != NULL || pSocketData->rxBufLen != 0) { + os_printf(" - Oh oh ... attempt to close socket while the rxBuffer is not empty!\n"); + } + resetSocket(socketId); + os_printf("< releaseSocket\n"); +} // End of releaseSocket + + +/** + * \brief Initialize the ESP8266_BOARD environment. + * Walk through each of the sockets and initialize each one. + */ +void netInit_esp8266_board() { + for (int socketArrayIndex=0; socketArrayIndexstate != SOCKET_STATE_CLOSING) { + int rc = espconn_disconnect(pSocketData->pEspconn); + pSocketData->state = SOCKET_STATE_CLOSING; + + if (rc != 0) { + os_printf("espconn_disconnect: rc=%d\n", rc); + setSocketInError(socketId, "espconn_disconnect", rc); + } + } else { + releaseSocket(socketId); + } +} // End of doClose + + +/** + * \brief Set the given socket as being in error supplying a message and a code. + * The socket state is placed in `SOCKET_STATE_ERROR`. + */ +static void setSocketInError( + int socketId, //!< The socket id that is being flagged as in error. + char *msg, //!< A message to associate with the error. + int code //!< A low level error code. + ) { + struct socketData *pSocketData = getSocketData(socketId); + pSocketData->state = SOCKET_STATE_ERROR; + pSocketData->errorMsg = msg; + pSocketData->errorCode = code; +} // End of setSocketInError + +/** + * \brief Callback function registered to the ESP8266 environment that is + * invoked when a new inbound connection has been formed. + * A new connection + * can occur when the ESP8266 makes a call out to a partner (in that + * case the ESP8266 is acting as a client) or a new connection can + * occur when a partner calls into a listening ESP8266. In that case + * the ESP8266 is acting as a server. + */ +static void esp8266_callback_connectCB_inbound( + void *arg //!< + ) { + os_printf(">> connectCB_inbound\n"); + struct espconn *pEspconn = (struct espconn *)arg; + assert(pEspconn != NULL); + + dumpEspConn(pEspconn); + + int s = getServerSocketByLocalPort(pEspconn->proto.tcp->local_port); + assert(s != -1); + struct socketData *pSocketData = getSocketData(s); + assert(pSocketData != NULL); + + esp8266_dumpSocket(pSocketData->socketId); + + os_printf("** new client has connected to us **\n"); + + if ((pSocketData->acceptedSocketsHead + 1) % MAX_ACCEPTED_SOCKETS == pSocketData->acceptedSocketsTail) { + os_printf("WARNING!! - Discarding inbound client because we have too many accepted clients.\n"); + os_printf("<< connectCB_inbound\n"); + return; + } + + int clientSocket = getNextFreeSocket(); + if (clientSocket < 0) { + os_printf("!!! Ran out of sockets !!!\n"); + return; + } + struct socketData *pClientSocketData = getSocketData(clientSocket); + assert(pClientSocketData != NULL); + pClientSocketData->pEspconn = pEspconn; + pClientSocketData->pEspconn->reverse = pClientSocketData; + pClientSocketData->isServer = false; + pClientSocketData->isConnected = true; + pClientSocketData->socketId = clientSocket; + pClientSocketData->state = SOCKET_STATE_IDLE; + + pSocketData->acceptedSockets[pSocketData->acceptedSocketsHead] = clientSocket; + pSocketData->acceptedSocketsHead = (pSocketData->acceptedSocketsHead + 1) % MAX_ACCEPTED_SOCKETS; + + os_printf("<< connectCB_inbound\n"); +} // End of esp8266_callback_connectCB_inbound + +/** + * \brief Callback function registered to the ESP8266 environment that is + * invoked when a new outbound connection has been formed. + */ +static void esp8266_callback_connectCB_outbound( + void *arg //!< A pointer to a `struct espconn`. + ) { + os_printf(">> connectCB_outbound\n"); + struct espconn *pEspconn = (struct espconn *)arg; + assert(pEspconn != NULL); + + dumpEspConn(pEspconn); + + struct socketData *pSocketData = (struct socketData *)pEspconn->reverse; + assert(pSocketData != NULL); + + esp8266_dumpSocket(pSocketData->socketId); + + // Flag the socket as connected to a partner. + pSocketData->isConnected = true; + + assert(pSocketData->state == SOCKET_STATE_CONNECTING); + if (pSocketData->shouldClose) { + doClose(pSocketData->socketId); + } else { + pSocketData->state = SOCKET_STATE_IDLE; + } + os_printf("<< connectCB_outbound\n"); +} // End of esp8266_callback_connectCB_outbound + + +/** + * \brief Callback function registered to the ESP8266 environment that is + * Invoked when a previous connection has been disconnected. + */ +static void esp8266_callback_disconnectCB( + void *arg //!< A pointer to a `struct espconn`. + ) { + struct espconn *pEspconn = (struct espconn *)arg; + struct socketData *pSocketData = (struct socketData *)pEspconn->reverse; + assert(pSocketData != NULL); + assert(pSocketData->state != SOCKET_STATE_UNUSED); + + os_printf(">> disconnectCB\n"); + dumpEspConn(pEspconn); + esp8266_dumpSocket(pSocketData->socketId); + + // If the socket state is SOCKET_STATE_CLOSING then that means we can release the socket. The reason + // for this is that the last thing the user did was request an explicit socket close. + if (pSocketData->state == SOCKET_STATE_CLOSING) { + releaseSocket(pSocketData->socketId); + } else { + pSocketData->state = SOCKET_STATE_CLOSING; + pSocketData->isConnected = false; + } + os_printf("<< disconnectCB\n"); +} // End of disconnectCB + + +/** + * + */ +static void esp8266_callback_writeFinishedCB( + void *arg //!< A pointer to a `struct espconn`. + ) { + os_printf(">> writeFinishedCB\n"); + struct espconn *pEspconn = (struct espconn *)arg; + struct socketData *pSocketData = (struct socketData *)pEspconn->reverse; + if (pSocketData->currentTx != NULL) { + os_free(pSocketData->currentTx); + pSocketData->currentTx = NULL; + } + os_printf("<< writeFinishedCB\n"); +} // End of writeFinishedCB + + +/** + * \brief Error handler callback. + * Although this is called reconnect by Espressif, this is really an error handler + * routine. It will be called when an error is detected. + */ +static void esp8266_callback_reconnectCB( + void *arg, //!< A pointer to a `struct espconn`. + sint8 err //!< The error code. + ) { + os_printf(">> reconnectCB: Error code is: %d - %s\n", err, esp8266_errorToString(err)); + os_printf("<< reconnectCB"); +} // End of reconnectCB + + +/** + * \brief Callback function registered to the ESP8266 environment that is + * invoked when a send operation has been completed. + */ +static void esp8266_callback_sentCB( + void *arg //!< A pointer to a `struct espconn`. + ) { + os_printf(">> sendCB\n"); + struct espconn *pEspconn = (struct espconn *)arg; + struct socketData *pSocketData = (struct socketData *)pEspconn->reverse; + + assert(pSocketData != NULL); + assert(pSocketData->state == SOCKET_STATE_TRANSMITTING); + + // We have transmitted the data ... which means that the data that was in the transmission + // buffer can be released. + if (pSocketData->currentTx != NULL) { + os_free(pSocketData->currentTx); + pSocketData->currentTx = NULL; + } + + if (pSocketData->shouldClose) { + doClose(pSocketData->socketId); + } else { + pSocketData->state = SOCKET_STATE_IDLE; + } + os_printf("<< sendCB\n"); +} // End of sentCB + + +/** + * \brief ESP8266 callback function that is invoked when new data has arrived over + * the TCP/IP connection. + */ +static void esp8266_callback_recvCB( + void *arg, //!< A pointer to a `struct espconn`. + char *pData, //!< A pointer to data received over the socket. + unsigned short len //!< The length of the data. + ) { + struct espconn *pEspconn = (struct espconn *)arg; + struct socketData *pSocketData = (struct socketData *)pEspconn->reverse; + + assert(pSocketData != NULL); + assert(pSocketData->state != SOCKET_STATE_UNUSED); + + os_printf(">> recvCB for socket=%d, length=%d\n", pSocketData->socketId, len); + + // If we don't have any existing unconsumed data then malloc some storage and + // copy the received data into that storage. + if (pSocketData->rxBufLen == 0) { + pSocketData->rxBuf = (void *)os_malloc(len); + memcpy(pSocketData->rxBuf, pData, len); + pSocketData->rxBufLen = len; + } else { +// Allocate a new buffer big enough for the original data and the new data +// Copy the original data to the start of the new buffer ... +// Copy the new new data to the offset into the new buffer just after +// the original data. +// Release the original data. +// Update the socket data. + uint8 *pNewBuf = (uint8 *)os_malloc(len + pSocketData->rxBufLen); + memcpy(pNewBuf, pSocketData->rxBuf, pSocketData->rxBufLen); + memcpy(pNewBuf + pSocketData->rxBufLen, pData, len); + os_free(pSocketData->rxBuf); + pSocketData->rxBuf = pNewBuf; + pSocketData->rxBufLen += len; + } // End of new data allocated. + dumpEspConn(pEspconn); + os_printf("<< recvCB\n"); + +} // End of recvCB + + +// ------------------------------------------------- + +/** + * \brief Define the implementation functions for the logical network functions. + */ +void netSetCallbacks_esp8266_board( + JsNetwork *net //!< The Network we are going to use. + ) { + net->idle = net_ESP8266_BOARD_idle; + net->checkError = net_ESP8266_BOARD_checkError; + net->createsocket = net_ESP8266_BOARD_createSocket; + net->closesocket = net_ESP8266_BOARD_closeSocket; + net->accept = net_ESP8266_BOARD_accept; + net->gethostbyname = net_ESP8266_BOARD_gethostbyname; + net->recv = net_ESP8266_BOARD_recv; + net->send = net_ESP8266_BOARD_send; +} // End of netSetCallbacks_esp8266_board + + +/** + * \brief Determine if there is a new client connection on the server socket. + * This function is called to poll to see if the serverSckt has a new + * accepted connection (socket) and, if it does, return it else return -1 to indicate + * that there was no new accepted socket. + */ +int net_ESP8266_BOARD_accept( + JsNetwork *net, //!< The Network we are going to use to create the socket. + int serverSckt //!< The socket that we are checking to see if there is a new client connection. + ) { + //os_printf("> net_ESP8266_BOARD_accept\n"); + struct socketData *pSocketData = getSocketData(serverSckt); + assert(pSocketData->state != SOCKET_STATE_UNUSED); + assert(pSocketData->isServer == true); + + // If the list is empty, return. + if (pSocketData->acceptedSocketsHead == pSocketData->acceptedSocketsTail) { + // Return -1 if there is no new client socket for this server. + return -1; + } + + // Return the 1st socket id that is in the list of accepted sockets. We also update the + // list to indicate that it has been read. + int acceptedSocketId = pSocketData->acceptedSockets[pSocketData->acceptedSocketsTail]; + pSocketData->acceptedSocketsTail = (pSocketData->acceptedSocketsTail + 1) % MAX_ACCEPTED_SOCKETS; + + os_printf("> net_ESP8266_BOARD_accept: Accepted a new socket, socketId=%d\n", acceptedSocketId); + return acceptedSocketId; +} // End of net_ESP8266_BOARD_accept + + +/** + * \brief Receive data from the network device. + * Returns the number of bytes received which may be 0 and -1 if there was an error. + */ +int net_ESP8266_BOARD_recv( + JsNetwork *net, //!< The Network we are going to use to create the socket. + int sckt, //!< The socket from which we are to receive data. + void *buf, //!< The storage buffer into which we will receive data. + size_t len //!< The length of the buffer. + ) { + assert(sckt >=0 && scktstate != SOCKET_STATE_UNUSED); + + // If there is no data in the receive buffer, then all we need do is return + // 0 bytes as the length of data moved. + if (pSocketData->rxBufLen == 0) { + if (pSocketData->state == SOCKET_STATE_CLOSING) { + return -1; + } + return 0; + } + + // If the receive buffer contains data and is it is able to fit in the buffer + // passed into us then we can copy all the data and the receive buffer will be clear. + if (pSocketData->rxBufLen <= len) { + memcpy(buf, pSocketData->rxBuf, pSocketData->rxBufLen); + int retLen = pSocketData->rxBufLen; + os_free(pSocketData->rxBuf); + pSocketData->rxBufLen = 0; + pSocketData->rxBuf = NULL; + return retLen; + } + + // If we are here, then we have more data in the receive buffer than is available + // to be returned in this request for data. So we have to copy the amount of data + // that is allowed to be returned and then strip that from the beginning of the + // receive buffer. + + // First we copy the data we are going to return. + memcpy(buf, pSocketData->rxBuf, len); + + // Next we allocate a new buffer and copy in the data we are not returning. + uint8 *pNewBuf = (uint8 *)os_malloc(pSocketData->rxBufLen-len); + memcpy(pNewBuf, pSocketData->rxBuf + len, pSocketData->rxBufLen-len); + + // Now we juggle pointers and release the original RX buffer now that we have a new + // one. It is likely that this algorithm can be significantly improved since there + // is a period of time where we might actuall have TWO copies of the data. + uint8 *pTemp = pSocketData->rxBuf; + pSocketData->rxBuf = pNewBuf; + pSocketData->rxBufLen = pSocketData->rxBufLen-len; + os_free(pTemp); + + return len; +} // End of net_ESP8266_BOARD_recv. + + +/** + * \brief Send data to the partner. + * The return is the number of bytes actually transmitted which may also be + * 0 to indicate no bytes sent or -1 to indicate an error. For the ESP8266 implementation we + * will return 0 if the socket is not connected or we are in the `SOCKET_STATE_TRANSMITTING` + * state. + */ +int net_ESP8266_BOARD_send( + JsNetwork *net, //!< The Network we are going to use to create the socket. + int sckt, //!< The socket over which we will send data. + const void *buf, //!< The buffer containing the data to be sent. + size_t len //!< The length of data in the buffer to send. + ) { + assert(sckt >=0 && sckt net_ESP8266_BOARD_send: Request to send data to socket %d of size %d: ", sckt, len); + + struct socketData *pSocketData = getSocketData(sckt); + assert(pSocketData->state != SOCKET_STATE_UNUSED); + + // If we are not connected, then we can't currently send data. + if (pSocketData->isConnected == false) { + os_printf(" - Not connected\n"); + return 0; + } + + // If we are currently sending data, we can't send more. + if (pSocketData->state == SOCKET_STATE_TRANSMITTING) { + os_printf(" - Currently transmitting\n"); + return 0; + } + + // Log the content of the data we are sening. + esp8266_board_writeString(buf, len); + + os_printf("\n"); + + assert(pSocketData->state == SOCKET_STATE_IDLE); + + pSocketData->state = SOCKET_STATE_TRANSMITTING; + + // Copy the data that was passed to us to a private area. We do this because we must not + // assume that the data passed in will be available after this function returns. It may have + // been passed in on the stack. + assert(pSocketData->currentTx == NULL); + pSocketData->currentTx = (uint8_t *)os_malloc(len); + memcpy(pSocketData->currentTx, buf, len); + + // Send the data over the ESP8266 SDK. + int rc = espconn_send(pSocketData->pEspconn, pSocketData->currentTx, len); + if (rc < 0) { + setSocketInError(sckt, "espconn_send", rc); + os_free(pSocketData->currentTx); + pSocketData->currentTx = NULL; + return -1; + } + + esp8266_dumpSocket(sckt); + os_printf("< net_ESP8266_BOARD_send\n"); + return len; +} // End of net_ESP8266_BOARD_send + + +/** + * \brief Perform idle processing. + * There is the possibility that we may wish to perform logic when we are idle. For the + * ESP8266 there is no specific idle network processing needed. + */ +void net_ESP8266_BOARD_idle( + JsNetwork *net //!< The Network we are part of. + ) { + // Don't echo here because it is called continuously + //os_printf("> net_ESP8266_BOARD_idle\n"); +} // End of net_ESP8266_BOARD_idle + + +/** + * \brief Check for errors. + * Returns true if there are NO errors. + */ +bool net_ESP8266_BOARD_checkError( + JsNetwork *net //!< The Network we are going to use to create the socket. + ) { + //os_printf("> net_ESP8266_BOARD_checkError\n"); + return true; +} // End of net_ESP8266_BOARD_checkError + + +/** + * \brief Create a new socket. + * if `ipAddress == 0`, creates a server otherwise creates a client (and automatically connects). Returns >=0 on success. + */ +int net_ESP8266_BOARD_createSocket( + JsNetwork *net, //!< The Network we are going to use to create the socket. + uint32_t ipAddress, //!< The address of the partner of the socket or 0 if we are to be a server. + unsigned short port //!< The port number that the partner is listening upon. + ) { + os_printf("> net_ESP8266_BOARD_createSocket: host: %d.%d.%d.%d, port:%d \n", ((char *)(&ipAddress))[0], ((char *)(&ipAddress))[1], ((char *)(&ipAddress))[2], ((char *)(&ipAddress))[3], port); + + bool isServer = (ipAddress == 0); + + int sckt = getNextFreeSocket(); + if (sckt < 0) { // No free socket + os_printf("< net_ESP8266_BOARD_createSocket: No free sockets\n"); + return -1; + } + + struct socketData *pSocketData = getSocketData(sckt); + struct espconn *pEspconn = pSocketData->pEspconn; + + espconn_regist_disconcb(pEspconn, esp8266_callback_disconnectCB); + espconn_regist_reconcb(pEspconn, esp8266_callback_reconnectCB); + espconn_regist_sentcb(pEspconn, esp8266_callback_sentCB); + espconn_regist_recvcb(pEspconn, esp8266_callback_recvCB); + espconn_regist_write_finish(pEspconn, esp8266_callback_writeFinishedCB); + + struct ip_info ipconfig; + wifi_get_ip_info(STATION_IF, &ipconfig); // Get the local IP address + os_memcpy(pEspconn->proto.tcp->local_ip, &ipconfig.ip, 4); + pEspconn->type = ESPCONN_TCP; + pEspconn->state = ESPCONN_NONE; + pEspconn->proto.tcp = &tcpArray[sckt]; + pEspconn->reverse = pSocketData; + + // If ipAddress != 0 then make a client connection + if (isServer == false) { + pSocketData->state = SOCKET_STATE_CONNECTING; + pSocketData->isServer = false; + pEspconn->proto.tcp->remote_port = port; + pEspconn->proto.tcp->local_port = espconn_port(); + + *(uint32 *)(pEspconn->proto.tcp->remote_ip) = ipAddress; + + // Ensure that we have flagged this socket as NOT connected + pSocketData->isConnected = false; + + espconn_regist_connectcb(pEspconn, esp8266_callback_connectCB_outbound); + + // Make a call to espconn_connect. + int rc = espconn_connect(pEspconn); + if (rc != 0) { + os_printf("Err: net_ESP8266_BOARD_createSocket -> espconn_connect returned: %d. Using local port: %d\n", rc, pEspconn->proto.tcp->local_port); + setSocketInError(sckt, "espconn_connect", rc); + } + /* + os_printf("Checking to see if we are connected ...\n"); + uint32 clock= system_get_time(); + while(pSocketData->isConnected == false) { + system_soft_wdt_feed(); + if ((system_get_time() - clock) > 10000000) { + os_printf("clock %d\n", (unit32)clock); + clock = system_get_time(); + } + } + */ + } + // If the ipAddress IS 0 ... then we are a server. + else + { + pSocketData->state = SOCKET_STATE_IDLE; + pSocketData->isServer = true; + // We are going to set ourselves up as a server + pEspconn->proto.tcp->local_port = port; + + espconn_regist_connectcb(pEspconn, esp8266_callback_connectCB_inbound); + + // Make a call to espconn_accept + int rc = espconn_accept(pEspconn); + if (rc != 0) { + os_printf("Err: net_ESP8266_BOARD_createSocket -> espconn_accept returned: %d. Using local port: %d\n", rc, pEspconn->proto.tcp->local_port); + setSocketInError(sckt, "espconn_accept", rc); + } + } // End of + + dumpEspConn(pEspconn); + os_printf("< net_ESP8266_BOARD_createSocket, socket=%d\n", sckt); + return sckt; +} // End of net_ESP8266_BOARD_createSocket + + +/** + * \brief Close a socket. + */ +void net_ESP8266_BOARD_closeSocket( + JsNetwork *net, //!< The Network we are going to use to create the socket. + int socketId //!< The socket to be closed. + ) { + os_printf("> net_ESP8266_BOARD_closeSocket, socket=%d\n", socketId); + + assert(socketId >=0 && socketIdstate != SOCKET_STATE_UNUSED); // Shouldn't be closing an unused socket. + + dumpEspConn(pSocketData->pEspconn); + esp8266_dumpSocket(socketId); + + // How we close the socket is a function of what kind of socket it is. + if (pSocketData->isServer == true) { + int rc = espconn_delete(pSocketData->pEspconn); + if (rc != 0) { + os_printf("espconn_delete: rc=%d\n", rc); + } + } // End this is a server socket + else + { + if (pSocketData->state == SOCKET_STATE_IDLE || pSocketData->state == SOCKET_STATE_CLOSING) { + doClose(socketId); + } else { + pSocketData->shouldClose = true; + } + } // End this is a client socket +} // End of net_ESP8266_BOARD_closeSocket + + +/** + * \brief Get an IP address from a name. + * Sets 'outIp' to 0 on failure. + */ +void net_ESP8266_BOARD_gethostbyname( + JsNetwork *net, //!< The Network we are going to use to create the socket. + char *hostName, //!< The string representing the hostname we wish to lookup. + uint32_t *outIp //!< The address into which the resolved IP address will be stored. + ) { + os_printf("> net_ESP8266_BOARD_gethostbyname\n"); + *outIp = 0x00000000; +} // End of net_ESP8266_BOARD_gethostbyname + + +// ---------------------------------------------------------------- + +/** + * The following section is all about a logical concept called a memoryBuffer. This is an + * abstract data type that contains data in memory. The operations we can perform upon it + * are: + + * o memoryBuffer_append - Append data to the end of the memory buffer. + * o memoryBuffer_read - Read a fixed number of bytes from the memory buffer. + * o memoryBuffer_delete - Delete the memory buffer. No further operations should be performed + * against it. + * o memoryBuffer_getSize - Get the size of data contained in the memory buffer. + */ + +/** + * \brief Delete all content of the memory buffer. + */ +static void memoryBuffer_delete( + struct memoryBuffer *pMemoryBuffer //!< + ) { + if (pMemoryBuffer->length > 0) { + os_free(pMemoryBuffer->buf); + pMemoryBuffer->buf = NULL; + pMemoryBuffer->length = 0; + } +} // End of memoryBuffer_delete + + +/** + * \brief Append new data to the end of the existing memory buffer. + */ +static uint8 *memoryBuffer_append( + struct memoryBuffer *pMemoryBuffer, //!< + uint8 *pNewData, //!< + size_t length //!< + ) { + assert(pMemoryBuffer != NULL); + + if (length == 0) { + return pMemoryBuffer->buf; + } + + assert(pNewData != NULL); + + // Handle the memory buffer being empty. + if (pMemoryBuffer->length == 0) { + pMemoryBuffer->buf = (uint8 *)os_malloc(length); + if (pMemoryBuffer->buf == NULL) { // Out of memory + jsError("malloc failed at memoryBuffer_append trying to allocate %d", length); + } else { + memcpy(pMemoryBuffer->buf, pNewData, length); + pMemoryBuffer->length = length; + } + } else { + // The memory buffer was not empty, so we append data. + int newSize = pMemoryBuffer->length + length; + uint8 *resizedStorage = (uint8 *)os_realloc(pMemoryBuffer->buf, newSize); + if (resizedStorage != NULL) { + pMemoryBuffer->buf = resizedStorage; + memcpy(pMemoryBuffer->buf + length, pNewData, length); + pMemoryBuffer->length = newSize; + } + } + return pMemoryBuffer->buf; +} // End of memoryBuffer_append + + +/** + * \brief Return how much data is stored in the memory buffer. + */ +static int memoryBuffer_getSize( + struct memoryBuffer *pMemoryBuffer //!< + ) { + assert(pMemoryBuffer != NULL); + return pMemoryBuffer->length; +} // End of memoryBuffer_getSize + + +/** + * \brief Read data from the memory buffer of an exact size. + * The data that is returned + * should be released with an os_free() call. + */ +static uint8 *memoryBuffer_read( + struct memoryBuffer *pMemoryBuffer, //!< + size_t readSize //!< + ) { + assert(pMemoryBuffer != NULL); + assert((pMemoryBuffer->length > 0 && pMemoryBuffer->buf != NULL) || pMemoryBuffer->length == 0); + + // Check that we are NOT trying to read more data than we actually have available to us. + assert(readSize > pMemoryBuffer->length); + + // Handle the case where we are trying to read 0 bytes. + if (readSize == 0) { + return NULL; + } + + // If the size of data we are willing to read is EXACTLY the size of the buffer we + // have, then simply return a pointer to the buffer and we are done. + if (readSize == pMemoryBuffer->length) { + uint8 *pTemp = pMemoryBuffer->buf; + pMemoryBuffer->buf = NULL; + pMemoryBuffer->length = 0; + return pTemp; + } + + // We can assert that size < memory buffer length. + // + // Here we have determined that we wish to consume LESS data that we have available. + // That means we have to split our data into parts. + + // First we build the data that we will return and copy in the memory buffer data. + uint8 *pTemp = (uint8 *)os_malloc(readSize); + if (pTemp == NULL) { // Out of memory + jsError("malloc failed at memoryBuffer_append trying to allocate %d", readSize); + return NULL; + } + os_memcpy(pTemp, pMemoryBuffer->buf, readSize); + + // Now we create a memory buffer to hold the remaining data that was not + // returned. + int newSize = pMemoryBuffer->length - readSize; + uint8 *pTemp2 = (uint8 *)os_malloc(newSize); + os_memcpy(pTemp2, pMemoryBuffer->buf + readSize, newSize); + os_free(pMemoryBuffer->buf); + pMemoryBuffer->buf = pTemp2; + pMemoryBuffer->length = newSize; + return pTemp; +} // End of memoryBuffer_read +// End of file diff --git a/libs/network/esp8266/network_esp8266.h b/libs/network/esp8266/network_esp8266.h new file mode 100644 index 000000000..84d32f03e --- /dev/null +++ b/libs/network/esp8266/network_esp8266.h @@ -0,0 +1,23 @@ +/* + * network_esp8266_board.h + * + * Created on: Aug 29, 2015 + * Author: kolban + */ + +#ifndef LIBS_NETWORK_ESP8266_NETWORK_ESP8266_H_ +#define LIBS_NETWORK_ESP8266_NETWORK_ESP8266_H_ + +#include "network.h" +void netInit_esp8266_board(); +void netSetCallbacks_esp8266_board(JsNetwork *net); +void esp8266_dumpSocket(int socketId); +int net_ESP8266_BOARD_accept(JsNetwork *net, int serverSckt); +int net_ESP8266_BOARD_recv(JsNetwork *net, int sckt, void *buf, size_t len); +int net_ESP8266_BOARD_send(JsNetwork *net, int sckt, const void *buf, size_t len); +void net_ESP8266_BOARD_idle(JsNetwork *net); +bool net_ESP8266_BOARD_checkError(JsNetwork *net); +int net_ESP8266_BOARD_createSocket(JsNetwork *net, uint32_t ipAddress, unsigned short port); +void net_ESP8266_BOARD_closeSocket(JsNetwork *net, int sckt); +void net_ESP8266_BOARD_gethostbyname(JsNetwork *net, char *hostName, uint32_t *outIp); +#endif /* LIBS_NETWORK_ESP8266_NETWORK_ESP8266_H_ */ diff --git a/targets/esp8266/ESP8266_board.h b/targets/esp8266/ESP8266_board.h new file mode 100644 index 000000000..e72e7481c --- /dev/null +++ b/targets/esp8266/ESP8266_board.h @@ -0,0 +1,17 @@ +/* + * ESP8266_board.h + * + * Created on: Aug 25, 2015 + * Author: kolban + */ + +#ifndef TARGETS_ESP8266_ESP8266_BOARD_H_ +#define TARGETS_ESP8266_ESP8266_BOARD_H_ +#include +// Define the task ids for the APP event handler +#define TASK_APP_MAINLOOP ((os_signal_t)1) +#define TASK_APP_RX_DATA ((os_signal_t)2) +#define TASK_APP_QUEUE USER_TASK_PRIO_1 + + +#endif /* TARGETS_ESP8266_ESP8266_BOARD_H_ */ diff --git a/targets/esp8266/docs/ESP8266.md b/targets/esp8266/docs/ESP8266.md new file mode 100644 index 000000000..232da60b2 --- /dev/null +++ b/targets/esp8266/docs/ESP8266.md @@ -0,0 +1,2 @@ +# ESP8266 +This is where documentation on the ESP8266 target will be provided. \ No newline at end of file diff --git a/targets/esp8266/driver/uart.h b/targets/esp8266/driver/uart.h new file mode 100644 index 000000000..552720471 --- /dev/null +++ b/targets/esp8266/driver/uart.h @@ -0,0 +1,226 @@ +/* + * File : uart.h + * Copyright (C) 2013 - 2016, Espressif Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ +#ifndef UART_APP_H +#define UART_APP_H + +#include "uart_register.h" +#include "eagle_soc.h" +#include "c_types.h" + + +int getRXBuffer(char *pBuffer, int bufferLen); +int uart_rx_discard(); + + +#define UART_TX_BUFFER_SIZE 256 //Ring buffer length of tx buffer +#define UART_RX_BUFFER_SIZE 256 //Ring buffer length of rx buffer + +#define UART_BUFF_EN 0 //use uart buffer , FOR UART0 +#define UART_SELFTEST 0 //set 1:enable the loop test demo for uart buffer, FOR UART0 + +#define UART_HW_RTS 0 //set 1: enable uart hw flow control RTS, PIN MTDO, FOR UART0 +#define UART_HW_CTS 0 //set1: enable uart hw flow contrl CTS , PIN MTCK, FOR UART0 + + + + +#define UART0 0 +#define UART1 1 + + +typedef enum { + FIVE_BITS = 0x0, + SIX_BITS = 0x1, + SEVEN_BITS = 0x2, + EIGHT_BITS = 0x3 +} UartBitsNum4Char; + +typedef enum { + ONE_STOP_BIT = 0x1, + ONE_HALF_STOP_BIT = 0x2, + TWO_STOP_BIT = 0x3 +} UartStopBitsNum; + +typedef enum { + NONE_BITS = 0x2, + ODD_BITS = 1, + EVEN_BITS = 0 +} UartParityMode; + +typedef enum { + STICK_PARITY_DIS = 0, + STICK_PARITY_EN = 1 +} UartExistParity; + +typedef enum { + UART_None_Inverse = 0x0, + UART_Rxd_Inverse = UART_RXD_INV, + UART_CTS_Inverse = UART_CTS_INV, + UART_Txd_Inverse = UART_TXD_INV, + UART_RTS_Inverse = UART_RTS_INV, +} UART_LineLevelInverse; + + +typedef enum { + BIT_RATE_300 = 300, + BIT_RATE_600 = 600, + BIT_RATE_1200 = 1200, + BIT_RATE_2400 = 2400, + BIT_RATE_4800 = 4800, + BIT_RATE_9600 = 9600, + BIT_RATE_19200 = 19200, + BIT_RATE_38400 = 38400, + BIT_RATE_57600 = 57600, + BIT_RATE_74880 = 74880, + BIT_RATE_115200 = 115200, + BIT_RATE_230400 = 230400, + BIT_RATE_460800 = 460800, + BIT_RATE_921600 = 921600, + BIT_RATE_1843200 = 1843200, + BIT_RATE_3686400 = 3686400, +} UartBautRate; + +typedef enum { + NONE_CTRL, + HARDWARE_CTRL, + XON_XOFF_CTRL +} UartFlowCtrl; + +typedef enum { + USART_HardwareFlowControl_None = 0x0, + USART_HardwareFlowControl_RTS = 0x1, + USART_HardwareFlowControl_CTS = 0x2, + USART_HardwareFlowControl_CTS_RTS = 0x3 +} UART_HwFlowCtrl; + +typedef enum { + EMPTY, + UNDER_WRITE, + WRITE_OVER +} RcvMsgBuffState; + +typedef struct { + uint32 RcvBuffSize; + uint8 *pRcvMsgBuff; + uint8 *pWritePos; + uint8 *pReadPos; + uint8 TrigLvl; //JLU: may need to pad + RcvMsgBuffState BuffState; +} RcvMsgBuff; + +typedef struct { + uint32 TrxBuffSize; + uint8 *pTrxBuff; +} TrxMsgBuff; + +typedef enum { + BAUD_RATE_DET, + WAIT_SYNC_FRM, + SRCH_MSG_HEAD, + RCV_MSG_BODY, + RCV_ESC_CHAR, +} RcvMsgState; + +typedef struct { + UartBautRate baut_rate; + UartBitsNum4Char data_bits; + UartExistParity exist_parity; + UartParityMode parity; + UartStopBitsNum stop_bits; + UartFlowCtrl flow_ctrl; + RcvMsgBuff rcv_buff; + TrxMsgBuff trx_buff; + RcvMsgState rcv_state; + int received; + int buff_uart_no; //indicate which uart use tx/rx buffer +} UartDevice; + +void uart_init(UartBautRate uart0_br, UartBautRate uart1_br); +void uart0_sendStr(const char *str); + + +/////////////////////////////////////// +#define UART_FIFO_LEN 128 //define the tx fifo length +#define UART_TX_EMPTY_THRESH_VAL 0x10 + + + struct UartBuffer{ + uint32 UartBuffSize; + uint8 *pUartBuff; + uint8 *pInPos; + uint8 *pOutPos; + STATUS BuffState; + uint16 Space; //remanent space of the buffer + uint8 TcpControl; + struct UartBuffer * nextBuff; +}; + +struct UartRxBuff{ + uint32 UartRxBuffSize; + uint8 *pUartRxBuff; + uint8 *pWritePos; + uint8 *pReadPos; + STATUS RxBuffState; + uint32 Space; //remanent space of the buffer +} ; + +typedef enum { + RUN = 0, + BLOCK = 1, +} TCPState; + +//void ICACHE_FLASH_ATTR uart_test_rx(); +STATUS uart_tx_one_char(uint8 uart, uint8 TxChar); +STATUS uart_tx_one_char_no_wait(uint8 uart, uint8 TxChar); +void uart1_sendStr_no_wait(const char *str); +struct UartBuffer* Uart_Buf_Init(); + + +#if UART_BUFF_EN +LOCAL void Uart_Buf_Cpy(struct UartBuffer* pCur, char* pdata , uint16 data_len); +void uart_buf_free(struct UartBuffer* pBuff); +void tx_buff_enq(char* pdata, uint16 data_len ); +LOCAL void tx_fifo_insert(struct UartBuffer* pTxBuff, uint8 data_len, uint8 uart_no); +void tx_start_uart_buffer(uint8 uart_no); +uint16 rx_buff_deq(char* pdata, uint16 data_len ); +void Uart_rx_buff_enq(); +#endif +void uart_rx_intr_enable(uint8 uart_no); +void uart_rx_intr_disable(uint8 uart_no); +void uart0_tx_buffer(uint8 *buf, uint16 len); + +//============================================== +#define FUNC_UART0_CTS 4 +#define FUNC_U0CTS 4 +#define FUNC_U1TXD_BK 2 +#define UART_LINE_INV_MASK (0x3f<<19) +void UART_SetWordLength(uint8 uart_no, UartBitsNum4Char len); +void UART_SetStopBits(uint8 uart_no, UartStopBitsNum bit_num); +void UART_SetLineInverse(uint8 uart_no, UART_LineLevelInverse inverse_mask); +void UART_SetParity(uint8 uart_no, UartParityMode Parity_mode); +void UART_SetBaudrate(uint8 uart_no,uint32 baud_rate); +void UART_SetFlowCtrl(uint8 uart_no,UART_HwFlowCtrl flow_ctrl,uint8 rx_thresh); +void UART_WaitTxFifoEmpty(uint8 uart_no , uint32 time_out_us); //do not use if tx flow control enabled +void UART_ResetFifo(uint8 uart_no); +void UART_ClearIntrStatus(uint8 uart_no,uint32 clr_mask); +void UART_SetIntrEna(uint8 uart_no,uint32 ena_mask); +void UART_SetPrintPort(uint8 uart_no); +bool UART_CheckOutputFinished(uint8 uart_no, uint32 time_out_us); +//============================================== + +#endif + diff --git a/targets/esp8266/driver/uart_register.h b/targets/esp8266/driver/uart_register.h new file mode 100644 index 000000000..05f6ceaf2 --- /dev/null +++ b/targets/esp8266/driver/uart_register.h @@ -0,0 +1,156 @@ +/* + * File : uart_register.h + * Copyright (C) 2013 - 2016, Espressif Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ +/* + * Copyright (c) 2010 - 2011 Espressif System + * + */ + +#ifndef UART_REGISTER_H_ +#define UART_REGISTER_H_ + +#define REG_UART_BASE(i) (0x60000000 + (i)*0xf00) +//version value:32'h062000 + +#define UART_FIFO(i) (REG_UART_BASE(i) + 0x0) +#define UART_RXFIFO_RD_BYTE 0x000000FF +#define UART_RXFIFO_RD_BYTE_S 0 + +#define UART_INT_RAW(i) (REG_UART_BASE(i) + 0x4) +#define UART_RXFIFO_TOUT_INT_RAW (BIT(8)) +#define UART_BRK_DET_INT_RAW (BIT(7)) +#define UART_CTS_CHG_INT_RAW (BIT(6)) +#define UART_DSR_CHG_INT_RAW (BIT(5)) +#define UART_RXFIFO_OVF_INT_RAW (BIT(4)) +#define UART_FRM_ERR_INT_RAW (BIT(3)) +#define UART_PARITY_ERR_INT_RAW (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_RAW (BIT(1)) +#define UART_RXFIFO_FULL_INT_RAW (BIT(0)) + +#define UART_INT_ST(i) (REG_UART_BASE(i) + 0x8) +#define UART_RXFIFO_TOUT_INT_ST (BIT(8)) +#define UART_BRK_DET_INT_ST (BIT(7)) +#define UART_CTS_CHG_INT_ST (BIT(6)) +#define UART_DSR_CHG_INT_ST (BIT(5)) +#define UART_RXFIFO_OVF_INT_ST (BIT(4)) +#define UART_FRM_ERR_INT_ST (BIT(3)) +#define UART_PARITY_ERR_INT_ST (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_ST (BIT(1)) +#define UART_RXFIFO_FULL_INT_ST (BIT(0)) + +#define UART_INT_ENA(i) (REG_UART_BASE(i) + 0xC) +#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) +#define UART_BRK_DET_INT_ENA (BIT(7)) +#define UART_CTS_CHG_INT_ENA (BIT(6)) +#define UART_DSR_CHG_INT_ENA (BIT(5)) +#define UART_RXFIFO_OVF_INT_ENA (BIT(4)) +#define UART_FRM_ERR_INT_ENA (BIT(3)) +#define UART_PARITY_ERR_INT_ENA (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) +#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) + +#define UART_INT_CLR(i) (REG_UART_BASE(i) + 0x10) +#define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) +#define UART_BRK_DET_INT_CLR (BIT(7)) +#define UART_CTS_CHG_INT_CLR (BIT(6)) +#define UART_DSR_CHG_INT_CLR (BIT(5)) +#define UART_RXFIFO_OVF_INT_CLR (BIT(4)) +#define UART_FRM_ERR_INT_CLR (BIT(3)) +#define UART_PARITY_ERR_INT_CLR (BIT(2)) +#define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) +#define UART_RXFIFO_FULL_INT_CLR (BIT(0)) + +#define UART_CLKDIV(i) (REG_UART_BASE(i) + 0x14) +#define UART_CLKDIV_CNT 0x000FFFFF +#define UART_CLKDIV_S 0 + +#define UART_AUTOBAUD(i) (REG_UART_BASE(i) + 0x18) +#define UART_GLITCH_FILT 0x000000FF +#define UART_GLITCH_FILT_S 8 +#define UART_AUTOBAUD_EN (BIT(0)) + +#define UART_STATUS(i) (REG_UART_BASE(i) + 0x1C) +#define UART_TXD (BIT(31)) +#define UART_RTSN (BIT(30)) +#define UART_DTRN (BIT(29)) +#define UART_TXFIFO_CNT 0x000000FF +#define UART_TXFIFO_CNT_S 16 +#define UART_RXD (BIT(15)) +#define UART_CTSN (BIT(14)) +#define UART_DSRN (BIT(13)) +#define UART_RXFIFO_CNT 0x000000FF +#define UART_RXFIFO_CNT_S 0 + +#define UART_CONF0(i) (REG_UART_BASE(i) + 0x20) +#define UART_DTR_INV (BIT(24)) +#define UART_RTS_INV (BIT(23)) +#define UART_TXD_INV (BIT(22)) +#define UART_DSR_INV (BIT(21)) +#define UART_CTS_INV (BIT(20)) +#define UART_RXD_INV (BIT(19)) +#define UART_TXFIFO_RST (BIT(18)) +#define UART_RXFIFO_RST (BIT(17)) +#define UART_IRDA_EN (BIT(16)) +#define UART_TX_FLOW_EN (BIT(15)) +#define UART_LOOPBACK (BIT(14)) +#define UART_IRDA_RX_INV (BIT(13)) +#define UART_IRDA_TX_INV (BIT(12)) +#define UART_IRDA_WCTL (BIT(11)) +#define UART_IRDA_TX_EN (BIT(10)) +#define UART_IRDA_DPLX (BIT(9)) +#define UART_TXD_BRK (BIT(8)) +#define UART_SW_DTR (BIT(7)) +#define UART_SW_RTS (BIT(6)) +#define UART_STOP_BIT_NUM 0x00000003 +#define UART_STOP_BIT_NUM_S 4 +#define UART_BIT_NUM 0x00000003 +#define UART_BIT_NUM_S 2 +#define UART_PARITY_EN (BIT(1)) +#define UART_PARITY_EN_M 0x00000001 +#define UART_PARITY_EN_S 1 +#define UART_PARITY (BIT(0)) +#define UART_PARITY_M 0x00000001 +#define UART_PARITY_S 0 + +#define UART_CONF1(i) (REG_UART_BASE(i) + 0x24) +#define UART_RX_TOUT_EN (BIT(31)) +#define UART_RX_TOUT_THRHD 0x0000007F +#define UART_RX_TOUT_THRHD_S 24 +#define UART_RX_FLOW_EN (BIT(23)) +#define UART_RX_FLOW_THRHD 0x0000007F +#define UART_RX_FLOW_THRHD_S 16 +#define UART_TXFIFO_EMPTY_THRHD 0x0000007F +#define UART_TXFIFO_EMPTY_THRHD_S 8 +#define UART_RXFIFO_FULL_THRHD 0x0000007F +#define UART_RXFIFO_FULL_THRHD_S 0 + +#define UART_LOWPULSE(i) (REG_UART_BASE(i) + 0x28) +#define UART_LOWPULSE_MIN_CNT 0x000FFFFF +#define UART_LOWPULSE_MIN_CNT_S 0 + +#define UART_HIGHPULSE(i) (REG_UART_BASE(i) + 0x2C) +#define UART_HIGHPULSE_MIN_CNT 0x000FFFFF +#define UART_HIGHPULSE_MIN_CNT_S 0 + +#define UART_PULSE_NUM(i) (REG_UART_BASE(i) + 0x30) +#define UART_PULSE_NUM_CNT 0x0003FF +#define UART_PULSE_NUM_CNT_S 0 + +#define UART_DATE(i) (REG_UART_BASE(i) + 0x78) +#define UART_ID(i) (REG_UART_BASE(i) + 0x7C) + +#endif // UART_REGISTER_H_INCLUDED + diff --git a/targets/esp8266/eagle.app.v6.0x10000.ld b/targets/esp8266/eagle.app.v6.0x10000.ld new file mode 100644 index 000000000..1157155a6 --- /dev/null +++ b/targets/esp8266/eagle.app.v6.0x10000.ld @@ -0,0 +1,211 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40210000, len = 0x60000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.sdk.version) + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/targets/esp8266/esp8266_board_utils.c b/targets/esp8266/esp8266_board_utils.c new file mode 100644 index 000000000..72e5ba547 --- /dev/null +++ b/targets/esp8266/esp8266_board_utils.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define _GCC_WRAP_STDINT_H +typedef long long int64_t; + +#include "jsutils.h" + +/** + * \brief Convert an ESP8266 error code to a string. + * Given an ESP8266 network error code, return a string representation + * of the meaning of that code. + * \return A string representation of an error code. + */ +const char *esp8266_errorToString( + sint8 err //!< The error code to be transformed to a string. + ) { + switch(err) { + case ESPCONN_MEM: + return "ESPCONN_MEM"; + case ESPCONN_TIMEOUT: + return "ESPCONN_TIMEOUT"; + case ESPCONN_RTE: + return "ESPCONN_RTE"; + case ESPCONN_INPROGRESS: + return "ESPCONN_INPROGRESS"; + case ESPCONN_ABRT: + return "ESPCONN_ABRT"; + case ESPCONN_RST: + return "ESPCONN_RST"; + case ESPCONN_CLSD: + return "ESPCONN_CLSD"; + case ESPCONN_CONN: + return "ESPCONN_CONN"; + case ESPCONN_ARG: + return "ESPCONN_ARG"; + case ESPCONN_ISCONN: + return "ESPCONN_ISCONN"; + case ESPCONN_HANDSHAKE: + return "ESPCONN_HANDSHAKE"; + default: + return "Unknown error"; + } +} // End of errorToString + + +/** + * \brief Write a buffer of data to the console. + * The buffer is pointed to by the buffer + * parameter and will be written for the length parameter. This is useful because + * unlike a string, the data does not have to be NULL terminated. + */ +void esp8266_board_writeString( + uint8 *buffer, //!< The start of the buffer to write. + size_t length //!< The length of the buffer to write. + ) { + assert(length==0 || buffer != NULL); + + for (size_t i=0; i +#include + +//Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. +//MOST OF THESE ARE GUESSED! but they seem to work and shut up the compiler. +typedef struct espconn espconn; + +bool wifi_station_set_hostname(char *); +char *wifi_station_get_hostname(void); + +int atoi(const char *nptr); + +void ets_install_putc1(void *routine); // necessary for #define os_xxx -> ets_xxx +void ets_isr_attach(int intr, void *handler, void *arg); +void ets_isr_mask(unsigned intr); +void ets_isr_unmask(unsigned intr); + +int ets_memcmp(const void *s1, const void *s2, size_t n); +void *ets_memcpy(void *dest, const void *src, size_t n); +void *ets_memset(void *s, int c, size_t n); +int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3))); +int ets_str2macaddr(void *, void *); +int ets_strcmp(const char *s1, const char *s2); +char *ets_strcpy(char *dest, const char *src); +size_t ets_strlen(const char *s); +int ets_strncmp(const char *s1, const char *s2, int len); +char *ets_strncpy(char *dest, const char *src, size_t n); +char *ets_strstr(const char *haystack, const char *needle); + +void ets_timer_arm_new(ETSTimer *a, int b, int c, int isMstimer); +void ets_timer_disarm(ETSTimer *a); +void ets_timer_setfn(ETSTimer *t, ETSTimerFunc *fn, void *parg); + +void ets_update_cpu_frequency(int freqmhz); + +#ifdef SDK_DBG +#define DEBUG_SDK true +#else +#define DEBUG_SDK false +#endif + +int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__((format(printf, 3, 4))); +int os_printf_plus(const char *format, ...) __attribute__((format(printf, 1, 2))); + +#undef os_printf +#define os_printf(format, ...) do {\ + system_set_os_print(true); \ + os_printf_plus(format, ## __VA_ARGS__); \ + system_set_os_print(DEBUG_SDK); \ +} while(0) + +// memory allocation functions are "different" due to memory debugging functionality +// added in SDK 1.4.0 +void vPortFree(void *ptr, char * file, int line); +void *pvPortMalloc(size_t xWantedSize, char * file, int line); +void *pvPortZalloc(size_t, char * file, int line); +void *vPortMalloc(size_t xWantedSize); +void pvPortFree(void *ptr); +void *pvPortRealloc(void *pv, size_t size, char * file, int line); + +void uart_div_modify(int no, unsigned int freq); +uint32 system_get_time(); +int rand(void); +void ets_bzero(void *s, size_t n); +void ets_delay_us(int ms); + +// disappeared in SDK 1.1.0: +#define os_timer_done ets_timer_done +#define os_timer_handler_isr ets_timer_handler_isr +#define os_timer_init ets_timer_init + +// This is not missing in SDK 1.1.0 but causes a parens error +#undef PIN_FUNC_SELECT +#define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \ + WRITE_PERI_REG(PIN_NAME, \ + (READ_PERI_REG(PIN_NAME) & ~(PERIPHS_IO_MUX_FUNC< +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define FAKE_STDLIB +#define _GCC_WRAP_STDINT_H +typedef long long int64_t; + +#include "jshardware.h" +#include "jsutils.h" +#include "jsparse.h" +#include "jsinteractive.h" + +// The maximum time that we can safely delay/block without risking a watch dog +// timer error or other undesirable WiFi interaction. The time is measured in +// microseconds. +#define MAX_SLEEP_TIME_US 10000 + +/** + * Transmit all the characters in the transmit buffer. + * + */ +void esp8266_uartTransmitAll(IOEventFlags device) { + // Get the next character to transmit. We will have reached the end when + // the value of the character to transmit is -1. + int c = jshGetCharToTransmit(device); + + while (c >= 0) { + uart_tx_one_char(0, c); + c = jshGetCharToTransmit(device); + } // No more characters to transmit +} // End of esp8266_transmitAll + +// ---------------------------------------------------------------------------- + +IOEventFlags pinToEVEXTI(Pin pin) { + return (IOEventFlags) 0; +} + +/** + * Initialize the ESP8266 hardware environment. + */ +void jshInit() { + // A call to jshInitDevices is architected as something we have to do. + jshInitDevices(); +} // End of jshInit + +void jshReset() { + // TODO +} // End of jshReset + +void jshKill() { + // TODO +} // End of jshKill + +/** + * Hardware idle processing. + */ +void jshIdle() { +} // End of jshIdle + +// ---------------------------------------------------------------------------- + +int jshGetSerialNumber(unsigned char *data, int maxChars) { + const char *code = "ESP8266"; + strncpy((char *) data, code, maxChars); + return strlen(code); +} // End of jshSerialNumber + +// ---------------------------------------------------------------------------- + +void jshInterruptOff() { + // TODO +} // End of jshInterruptOff + +void jshInterruptOn() { + // TODO +} // End of jshInterruptOn + +/** + * Delay (blocking) for the supplied number of microseconds. + * Note that for the ESP8266 we must NOT CPU block for more than + * 10 milliseconds or else we may starve the WiFi subsystem. + */ +void jshDelayMicroseconds(int microsec) { + // Get the current time + /* + uint32 endTime = system_get_time() + microsec; + while ((endTime - system_get_time()) > 10000) { + os_delay_us(10000); + system_soft_wdt_feed(); + } + int lastDelta = endTime - system_get_time(); + if (lastDelta > 0) { + os_delay_us(lastDelta); + } + */ + + // This is a place holder implementation. We can and must do better + // than this. This fails because we will sleep too long. We will sleep + // for the given number of microseconds PLUS multiple calls back to the + // WiFi environment. + int count = microsec / MAX_SLEEP_TIME_US; + for (int i=0; i 0) { + os_delay_us(microsec); + } + + /* + if (0 < microsec) { + os_delay_us(microsec); + } + */ +} // End of jshDelayMicroseconds + + +static uint8_t PERIPHS[] = { +PERIPHS_IO_MUX_GPIO0_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_U0TXD_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_GPIO2_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_U0RXD_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_GPIO4_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_GPIO5_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_SD_CLK_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_SD_DATA0_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_SD_DATA1_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_SD_DATA2_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_SD_DATA3_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_SD_CMD_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_MTDI_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_MTCK_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_MTMS_U - PERIPHS_IO_MUX, +PERIPHS_IO_MUX_MTDO_U - PERIPHS_IO_MUX }; + +#define FUNC_SPI 1 +#define FUNC_GPIO 3 +#define FUNC_UART 4 + +static uint8_t pinFunction(JshPinState state) { + switch (state) { + case JSHPINSTATE_GPIO_OUT: + case JSHPINSTATE_GPIO_OUT_OPENDRAIN: + case JSHPINSTATE_GPIO_IN: + case JSHPINSTATE_GPIO_IN_PULLUP: + case JSHPINSTATE_GPIO_IN_PULLDOWN: + return FUNC_GPIO; + case JSHPINSTATE_USART_OUT: + case JSHPINSTATE_USART_IN: + return FUNC_UART; + case JSHPINSTATE_I2C: + return FUNC_SPI; + case JSHPINSTATE_AF_OUT: + case JSHPINSTATE_AF_OUT_OPENDRAIN: + case JSHPINSTATE_DAC_OUT: + case JSHPINSTATE_ADC_IN: + default: + return 0; + } +} // End of pinFunction + +/** + * \brief Set the state of the specific pin. + * + * The possible states are: + * + * JSHPINSTATE_UNDEFINED + * JSHPINSTATE_GPIO_OUT + * JSHPINSTATE_GPIO_OUT_OPENDRAIN + * JSHPINSTATE_GPIO_IN + * JSHPINSTATE_GPIO_IN_PULLUP + * JSHPINSTATE_GPIO_IN_PULLDOWN + * JSHPINSTATE_ADC_IN + * JSHPINSTATE_AF_OUT + * JSHPINSTATE_AF_OUT_OPENDRAIN + * JSHPINSTATE_USART_IN + * JSHPINSTATE_USART_OUT + * JSHPINSTATE_DAC_OUT + * JSHPINSTATE_I2C + */ +void jshPinSetState(Pin pin, //!< The pin to have its state changed. + JshPinState state //!< The new desired state of the pin. + ) { + jsiConsolePrintf("ESP8266: jshPinSetState %d, %d\n", pin, state); + + assert(pin < 16); + int periph = PERIPHS_IO_MUX + PERIPHS[pin]; + + // Disable the pin's pull-up. + PIN_PULLUP_DIS(periph); + //PIN_PULLDWN_DIS(periph); + + uint8_t primary_func = + pin < 6 ? + (PERIPHS_IO_MUX_U0TXD_U == pin + || PERIPHS_IO_MUX_U0RXD_U == pin) ? + FUNC_UART : FUNC_GPIO + : 0; + uint8_t select_func = pinFunction(state); + PIN_FUNC_SELECT(periph, primary_func == select_func ? 0 : select_func); + + switch (state) { + case JSHPINSTATE_GPIO_OUT: + case JSHPINSTATE_GPIO_OUT_OPENDRAIN: + //case JSHPINSTATE_AF_OUT: + //case JSHPINSTATE_AF_OUT_OPENDRAIN: + //case JSHPINSTATE_USART_OUT: + //case JSHPINSTATE_DAC_OUT: + gpio_output_set(0, 1 << pin, 1 << pin, 0); + break; + + case JSHPINSTATE_GPIO_IN_PULLUP: + PIN_PULLUP_EN(periph); + //case JSHPINSTATE_GPIO_IN_PULLDOWN: if (JSHPINSTATE_GPIO_IN_PULLDOWN == pin) PIN_PULLDWN_EN(periph); + case JSHPINSTATE_GPIO_IN: + gpio_output_set(0, 0, 0, 1 << pin); + break; + + case JSHPINSTATE_ADC_IN: + case JSHPINSTATE_USART_IN: + case JSHPINSTATE_I2C: + PIN_PULLUP_EN(periph); + break; + + default: + break; + } +} // End of jshPinSetState + + +/** + * \brief Return the current state of the selected pin. + * \return The current state of the selected pin. + */ +JshPinState jshPinGetState(Pin pin) { + jsiConsolePrintf("ESP8266: jshPinGetState %d\n", pin); + return JSHPINSTATE_UNDEFINED; +} // End of jshPinGetState + + +/** + * \brief Set the value of the corresponding pin. + */ +void jshPinSetValue(Pin pin, //!< The pin to have its value changed. + bool value //!< The new value of the pin. + ) { + jsiConsolePrintf("ESP8266: jshPinSetValue %d, %d\n", pin, value); + GPIO_OUTPUT_SET(pin, value); +} // End of jshPinSetValue + + +/** + * \brief Get the value of the corresponding pin. + * \return The current value of the pin. + */ +bool jshPinGetValue(Pin pin //!< The pin to have its value read. + ) { + jsiConsolePrintf("ESP8266: jshPinGetValue %d, %d\n", pin, GPIO_INPUT_GET(pin)); + return GPIO_INPUT_GET(pin); +} // End of jshPinGetValue + + +bool jshIsDeviceInitialised(IOEventFlags device) { + jsiConsolePrintf("ESP8266: jshIsDeviceInitialised %d\n", device); + return true; +} // End of jshIsDeviceInitialised + +bool jshIsUSBSERIALConnected() { + jsiConsolePrintf("ESP8266: jshIsUSBSERIALConnected\n"); + return true; +} // End of jshIsUSBSERIALConnected + +JsSysTime jshGetTimeFromMilliseconds(JsVarFloat ms) { +// jsiConsolePrintf("jshGetTimeFromMilliseconds %d, %f\n", (JsSysTime)(ms * 1000.0), ms); + return (JsSysTime) (ms * 1000.0 + 0.5); +} // End of jshGetTimeFromMilliseconds + +/** + * Given a time in microseconds, get us the value in milliseconds (float) + */ +JsVarFloat jshGetMillisecondsFromTime(JsSysTime time) { +// jsiConsolePrintf("jshGetMillisecondsFromTime %d, %f\n", time, (JsVarFloat)time / 1000.0); + return (JsVarFloat) time / 1000.0; +} // End of jshGetMillisecondsFromTime + +/** + * Return the current time in microseconds. + */ +JsSysTime jshGetSystemTime() { // in us + return system_get_time(); +} // End of jshGetSystemTime + + +/** + * Set the current time in microseconds. + */ +void jshSetSystemTime(JsSysTime time) { + os_printf("ESP8266: jshSetSystemTime: %d\n", time); +} // End of jshSetSystemTime + +// ---------------------------------------------------------------------------- + +JsVarFloat jshPinAnalog(Pin pin) { + jsiConsolePrintf("ESP8266: jshPinAnalog: %d\n", pin); + return (JsVarFloat) system_adc_read(); +} // End of jshPinAnalog + + +int jshPinAnalogFast(Pin pin) { + jsiConsolePrintf("ESP8266: jshPinAnalogFast: %d\n", pin); + return NAN; +} // End of jshPinAnalogFast + + +JshPinFunction jshPinAnalogOutput(Pin pin, JsVarFloat value, JsVarFloat freq, JshAnalogOutputFlags flags) { // if freq<=0, the default is used + jsiConsolePrintf("ESP8266: jshPinAnalogOutput: %d, %d, %d\n", pin, (int)value, (int)freq); +//pwm_set(pin, value < 0.0f ? 0 : 255.0f < value ? 255 : (uint8_t)value); + return 0; +} // End of jshPinAnalogOutput + + +void jshSetOutputValue(JshPinFunction func, int value) { + jsiConsolePrintf("ESP8266: jshSetOutputValue %d %d\n", func, value); +} // End of jshSetOutputValue + + +void jshEnableWatchDog(JsVarFloat timeout) { + jsiConsolePrintf("ESP8266: jshEnableWatchDog %0.3f\n", timeout); +} // End of jshEnableWatchDog + + +bool jshGetWatchedPinState(IOEventFlags device) { + jsiConsolePrintf("ESP8266: jshGetWatchedPinState %d", device); + return false; +} // End of jshGetWatchedPinState + + +/** + * \brief Set the value of the pin to be the value supplied and then wait for + * a given period and set the pin value again to be the opposite. + */ +void jshPinPulse(Pin pin, //!< The pin to be pulsed. + bool value, //!< The value to be pulsed into the pin. + JsVarFloat time //!< The period in milliseconds to hold the pin. + ) { + if (jshIsPinValid(pin)) { + jshPinSetState(pin, JSHPINSTATE_GPIO_OUT); + jshPinSetValue(pin, value); + jshDelayMicroseconds(jshGetTimeFromMilliseconds(time)); + jshPinSetValue(pin, !value); + } else + jsError("Invalid pin!"); +} // End of jshPinPulse + + +bool jshCanWatch(Pin pin) { + return false; +} // End of jshCanWatch + + +IOEventFlags jshPinWatch(Pin pin, bool shouldWatch) { + if (jshIsPinValid(pin)) { + } else + jsError("Invalid pin!"); + return EV_NONE; +} // End of jshPinWatch + + +JshPinFunction jshGetCurrentPinFunction(Pin pin) { + //os_printf("jshGetCurrentPinFunction %d\n", pin); + return JSH_NOTHING; +} // End of jshGetCurrentPinFunction + + +bool jshIsEventForPin(IOEvent *event, Pin pin) { + return IOEVENTFLAGS_GETTYPE(event->flags) == pinToEVEXTI(pin); +} // End of jshIsEventForPin + + +void jshUSARTSetup(IOEventFlags device, JshUSARTInfo *inf) { +} // End of jshUSARTSetup + + +/** + * Kick a device into action (if required). For instance we may need + * to set up interrupts. In this ESP8266 implementation, we transmit all the + * data that can be found associated with the device. + */ +void jshUSARTKick(IOEventFlags device) { + esp8266_uartTransmitAll(device); +} // End of jshUSARTKick + +void jshSPISetup(IOEventFlags device, JshSPIInfo *inf) { +} // End of jshSPISetup + +/** Send data through the given SPI device (if data>=0), and return the result + * of the previous send (or -1). If data<0, no data is sent and the function + * waits for data to be returned */ +int jshSPISend(IOEventFlags device, int data) { + return NAN; +} // End of jshSPISend + +/** Send 16 bit data through the given SPI device. */ +void jshSPISend16(IOEventFlags device, int data) { + jshSPISend(device, data >> 8); + jshSPISend(device, data & 255); +} // End of jshSPISend16 + +/** Set whether to send 16 bits or 8 over SPI */ +void jshSPISet16(IOEventFlags device, bool is16) { +} // End of jshSPISet16 + +/** Wait until SPI send is finished, */ +void jshSPIWait(IOEventFlags device) { +} // End of jshSPIWait + +void jshI2CSetup(IOEventFlags device, JshI2CInfo *inf) { +} // End of jshI2CSetup + +void jshI2CWrite(IOEventFlags device, unsigned char address, int nBytes, + const unsigned char *data, bool sendStop) { +} // End of jshI2CWrite + +void jshI2CRead(IOEventFlags device, unsigned char address, int nBytes, + unsigned char *data, bool sendStop) { +} // End of jshI2CRead + +/** + * \brief Save what is in RAM to flash. + * See also `jshLoadFromFlash`. + */ +void jshSaveToFlash() { + os_printf("ESP8266: jshSaveToFlash\n"); +} // End of jshSaveToFlash + +void jshLoadFromFlash() { + os_printf("ESP8266: jshLoadFromFlash\n"); +} // End of jshLoadFromFlash + + +bool jshFlashContainsCode() { + os_printf("ESP8266: jshFlashContainsCode\n"); + return false; +} // End of jshFlashContainsCode + + +/// Enter simple sleep mode (can be woken up by interrupts). Returns true on success +bool jshSleep(JsSysTime timeUntilWake) { + int time = (int) timeUntilWake; +// os_printf("jshSleep %d\n", time); + jshDelayMicroseconds(time); + return true; +} // End of jshSleep + + +void jshUtilTimerDisable() { + os_printf("ESP8266: jshUtilTimerDisable\n"); +} // End of jshUtilTimerDisable + + +void jshUtilTimerReschedule(JsSysTime period) { + os_printf("ESP8266: jshUtilTimerReschedule %d\n", period); +} // End of jshUtilTimerReschedule + + +void jshUtilTimerStart(JsSysTime period) { + os_printf("ESP8266: jshUtilTimerStart %d\n", period); +} // End of jshUtilTimerStart + + +JsVarFloat jshReadTemperature() { + return NAN; +} // End of jshReadTemperature + + +JsVarFloat jshReadVRef() { + return NAN; +} // End of jshReadVRef + + +unsigned int jshGetRandomNumber() { + return rand(); +} // End of jshGetRandomNumber + + +/** + * \brief Read data from flash memory into the buffer. + * + */ +void jshFlashRead( + void *buf, //!< buffer to read into + uint32_t addr, //!< Flash address to read from + uint32_t len //!< Length of data to read + ) { + os_printf("ESP8266: jshFlashRead: buf=0x%x for len=%d from flash addr=0x%x\n", buf, len, addr); +} // End of jshFlashRead + + +/** + * \brief Write data to flash memory from the buffer. + */ +void jshFlashWrite( + void *buf, //!< Buffer to write from + uint32_t addr, //!< Flash address to write into + uint32_t len //!< Length of data to write + ) { + os_printf("ESP8266: jshFlashWrite: buf=0x%x for len=%d into flash addr=0x%x\n", buf, len, addr); +} // End of jshFlashWrite + + +/** + * \brief Return start address and size of the flash page the given address resides in. Returns false if no page. + */ +bool jshFlashGetPage( + uint32_t addr, //!< + uint32_t *startAddr, //!< + uint32_t *pageSize //!< + ) { + os_printf("ESP8266: jshFlashGetPage: addr=0x%x, startAddr=0x%x, pageSize=%d\n", addr, startAddr, pageSize); + return false; +} // End of jshFlashGetPage + + +/** + * \brief Erase the flash page containing the address. + */ +void jshFlashErasePage( + uint32_t addr //!< + ) { + os_printf("ESP8266: jshFlashErasePage: addr=0x%x\n", addr); +} // End of jshFlashErasePage + + +/** Set whether to use the receive interrupt or not */ +void jshSPISetReceive(IOEventFlags device, bool isReceive) { +} // End of jshSPISetReceive + + +/** + * Callback for end of runtime. This should never be called and has been + * added to satisfy the linker. + */ +void _exit(int status) { +} // End of _exit +// End of file diff --git a/targets/esp8266/telnet.h b/targets/esp8266/telnet.h new file mode 100644 index 000000000..d6fe44dbd --- /dev/null +++ b/targets/esp8266/telnet.h @@ -0,0 +1,25 @@ +/* + * This file is part of Espruino/ESP8266, a JavaScript interpreter for ESP8266 + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef USER_TELNET_H_ +#define USER_TELNET_H_ + +/** + * Register a telnet serever listener on port 23 to handle incoming telnet client + * connections. The parameter called pLineCB is a pointer to a callback function + * that should be called when we have received a link of input. + */ +void telnet_startListening(void (*lineCB)(char *arg)); + +/** + * Send the string passed as a parameter to the telnet client. + */ +void telnet_send(char *text); + +#endif /* USER_TELNET_H_ */ diff --git a/targets/esp8266/telnet_client.c b/targets/esp8266/telnet_client.c new file mode 100644 index 000000000..9f89190be --- /dev/null +++ b/targets/esp8266/telnet_client.c @@ -0,0 +1,94 @@ +/* + * This file is part of Espruino/ESP8266, a JavaScript interpreter for ESP8266 + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +//#include "espmissingincludes.h" +#include +#include +#include +#include +#include +#include +#include + +#define MAXLINE 200 + +static char line[MAXLINE]; +static struct espconn conn1; +static esp_tcp tcp1; +static bool discard; +static void (*lineCB)(char *); +static struct espconn *pTelnetClientConn; + +/** + * Callback invoked when a new TCP/IP connection has been formed. + * o arg - This is a pointer to the struct espconn that reflects this entry. + */ +static void connectCB(void *arg) { + struct espconn *pConn = (struct espconn *)arg; + + // By default, an ESP TCP connection times out after 10 seconds + // Change to the max value. + espconn_regist_time(pConn, 7200, 1); + + os_printf("Connect cb!!\n"); + strcpy(line, ""); + discard = false; + pTelnetClientConn = pConn; +} // End of connectCB + +static void disconnectCB(void *arg) { + os_printf("Disconnect cb!!\n"); +} // End of disconnectCB + + +static void receiveCB(void *arg, char *pData, unsigned short len) { + //os_printf("Receive cb!! len=%d\n", len); + if (strlen(line) + len >= MAXLINE) { + discard = true; + strcpy(line, ""); + } + strncat(line, pData, len); + if (strstr(line, "\r\n") != NULL) { + if (discard) { + os_printf("Discarded ... line too long"); + discard = false; + } else { + //os_printf("Found: %s", line); + (*lineCB)(line); + } + strcpy(line, ""); + } else { + //os_printf("Not found, current='%s'\n", line); + } +} // End of receiveCB + +/** + * Register a telnet serever listener on port 23 to handle incoming telnet client + * connections. The parameter called pLineCB is a pointer to a callback function + * that should be called when we have received a link of input. + */ +void telnet_startListening(void (*plineCB)(char *)) { + lineCB= plineCB; + tcp1.local_port = 23; + conn1.type = ESPCONN_TCP; + conn1.state = ESPCONN_NONE; + conn1.proto.tcp = &tcp1; + espconn_regist_connectcb(&conn1, connectCB); + espconn_regist_disconcb(&conn1, disconnectCB); + espconn_regist_recvcb(&conn1, receiveCB); + espconn_accept(&conn1); + os_printf("Now listening for telnet client connection ...\n"); +} // End of telnet_startListening + +/** + * Send the string passed as a parameter to the telnet client. + */ +void telnet_send(char *text) { + espconn_sent(pTelnetClientConn, (uint8_t *)text, strlen(text)); +} // End of telnet_send +// End of file diff --git a/targets/esp8266/uart.c b/targets/esp8266/uart.c new file mode 100644 index 000000000..672551f5f --- /dev/null +++ b/targets/esp8266/uart.c @@ -0,0 +1,821 @@ +/* + * File : uart.c + * Copyright (C) 2013 - 2016, Espressif Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ +#include "ets_sys.h" +#include "osapi.h" +#include "driver/uart.h" +#include "osapi.h" +#include "driver/uart_register.h" +#include "mem.h" +#include "os_type.h" +#include "espmissingincludes.h" + +// UartDev is defined and initialized in rom code. +extern UartDevice UartDev; + +LOCAL struct UartBuffer* pTxBuffer = NULL; +LOCAL struct UartBuffer* pRxBuffer = NULL; + +#define MAX_RX_BUFFER 500 +static char rxBuffer[MAX_RX_BUFFER]; +static int rxBufferLen = 0; + +int getRXBuffer(char *pBuffer, int bufferLen) { + if (rxBufferLen > bufferLen) { + memcpy(pBuffer,rxBuffer, bufferLen); + memcpy(rxBuffer, rxBuffer+bufferLen, rxBufferLen-bufferLen); + rxBufferLen = rxBufferLen - bufferLen; + return bufferLen; + } + int sizeReturned = rxBufferLen; + memcpy(pBuffer, rxBuffer, rxBufferLen); + rxBufferLen = 0; + return sizeReturned; +} // End of getRXBuffer + +/*uart demo with a system task, to output what uart receives*/ +/*this is a example to process uart data from task,please change the priority to fit your application task if exists*/ +/*it might conflict with your task, if so,please arrange the priority of different task, or combine it to a different event in the same task. */ +#define uart_recvTaskPrio 2 +#define uart_recvTaskQueueLen 10 +os_event_t uart_recvTaskQueue[uart_recvTaskQueueLen]; + +#define DBG +#define DBG1 uart1_sendStr_no_wait +#define DBG2 os_printf + + +LOCAL void uart0_rx_intr_handler(void *para); + +/****************************************************************************** + * FunctionName : uart_config + * Description : Internal used function + * UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled + * UART1 just used for debug output + * Parameters : uart_no, use UART0 or UART1 defined ahead + * Returns : NONE +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +uart_config(uint8 uart_no) +{ + if (uart_no == UART1){ + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); + }else{ + /* rcv_buff size if 0x100 */ + ETS_UART_INTR_ATTACH(uart0_rx_intr_handler, &(UartDev.rcv_buff)); + PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); + #if UART_HW_RTS + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS); //HW FLOW CONTROL RTS PIN + #endif + #if UART_HW_CTS + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_U0CTS); //HW FLOW CONTROL CTS PIN + #endif + } + uart_div_modify(uart_no, UART_CLK_FREQ / (UartDev.baut_rate));//SET BAUDRATE + + WRITE_PERI_REG(UART_CONF0(uart_no), ((UartDev.exist_parity & UART_PARITY_EN_M) << UART_PARITY_EN_S) //SET BIT AND PARITY MODE + | ((UartDev.parity & UART_PARITY_M) <> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) { + break; + } + } + WRITE_PERI_REG(UART_FIFO(uart) , TxChar); + return OK; +} + +/****************************************************************************** + * FunctionName : uart1_write_char + * Description : Internal used function + * Do some special deal while tx char is '\r' or '\n' + * Parameters : char c - character to tx + * Returns : NONE +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +uart1_write_char(char c) +{ + if (c == '\n'){ + uart_tx_one_char(UART1, '\r'); + uart_tx_one_char(UART1, '\n'); + }else if (c == '\r'){ + + }else{ + uart_tx_one_char(UART1, c); + } +} + +//os_printf output to fifo or to the tx buffer +LOCAL void ICACHE_FLASH_ATTR +uart0_write_char_no_wait(char c) +{ +#if UART_BUFF_EN //send to uart0 fifo but do not wait + uint8 chr; + if (c == '\n'){ + chr = '\r'; + tx_buff_enq(&chr, 1); + chr = '\n'; + tx_buff_enq(&chr, 1); + }else if (c == '\r'){ + + }else{ + tx_buff_enq(&c,1); + } +#else //send to uart tx buffer + if (c == '\n'){ + uart_tx_one_char_no_wait(UART0, '\r'); + uart_tx_one_char_no_wait(UART0, '\n'); + }else if (c == '\r'){ + + } + else{ + uart_tx_one_char_no_wait(UART0, c); + } +#endif +} + +/****************************************************************************** + * FunctionName : uart0_tx_buffer + * Description : use uart0 to transfer buffer + * Parameters : uint8 *buf - point to send buffer + * uint16 len - buffer len + * Returns : +*******************************************************************************/ +void ICACHE_FLASH_ATTR +uart0_tx_buffer(uint8 *buf, uint16 len) +{ + uint16 i; + for (i = 0; i < len; i++) + { + uart_tx_one_char(UART0, buf[i]); + } +} + +/****************************************************************************** + * FunctionName : uart0_sendStr + * Description : use uart0 to transfer buffer + * Parameters : uint8 *buf - point to send buffer + * uint16 len - buffer len + * Returns : +*******************************************************************************/ +void ICACHE_FLASH_ATTR +uart0_sendStr(const char *str) +{ + while(*str){ + uart_tx_one_char(UART0, *str++); + } +} +void at_port_print(const char *str) __attribute__((alias("uart0_sendStr"))); +/****************************************************************************** + * FunctionName : uart0_rx_intr_handler + * Description : Internal used function + * UART0 interrupt handler, add self handle code inside + * Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg + * Returns : NONE +*******************************************************************************/ +LOCAL void +uart0_rx_intr_handler(void *para) +{ + /* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents + * uart1 and uart0 respectively + */ + uint8 RcvChar; + uint8 uart_no = UART0;//UartDev.buff_uart_no; + uint8 fifo_len = 0; + uint8 buf_idx = 0; + uint8 temp,cnt; + //RcvMsgBuff *pRxBuff = (RcvMsgBuff *)para; + + /*ATTENTION:*/ + /*IN NON-OS VERSION SDK, DO NOT USE "ICACHE_FLASH_ATTR" FUNCTIONS IN THE WHOLE HANDLER PROCESS*/ + /*ALL THE FUNCTIONS CALLED IN INTERRUPT HANDLER MUST BE DECLARED IN RAM */ + /*IF NOT , POST AN EVENT AND PROCESS IN SYSTEM TASK */ + if(UART_FRM_ERR_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_FRM_ERR_INT_ST)){ + DBG1("FRM_ERR\r\n"); + WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR); + }else if(UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)){ + DBG("f"); + uart_rx_intr_disable(UART0); + WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR); + system_os_post(uart_recvTaskPrio, 0, 0); + }else if(UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST)){ + DBG("t"); + uart_rx_intr_disable(UART0); + WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_TOUT_INT_CLR); + system_os_post(uart_recvTaskPrio, 0, 0); + }else if(UART_TXFIFO_EMPTY_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_TXFIFO_EMPTY_INT_ST)){ + DBG("e"); + /* to output uart data from uart buffer directly in empty interrupt handler*/ + /*instead of processing in system event, in order not to wait for current task/function to quit */ + /*ATTENTION:*/ + /*IN NON-OS VERSION SDK, DO NOT USE "ICACHE_FLASH_ATTR" FUNCTIONS IN THE WHOLE HANDLER PROCESS*/ + /*ALL THE FUNCTIONS CALLED IN INTERRUPT HANDLER MUST BE DECLARED IN RAM */ + CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA); + #if UART_BUFF_EN + tx_start_uart_buffer(UART0); + #endif + //system_os_post(uart_recvTaskPrio, 1, 0); + WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_TXFIFO_EMPTY_INT_CLR); + + }else if(UART_RXFIFO_OVF_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_OVF_INT_ST)){ + WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_OVF_INT_CLR); + DBG1("RX OVF!!\r\n"); + } + +} + +/****************************************************************************** + * FunctionName : uart_init + * Description : user interface for init uart + * Parameters : UartBautRate uart0_br - uart0 bautrate + * UartBautRate uart1_br - uart1 bautrate + * Returns : NONE +*******************************************************************************/ +#if UART_SELFTEST&UART_BUFF_EN +os_timer_t buff_timer_t; +void ICACHE_FLASH_ATTR +uart_test_rx() +{ + uint8 uart_buf[128]={0}; + uint16 len = 0; + len = rx_buff_deq(uart_buf, 128 ); + tx_buff_enq(uart_buf,len); +} +#endif + +LOCAL void ICACHE_FLASH_ATTR /////// +uart_recvTask(os_event_t *events) +{ + if(events->sig == 0){ + #if UART_BUFF_EN + Uart_rx_buff_enq(); + #else + uint8 fifo_len = (READ_PERI_REG(UART_STATUS(UART0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; + uint8 d_tmp = 0; + uint8 idx=0; + for(idx=0;idx 0) { + system_os_post(1, 2, 0); + } + WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR|UART_RXFIFO_TOUT_INT_CLR); + uart_rx_intr_enable(UART0); + #endif + }else if(events->sig == 1){ + #if UART_BUFF_EN + //already move uart buffer output to uart empty interrupt + //tx_start_uart_buffer(UART0); + #else + + #endif + } +} + +void ICACHE_FLASH_ATTR +uart_init(UartBautRate uart0_br, UartBautRate uart1_br) +{ + /*this is a example to process uart data from task,please change the priority to fit your application task if exists*/ + system_os_task(uart_recvTask, uart_recvTaskPrio, uart_recvTaskQueue, uart_recvTaskQueueLen); //demo with a task to process the uart data + + UartDev.baut_rate = uart0_br; + uart_config(UART0); + UartDev.baut_rate = uart1_br; + uart_config(UART1); + ETS_UART_INTR_ENABLE(); + + #if UART_BUFF_EN + pTxBuffer = Uart_Buf_Init(UART_TX_BUFFER_SIZE); + pRxBuffer = Uart_Buf_Init(UART_RX_BUFFER_SIZE); + #endif + + + /*option 1: use default print, output from uart0 , will wait some time if fifo is full */ + //do nothing... + + /*option 2: output from uart1,uart1 output will not wait , just for output debug info */ + /*os_printf output uart data via uart1(GPIO2)*/ + //os_install_putc1((void *)uart1_write_char); //use this one to output debug information via uart1 // + + /*option 3: output from uart0 will skip current byte if fifo is full now... */ + /*see uart0_write_char_no_wait:you can output via a buffer or output directly */ + /*os_printf output uart data via uart0 or uart buffer*/ + //os_install_putc1((void *)uart0_write_char_no_wait); //use this to print via uart0 + + #if UART_SELFTEST&UART_BUFF_EN + os_timer_disarm(&buff_timer_t); + os_timer_setfn(&buff_timer_t, uart_test_rx , NULL); //a demo to process the data in uart rx buffer + os_timer_arm(&buff_timer_t,10,1); + #endif +} + +void ICACHE_FLASH_ATTR +uart_reattach() +{ + uart_init(BIT_RATE_115200, BIT_RATE_115200); +} + +/****************************************************************************** + * FunctionName : uart_tx_one_char_no_wait + * Description : uart tx a single char without waiting for fifo + * Parameters : uint8 uart - uart port + * uint8 TxChar - char to tx + * Returns : STATUS +*******************************************************************************/ +STATUS uart_tx_one_char_no_wait(uint8 uart, uint8 TxChar) +{ + uint8 fifo_cnt = (( READ_PERI_REG(UART_STATUS(uart))>>UART_TXFIFO_CNT_S)& UART_TXFIFO_CNT); + if (fifo_cnt < 126) { + WRITE_PERI_REG(UART_FIFO(uart) , TxChar); + } + return OK; +} + +STATUS uart0_tx_one_char_no_wait(uint8 TxChar) +{ + uint8 fifo_cnt = (( READ_PERI_REG(UART_STATUS(UART0))>>UART_TXFIFO_CNT_S)& UART_TXFIFO_CNT); + if (fifo_cnt < 126) { + WRITE_PERI_REG(UART_FIFO(UART0) , TxChar); + } + return OK; +} + + +/****************************************************************************** + * FunctionName : uart1_sendStr_no_wait + * Description : uart tx a string without waiting for every char, used for print debug info which can be lost + * Parameters : const char *str - string to be sent + * Returns : NONE +*******************************************************************************/ +void uart1_sendStr_no_wait(const char *str) +{ + while(*str){ + uart_tx_one_char_no_wait(UART1, *str++); + } +} + + +#if UART_BUFF_EN +/****************************************************************************** + * FunctionName : Uart_Buf_Init + * Description : tx buffer enqueue: fill a first linked buffer + * Parameters : char *pdata - data point to be enqueue + * Returns : NONE +*******************************************************************************/ +struct UartBuffer* ICACHE_FLASH_ATTR +Uart_Buf_Init(uint32 buf_size) +{ + uint32 heap_size = system_get_free_heap_size(); + if(heap_size <=buf_size){ + DBG1("no buf for uart\n\r"); + return NULL; + }else{ + DBG("test heap size: %d\n\r",heap_size); + struct UartBuffer* pBuff = (struct UartBuffer* )os_malloc(sizeof(struct UartBuffer)); + pBuff->UartBuffSize = buf_size; + pBuff->pUartBuff = (uint8*)os_malloc(pBuff->UartBuffSize); + pBuff->pInPos = pBuff->pUartBuff; + pBuff->pOutPos = pBuff->pUartBuff; + pBuff->Space = pBuff->UartBuffSize; + pBuff->BuffState = OK; + pBuff->nextBuff = NULL; + pBuff->TcpControl = RUN; + return pBuff; + } +} + + +//copy uart buffer +LOCAL void Uart_Buf_Cpy(struct UartBuffer* pCur, char* pdata , uint16 data_len) +{ + if(data_len == 0) return ; + + uint16 tail_len = pCur->pUartBuff + pCur->UartBuffSize - pCur->pInPos ; + if(tail_len >= data_len){ //do not need to loop back the queue + os_memcpy(pCur->pInPos , pdata , data_len ); + pCur->pInPos += ( data_len ); + pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize ); + pCur->Space -=data_len; + }else{ + os_memcpy(pCur->pInPos, pdata, tail_len); + pCur->pInPos += ( tail_len ); + pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize ); + pCur->Space -=tail_len; + os_memcpy(pCur->pInPos, pdata+tail_len , data_len-tail_len); + pCur->pInPos += ( data_len-tail_len ); + pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize ); + pCur->Space -=( data_len-tail_len); + } + +} + +/****************************************************************************** + * FunctionName : uart_buf_free + * Description : deinit of the tx buffer + * Parameters : struct UartBuffer* pTxBuff - tx buffer struct pointer + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +uart_buf_free(struct UartBuffer* pBuff) +{ + os_free(pBuff->pUartBuff); + os_free(pBuff); +} + + +//rx buffer dequeue +uint16 ICACHE_FLASH_ATTR +rx_buff_deq(char* pdata, uint16 data_len ) +{ + uint16 buf_len = (pRxBuffer->UartBuffSize- pRxBuffer->Space); + uint16 tail_len = pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize - pRxBuffer->pOutPos ; + uint16 len_tmp = 0; + len_tmp = ((data_len > buf_len)?buf_len:data_len); + if(pRxBuffer->pOutPos <= pRxBuffer->pInPos){ + os_memcpy(pdata, pRxBuffer->pOutPos,len_tmp); + pRxBuffer->pOutPos+= len_tmp; + pRxBuffer->Space += len_tmp; + }else{ + if(len_tmp>tail_len){ + os_memcpy(pdata, pRxBuffer->pOutPos, tail_len); + pRxBuffer->pOutPos += tail_len; + pRxBuffer->pOutPos = (pRxBuffer->pUartBuff + (pRxBuffer->pOutPos- pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize ); + pRxBuffer->Space += tail_len; + + os_memcpy(pdata+tail_len , pRxBuffer->pOutPos, len_tmp-tail_len); + pRxBuffer->pOutPos+= ( len_tmp-tail_len ); + pRxBuffer->pOutPos= (pRxBuffer->pUartBuff + (pRxBuffer->pOutPos- pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize ); + pRxBuffer->Space +=( len_tmp-tail_len); + }else{ + //os_printf("case 3 in rx deq\n\r"); + os_memcpy(pdata, pRxBuffer->pOutPos, len_tmp); + pRxBuffer->pOutPos += len_tmp; + pRxBuffer->pOutPos = (pRxBuffer->pUartBuff + (pRxBuffer->pOutPos- pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize ); + pRxBuffer->Space += len_tmp; + } + } + if(pRxBuffer->Space >= UART_FIFO_LEN){ + uart_rx_intr_enable(UART0); + } + return len_tmp; +} + + +//move data from uart fifo to rx buffer +void Uart_rx_buff_enq() +{ + uint8 fifo_len,buf_idx; + uint8 fifo_data; + #if 1 + fifo_len = (READ_PERI_REG(UART_STATUS(UART0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; + if(fifo_len >= pRxBuffer->Space){ + os_printf("buf full!!!\n\r"); + }else{ + buf_idx=0; + while(buf_idx < fifo_len){ + buf_idx++; + fifo_data = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; + *(pRxBuffer->pInPos++) = fifo_data; + if(pRxBuffer->pInPos == (pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize)){ + pRxBuffer->pInPos = pRxBuffer->pUartBuff; + } + } + pRxBuffer->Space -= fifo_len ; + if(pRxBuffer->Space >= UART_FIFO_LEN){ + //os_printf("after rx enq buf enough\n\r"); + uart_rx_intr_enable(UART0); + } + } + #endif +} + + +//fill the uart tx buffer +void ICACHE_FLASH_ATTR +tx_buff_enq(char* pdata, uint16 data_len ) +{ + CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA); + + if(pTxBuffer == NULL){ + DBG1("\n\rnull, create buffer struct\n\r"); + pTxBuffer = Uart_Buf_Init(UART_TX_BUFFER_SIZE); + if(pTxBuffer!= NULL){ + Uart_Buf_Cpy(pTxBuffer , pdata, data_len ); + }else{ + DBG1("uart tx MALLOC no buf \n\r"); + } + }else{ + if(data_len <= pTxBuffer->Space){ + Uart_Buf_Cpy(pTxBuffer , pdata, data_len); + }else{ + DBG1("UART TX BUF FULL!!!!\n\r"); + } + } + #if 0 + if(pTxBuffer->Space <= URAT_TX_LOWER_SIZE){ + set_tcp_block(); + } + #endif + SET_PERI_REG_MASK(UART_CONF1(UART0), (UART_TX_EMPTY_THRESH_VAL & UART_TXFIFO_EMPTY_THRHD)<pOutPos++)); + if(pTxBuff->pOutPos == (pTxBuff->pUartBuff + pTxBuff->UartBuffSize)){ + pTxBuff->pOutPos = pTxBuff->pUartBuff; + } + } + pTxBuff->pOutPos = (pTxBuff->pUartBuff + (pTxBuff->pOutPos - pTxBuff->pUartBuff) % pTxBuff->UartBuffSize ); + pTxBuff->Space += data_len; +} + + +/****************************************************************************** + * FunctionName : tx_start_uart_buffer + * Description : get data from the tx buffer and fill the uart tx fifo, co-work with the uart fifo empty interrupt + * Parameters : uint8 uart_no - uart port num + * Returns : NONE +*******************************************************************************/ +void tx_start_uart_buffer(uint8 uart_no) +{ + uint8 tx_fifo_len = (READ_PERI_REG(UART_STATUS(uart_no))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT; + uint8 fifo_remain = UART_FIFO_LEN - tx_fifo_len ; + uint8 len_tmp; + uint16 tail_ptx_len,head_ptx_len,data_len; + //struct UartBuffer* pTxBuff = *get_buff_prt(); + + if(pTxBuffer){ + data_len = (pTxBuffer->UartBuffSize - pTxBuffer->Space); + if(data_len > fifo_remain){ + len_tmp = fifo_remain; + tx_fifo_insert( pTxBuffer,len_tmp,uart_no); + SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA); + }else{ + len_tmp = data_len; + tx_fifo_insert( pTxBuffer,len_tmp,uart_no); + } + }else{ + DBG1("pTxBuff null \n\r"); + } +} + +#endif + + +void uart_rx_intr_disable(uint8 uart_no) +{ +#if 1 + CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); +#else + ETS_UART_INTR_DISABLE(); +#endif +} + +void uart_rx_intr_enable(uint8 uart_no) +{ +#if 1 + SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); +#else + ETS_UART_INTR_ENABLE(); +#endif +} + + +//======================================================== +LOCAL void +uart0_write_char(char c) +{ + if (c == '\n') { + uart_tx_one_char(UART0, '\r'); + uart_tx_one_char(UART0, '\n'); + } else if (c == '\r') { + } else { + uart_tx_one_char(UART0, c); + } +} + +void ICACHE_FLASH_ATTR +UART_SetWordLength(uint8 uart_no, UartBitsNum4Char len) +{ + SET_PERI_REG_BITS(UART_CONF0(uart_no),UART_BIT_NUM,len,UART_BIT_NUM_S); +} + +void ICACHE_FLASH_ATTR +UART_SetStopBits(uint8 uart_no, UartStopBitsNum bit_num) +{ + SET_PERI_REG_BITS(UART_CONF0(uart_no),UART_STOP_BIT_NUM,bit_num,UART_STOP_BIT_NUM_S); +} + +void ICACHE_FLASH_ATTR +UART_SetLineInverse(uint8 uart_no, UART_LineLevelInverse inverse_mask) +{ + CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_LINE_INV_MASK); + SET_PERI_REG_MASK(UART_CONF0(uart_no), inverse_mask); +} + +void ICACHE_FLASH_ATTR +UART_SetParity(uint8 uart_no, UartParityMode Parity_mode) +{ + CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_PARITY |UART_PARITY_EN); + if(Parity_mode==NONE_BITS){ + }else{ + SET_PERI_REG_MASK(UART_CONF0(uart_no), Parity_mode|UART_PARITY_EN); + } +} + +void ICACHE_FLASH_ATTR +UART_SetBaudrate(uint8 uart_no,uint32 baud_rate) +{ + uart_div_modify(uart_no, UART_CLK_FREQ /baud_rate); +} + +void ICACHE_FLASH_ATTR +UART_SetFlowCtrl(uint8 uart_no,UART_HwFlowCtrl flow_ctrl,uint8 rx_thresh) +{ + if(flow_ctrl&USART_HardwareFlowControl_RTS){ + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS); + SET_PERI_REG_BITS(UART_CONF1(uart_no),UART_RX_FLOW_THRHD,rx_thresh,UART_RX_FLOW_THRHD_S); + SET_PERI_REG_MASK(UART_CONF1(uart_no), UART_RX_FLOW_EN); + }else{ + CLEAR_PERI_REG_MASK(UART_CONF1(uart_no), UART_RX_FLOW_EN); + } + if(flow_ctrl&USART_HardwareFlowControl_CTS){ + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_UART0_CTS); + SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_TX_FLOW_EN); + }else{ + CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_TX_FLOW_EN); + } +} + +void ICACHE_FLASH_ATTR +UART_WaitTxFifoEmpty(uint8 uart_no , uint32 time_out_us) //do not use if tx flow control enabled +{ + uint32 t_s = system_get_time(); + while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S)){ + + if(( system_get_time() - t_s )> time_out_us){ + break; + } + WRITE_PERI_REG(0X60000914, 0X73);//WTD + + } +} + + +bool ICACHE_FLASH_ATTR +UART_CheckOutputFinished(uint8 uart_no, uint32 time_out_us) +{ + uint32 t_start = system_get_time(); + uint8 tx_fifo_len; + uint32 tx_buff_len; + while(1){ + tx_fifo_len =( (READ_PERI_REG(UART_STATUS(uart_no))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT); + if(pTxBuffer){ + tx_buff_len = ((pTxBuffer->UartBuffSize)-(pTxBuffer->Space)); + }else{ + tx_buff_len = 0; + } + + if( tx_fifo_len==0 && tx_buff_len==0){ + return TRUE; + } + if( system_get_time() - t_start > time_out_us){ + return FALSE; + } + WRITE_PERI_REG(0X60000914, 0X73);//WTD + } +} + + +void ICACHE_FLASH_ATTR +UART_ResetFifo(uint8 uart_no) +{ + SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); + CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); +} + +void ICACHE_FLASH_ATTR +UART_ClearIntrStatus(uint8 uart_no,uint32 clr_mask) +{ + WRITE_PERI_REG(UART_INT_CLR(uart_no), clr_mask); +} + +void ICACHE_FLASH_ATTR +UART_SetIntrEna(uint8 uart_no,uint32 ena_mask) +{ + SET_PERI_REG_MASK(UART_INT_ENA(uart_no), ena_mask); +} + + +void ICACHE_FLASH_ATTR +UART_SetPrintPort(uint8 uart_no) +{ + if(uart_no==1){ + os_install_putc1(uart1_write_char); + }else{ + /*option 1: do not wait if uart fifo is full,drop current character*/ + os_install_putc1(uart0_write_char_no_wait); + /*option 2: wait for a while if uart fifo is full*/ + os_install_putc1(uart0_write_char); + } +} + + +//======================================================== + + +/*test code*/ +void ICACHE_FLASH_ATTR +uart_init_2(UartBautRate uart0_br, UartBautRate uart1_br) +{ + // rom use 74880 baut_rate, here reinitialize + UartDev.baut_rate = uart0_br; + UartDev.exist_parity = STICK_PARITY_EN; + UartDev.parity = EVEN_BITS; + UartDev.stop_bits = ONE_STOP_BIT; + UartDev.data_bits = EIGHT_BITS; + + uart_config(UART0); + UartDev.baut_rate = uart1_br; + uart_config(UART1); + ETS_UART_INTR_ENABLE(); + + // install uart1 putc callback + os_install_putc1((void *)uart1_write_char);//print output at UART1 +} + +/** + * Discard any data in the RX buffer. + */ +int uart_rx_discard() { + int oldRxBufferLen = rxBufferLen; + rxBufferLen = 0; + return oldRxBufferLen; +} // End of uart_rx_discard diff --git a/targets/esp8266/user_config.h b/targets/esp8266/user_config.h new file mode 100644 index 000000000..d4724208c --- /dev/null +++ b/targets/esp8266/user_config.h @@ -0,0 +1,15 @@ +/* + * This file is part of Espruino/ESP8266, a JavaScript interpreter for ESP8266 + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef TARGETS_ESP8266_USER_CONFIG_H_ +#define TARGETS_ESP8266_USER_CONFIG_H_ + + + +#endif /* TARGETS_ESP8266_USER_CONFIG_H_ */ diff --git a/targets/esp8266/user_main.c b/targets/esp8266/user_main.c new file mode 100644 index 000000000..8afc82ad1 --- /dev/null +++ b/targets/esp8266/user_main.c @@ -0,0 +1,232 @@ +/* + * This file is part of Espruino/ESP8266, a JavaScript interpreter for ESP8266 + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include + +//#define FAKE_STDLIB +#define _GCC_WRAP_STDINT_H +typedef long long int64_t; + +#include +#include +#include +#include "ESP8266_board.h" + +// --- Constants +// The size of the task queue +#define TASK_QUEUE_LENGTH 10 + +// Should we introduce a ticker to say we are still alive? +//#define EPS8266_BOARD_HEARTBEAT + +// --- Forward definitions +static void mainLoop(); + +// --- File local variables + +// The task queue for the app +static os_event_t taskAppQueue[TASK_QUEUE_LENGTH]; + +// Flag indicating whether or not main loop processing is suspended. +static bool suspendMainLoopFlag = false; + +// Time structure for main loop time suspension. +static os_timer_t mainLoopSuspendTimer; + +// --- Functions +/** + * \brief Dump the ESP8266 restart information. + * This is purely for debugging. + * When an ESP8266 crashes, before it ends, it records its exception information. + * This function retrieves that data and logs it. + */ +static void dumpRestart() { + struct rst_info *rstInfo = system_get_rst_info(); + os_printf("Restart info:\n"); + os_printf(" reason: %d\n", rstInfo->reason); + os_printf(" exccause: %x\n", rstInfo->exccause); + os_printf(" epc1: %x\n", rstInfo->epc1); + os_printf(" epc2: %x\n", rstInfo->epc2); + os_printf(" epc3: %x\n", rstInfo->epc3); + os_printf(" excvaddr: %x\n", rstInfo->excvaddr); + os_printf(" depc: %x\n", rstInfo->depc); +} // End of dump_restart + + +/** + * \brief Queue a task for the main loop. + */ +static void queueTaskMainLoop() { + system_os_post(TASK_APP_QUEUE, TASK_APP_MAINLOOP, 0); +} // End of queueMainLoop + + +/** + * \brief Suspend processing the main loop for a period of time. + */ +void suspendMainLoop( + uint32 interval //!< + ) { + suspendMainLoopFlag = true; + os_timer_arm(&mainLoopSuspendTimer, interval, 0 /* No repeat */); +} // End of suspendMainLoop + + +/** + * \brief Enable main loop processing. + */ +static void enableMainLoop() { + suspendMainLoopFlag = false; + queueTaskMainLoop(); +} // End of enableMainLoop + + +/** + * \brief The event handler for ESP8266 tasks as created by system_os_post() on the TASK_APP_QUEUE. + */ +static void eventHandler( + os_event_t *pEvent //!< + ) { + + switch (pEvent->sig) { + // Handle the main loop event. + case TASK_APP_MAINLOOP: + mainLoop(); + break; + // Handle the event to process received data. + case TASK_APP_RX_DATA: + { + // Get the data from the UART RX buffer. If the size of the returned data is + // not zero, then push it onto the Espruino processing queue for characters. + char pBuffer[100]; + int size = getRXBuffer(pBuffer, sizeof(pBuffer)); + if (size > 0) { + jshPushIOCharEvents(jsiGetConsoleDevice(), pBuffer, size); + } + } + break; + // Handle the unknown event type. + default: + os_printf("user_main: eventHandler: Unknown task type: %d", + pEvent->sig); + break; + } +} // End of eventHandler + + +/** + * \brief A callback function to be invoked when a line has been entered on the telnet client. + * Here we want to pass that line to the JS parser for processing. + */ +static void telnetLineCB(char *line) { + jsiConsolePrintf("LineCB: %s", line); + // Pass the line to the interactive module ... + + jshPushIOCharEvents(jsiGetConsoleDevice(), line, strlen(line)); + //jspEvaluate(line, true); + //jsiDumpState(); + telnet_send("JS> "); +} // End of lineCB + + +/** + * When we have been allocated a TCP/IP address, this function is called back. Obviously + * there is little we can do at the network level until we have an IP. + */ +static void gotIpCallback() { + telnet_startListening(telnetLineCB); +} // End of gotIpCallback + + +/** + * \brief Perform the main loop processing. + * This is where work is performed + * as often as possible. + */ +static void mainLoop() { + if (suspendMainLoopFlag == true) { + return; + } + jsiLoop(); + +#ifdef EPS8266_BOARD_HEARTBEAT + if (system_get_time() - lastTime > 1000 * 1000 * 5) { + lastTime = system_get_time(); + os_printf("tick: %d\n", jshGetSystemTime()); + } +#endif + + // Setup for another callback + queueTaskMainLoop(); +} // End of mainLoop + + +/** + * The ESP8266 provides a mechanism to register a callback that is invoked when initialization + * of the ESP8266 is complete. This is the implementation of that callback. At this point + * we can assume that the ESP8266 is fully ready to do work for us. + */ +static void initDone() { + os_printf("initDone invoked\n"); + + // Discard any junk data in the input as this is a boot. + //uart_rx_discard(); + + jshInit(); // Initialize the hardware + jsvInit(); // Initialize the variables + jsiInit(false); // Initialize the interactive subsystem + + // Register the event handlers. + system_os_task(eventHandler, TASK_APP_QUEUE, taskAppQueue, TASK_QUEUE_LENGTH); + + // At this point, our JavaScript environment should be up and running. + + // Initialize the networking subsystem. + jswrap_ESP8266WiFi_init(); + + // Post the first event to get us going. + queueTaskMainLoop(); + + return; +} // End of initDone + + +/** + * This is a required function needed for ESP8266 SDK. In 99.999% of the instances, this function + * needs to be present but have no body. It isn't 100% known what this function does other than + * provide an architected callback during initializations. However, its purpose is unknown. + */ +void user_rf_pre_init() { +} // End of user_rf_pre_init + + +/** + * \brief The main entry point in an ESP8266 application. + * It is where the logic of ESP8266 starts. + */ +void user_init() { + // Initialize the UART devices + uart_init(BIT_RATE_115200, BIT_RATE_115200); + UART_SetPrintPort(1); + + // Dump the restart exception information. + dumpRestart(); + + // Register the ESP8266 initialization callback. + system_init_done_cb(initDone); + + // Do NOT attempt to auto connect to an access point. + //wifi_station_set_auto_connect(0); + os_timer_setfn(&mainLoopSuspendTimer, enableMainLoop, NULL); +} // End of user_init +// End of file