/** * Invoke the JavaScript callback to notify the program that an ESP8266 * WiFi event has occurred. */ static void sendWifiEvent( uint32 eventType, //!< The ESP8266 WiFi event type. JsVar *jsDetails //!< The JS object to be passed as a parameter to the callback. ) { jsvUnLock(jsDetails); // We need to check that we actually have an event callback handler because // it might have been disabled/removed. if (g_jsWiFiEventCallback != NULL) { // Build a callback event. JsVar *params[2]; params[0] = jsvNewFromInteger(eventType); params[1] = jsDetails; jsiQueueEvents(NULL, g_jsWiFiEventCallback, params, 2); } if (g_jsGotIpCallback != NULL && eventType == EVENT_STAMODE_GOT_IP) { JsVar *params[2]; params[0] = jsvNewFromInteger(eventType); params[1] = jsDetails; jsiQueueEvents(NULL, g_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; } }
void _jswrap_promise_queuereject(JsVar *promise, JsVar *data) { JsVar *fn = jsvNewNativeFunction((void (*)(void))_jswrap_promise_reject, JSWAT_VOID|JSWAT_THIS_ARG|(JSWAT_JSVAR<<JSWAT_BITS)); if (!fn) return; jsvObjectSetChild(fn, JSPARSE_FUNCTION_THIS_NAME, promise); jsiQueueEvents(promise, fn, &data, 1); jsvUnLock(fn); }
/** * 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 (g_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, g_jsPingCallback, params, 1); } }
/*JSON{ "type" : "method", "class" : "ESPWifi", "name" : "connect", "generate" : "jswrap_esp8266_connect", "params" : [ ["ap","JsVar","Access point name"], ["key","JsVar","WPA2 key (or undefined for unsecured connection)"], ["callback","JsVar","Function to call back with connection status. It has one argument which is one of 'connect'/'disconnect'/'dhcp'"] ], "return" : ["bool",""] } Connect to an access point */ bool jswrap_esp8266_connect(JsVar *wlanObj, JsVar *vAP, JsVar *vKey, JsVar *callback) { NOT_USED(wlanObj); JsNetwork net; if (!networkGetFromVar(&net)) return false; // 'AT+CWMODE=1\r' ? seems to be the default JsVar *msg = jsvVarPrintf("AT+CWJAP=%q,%q\r", vAP, vKey); esp8266_send(msg); jsvUnLock(msg); if (!esp8266_wait_for("OK",500, false)) return false; networkFree(&net); if (callback) jsiQueueEvents(callback, 0, 0); return true; }
/*JSON{ "type" : "staticmethod", "class" : "ESP8266", "name" : "connect", "generate" : "jswrap_esp8266_connect_device", "params" : [ ["serial","JsVar","The Serial port used for communications with the ESP8266 (must already be setup)"], ["callback","JsVar","Function to call back when connected"] ], "return" : ["JsVar","An ESP8266 Object"], "return_object" : "ESP8266" } Initialise the WIZnet module and return an Ethernet object */ JsVar *jswrap_esp8266_connect_device(JsVar *usart, JsVar *callback) { IOEventFlags usartDevice; usartDevice = jsiGetDeviceFromClass(usart); if (!DEVICE_IS_USART(usartDevice)) { jsExceptionHere(JSET_ERROR, "Expecting USART device, got %q", usart); return 0; } JsNetwork net; networkCreate(&net, JSNETWORKTYPE_ESP8266); net.data.device = usartDevice; networkSet(&net); JsVar *wifiObj = 0; JsVar *cmd = jsvNewFromString("AT+RST\r\n"); esp8266_send(cmd); jsvUnLock(cmd); if (esp8266_wait_for("OK", 100, false)) { if (esp8266_wait_for("ready", 4000, false)) { networkState = NETWORKSTATE_ONLINE; wifiObj = jspNewObject(0, "ESPWifi"); } else { jsExceptionHere(JSET_ERROR, "Module not ready"); } } else { jsExceptionHere(JSET_ERROR, "No Acknowledgement"); } networkFree(&net); if (callback) jsiQueueEvents(callback, 0, 0); return wifiObj; }
/*JSON{ "type" : "method", "class" : "Object", "name" : "emit", "generate" : "jswrap_object_emit", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["args","JsVarArray","Optional arguments"] ] } Call the event listeners for this object, for instance ```http.emit('data', 'Foo')```. See Node.js's EventEmitter. */ void jswrap_object_emit(JsVar *parent, JsVar *event, JsVar *argArray) { if (!jsvHasChildren(parent)) { jsWarn("Parent must be an object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.emit(..) must be a string"); return; } JsVar *eventName = jsvVarPrintf(JS_EVENT_PREFIX"%s",event); if (!eventName) return; // no memory // extract data const unsigned int MAX_ARGS = 4; JsVar *args[MAX_ARGS]; unsigned int n = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, argArray); while (jsvObjectIteratorHasValue(&it)) { if (n>=MAX_ARGS) { jsWarn("Too many arguments"); break; } args[n++] = jsvObjectIteratorGetValue(&it); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); JsVar *callback = jsvSkipNameAndUnLock(jsvFindChildFromVar(parent, eventName, 0)); jsvUnLock(eventName); if (callback) jsiQueueEvents(parent, callback, args, (int)n); jsvUnLock(callback); // unlock jsvUnLockMany(n, args); }
/*JSON{ "type" : "method", "class" : "Ethernet", "name" : "getIP", "generate" : "jswrap_ethernet_getIP", "params" : [ ["options","JsVar","An optional `callback(err, ipinfo)` function to be called back with the IP information."] ], "return" : ["JsVar",""] } Get the current IP address, subnet, gateway and mac address. */ JsVar *jswrap_ethernet_getIP(JsVar *wlanObj, JsVar *callback) { NOT_USED(wlanObj); if (networkState != NETWORKSTATE_ONLINE) { jsExceptionHere(JSET_ERROR, "Not connected to the internet"); return 0; } JsNetwork net; if (!networkGetFromVar(&net)) return 0; wiz_NetInfo gWIZNETINFO; ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO); /* If byte 1 is 0 we don't have a valid address */ JsVar *data = jsvNewObject(); networkPutAddressAsString(data, "ip", &gWIZNETINFO.ip[0], 4, 10, '.'); networkPutAddressAsString(data, "subnet", &gWIZNETINFO.sn[0], 4, 10, '.'); networkPutAddressAsString(data, "gateway", &gWIZNETINFO.gw[0], 4, 10, '.'); networkPutAddressAsString(data, "dns", &gWIZNETINFO.dns[0], 4, 10, '.'); networkPutAddressAsString(data, "mac", &gWIZNETINFO.mac[0], 6, 16, ':'); networkFree(&net); // Schedule callback if a function was provided if (jsvIsFunction(callback)) { JsVar *params[2]; params[0] = jsvNewWithFlags(JSV_NULL); params[1] = data; jsiQueueEvents(NULL, callback, params, 2); jsvUnLock(params[0]); } return data; }
/*JSON{ "type" : "staticmethod", "class" : "ESP8266WiFi", "name" : "connect", "generate" : "jswrap_ESP8266WiFi_connect", "params" : [ ["ssid","JsVar","The network id of the access point."], ["password","JsVar","The password to the access point"], ["gotIpCallback", "JsVar", "An optional callback invoked when we have an IP"] ] } * * Connect the station to an access point. */ void jswrap_ESP8266WiFi_connect( JsVar *jsv_ssid, //!< The SSID of the access point to connect. JsVar *jsv_password, //!< The password for the access point. JsVar *gotIpCallback //!< The Callback function to be called when we are connected. ) { os_printf("> jswrap_ESP8266WiFi_connect\n"); // Check that the ssid and password values aren't obviously in error. if (jsv_ssid == NULL || !jsvIsString(jsv_ssid)) { jsExceptionHere(JSET_ERROR, "No SSID."); return; } if (jsv_password == NULL || !jsvIsString(jsv_password)) { jsExceptionHere(JSET_ERROR, "No password."); return; } // Check that if a callback function was supplied that we actually have a callback function. if (gotIpCallback != NULL && !jsvIsUndefined(gotIpCallback) && !jsvIsFunction(gotIpCallback)) { gotIpCallback = NULL; jsExceptionHere(JSET_ERROR, "A callback function was supplied that is not a function."); return; } if (jsvIsUndefined(gotIpCallback) || jsvIsNull(gotIpCallback)) { gotIpCallback = NULL; } // Set the global which is the gotIP callback to null but first unlock it. if (g_jsGotIpCallback != NULL) { jsvUnLock(g_jsGotIpCallback); g_jsGotIpCallback = NULL; } // If we have a callback, save it for later invocation. if (gotIpCallback != NULL) { g_jsGotIpCallback = jsvLockAgainSafe(gotIpCallback); } // Debug // os_printf("jsGotIpCallback=%p\n", jsGotIpCallback); // Create strings from the JsVars for the ESP8266 API calls. char ssid[33]; int len = jsvGetString(jsv_ssid, ssid, sizeof(ssid)-1); ssid[len]='\0'; char password[65]; len = jsvGetString(jsv_password, password, sizeof(password)-1); password[len]='\0'; os_printf("> - ssid=%s, password=%s\n", ssid, password); // Set the WiFi mode of the ESP8266 wifi_set_opmode_current(STATION_MODE); struct station_config stationConfig; memset(&stationConfig, 0, sizeof(stationConfig)); os_strncpy((char *)stationConfig.ssid, ssid, 32); if (password != NULL) { os_strncpy((char *)stationConfig.password, password, 64); } else { os_strcpy((char *)stationConfig.password, ""); } // Set the WiFi configuration wifi_station_set_config(&stationConfig); uint8 wifiConnectStatus = wifi_station_get_connect_status(); os_printf(" - Current connect status: %s\n", wifiConnectStatusToString(wifiConnectStatus)); if (wifiConnectStatus == STATION_GOT_IP) { // See issue #618. There are currently three schools of thought on what should happen // when a connect is issued and we are already connected. // // Option #1 - Always perform a disconnect. // Option #2 - Perform a disconnect if the SSID or PASSWORD are different from current // Option #3 - Fail the connect if we are already connected. // #define ISSUE_618 1 #if ISSUE_618 == 1 wifi_station_disconnect(); #elif ISSUE_618 == 2 struct station_config existingConfig; wifi_station_get_config(&existingConfig); if (os_strncmp((char *)existingConfig.ssid, (char *)stationConfig.ssid, 32) == 0 && os_strncmp((char *)existingConfig.password, (char *)stationConfig.password, 64) == 0) { if (jsGotIpCallback != NULL) { JsVar *params[2]; params[0] = jsvNewFromInteger(STATION_GOT_IP); params[1] = jsvNewNull(); jsiQueueEvents(NULL, jsGotIpCallback, params, 2); } return; } else { wifi_station_disconnect(); } #elif ISSUE_618 == 3 // Add a return code to the function and return an already connected error. #endif } // Perform the network level connection. wifi_station_connect(); os_printf("< jswrap_ESP8266WiFi_connect\n"); }
/** * Callback function that is invoked at the culmination of a scan. */ static void scanCB(void *arg, STATUS status) { /** * Create a JsVar that is an array of JS objects where each JS object represents a * retrieved access point set of information. The structure of a record will be: * o authMode * o isHidden * o rssi * o channel * o ssid * When the array has been built, invoke the callback function passing in the array * of records. */ os_printf(">> scanCB\n"); // Create the Empty JS array that will be passed as a parameter to the callback. JsVar *accessPointArray = jsvNewArray(NULL, 0); struct bss_info *bssInfo; bssInfo = (struct bss_info *)arg; 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 \96 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); } // We have now completed the scan callback, so now we can invoke the JS callback. JsVar *params[1]; params[0] = accessPointArray; jsiQueueEvents(NULL, g_jsScanCallback, params, 1); jsvUnLock(g_jsScanCallback); os_printf("<< scanCB\n"); }
/*JSON{ "type" : "method", "class" : "Ethernet", "name" : "setIP", "generate" : "jswrap_ethernet_setIP", "params" : [ ["options","JsVar","Object containing IP address options `{ ip : '1,2,3,4', subnet, gateway, dns, mac }`, or do not supply an object in order to force DHCP."], ["options","JsVar","An optional `callback(err)` function to invoke when ip is set. `err==null` on success, or a string on failure."] ], "return" : ["bool","True on success"] } Set the current IP address or get an IP from DHCP (if no options object is specified) If 'mac' is specified as an option, it must be a string of the form `"00:01:02:03:04:05"` */ bool jswrap_ethernet_setIP(JsVar *wlanObj, JsVar *options, JsVar *callback) { NOT_USED(wlanObj); if (networkState != NETWORKSTATE_ONLINE) { jsExceptionHere(JSET_ERROR, "Not connected to the internet"); return false; } JsNetwork net; if (!networkGetFromVar(&net)) return false; const char *errorMessage = 0; wiz_NetInfo gWIZNETINFO; ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO); if (!gWIZNETINFO.mac[0] && !gWIZNETINFO.mac[1] && !gWIZNETINFO.mac[2] && !gWIZNETINFO.mac[3] && !gWIZNETINFO.mac[4] && !gWIZNETINFO.mac[5]) { // wow - no mac address - WIZ550BoB? Set up a simple one // in WIZnet's range of addresses gWIZNETINFO.mac[0]=0x00; gWIZNETINFO.mac[1]=0x08; gWIZNETINFO.mac[2]=0xDC; gWIZNETINFO.mac[3]=0x01; gWIZNETINFO.mac[4]=0x02; gWIZNETINFO.mac[5]=0x03; } if (jsvIsObject(options)) { _eth_getIP_set_address(options, "ip", &gWIZNETINFO.ip[0]); _eth_getIP_set_address(options, "subnet", &gWIZNETINFO.sn[0]); _eth_getIP_set_address(options, "gateway", &gWIZNETINFO.gw[0]); _eth_getIP_set_address(options, "dns", &gWIZNETINFO.dns[0]); JsVar *info = jsvObjectGetChild(options, "mac", 0); if (info) { char buf[64]; jsvGetString(info, buf, sizeof(buf)); networkParseMACAddress(&gWIZNETINFO.mac[0], buf); // TODO: check failure? jsvUnLock(info); } gWIZNETINFO.dhcp = NETINFO_STATIC; errorMessage = 0; // all ok } else { // DHCP uint8_t DHCPisSuccess = getIP_DHCPS(net_wiznet_getFreeSocket(), &gWIZNETINFO); if (DHCPisSuccess == 1) { // info in lease_time.lVal errorMessage = 0; // all ok } else { errorMessage = "DHCP failed"; jsWarn(errorMessage); } } ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO); networkFree(&net); // Schedule callback if a function was provided if (jsvIsFunction(callback)) { JsVar *params[1]; params[0] = errorMessage ? jsvNewFromString(errorMessage) : jsvNewWithFlags(JSV_NULL); jsiQueueEvents(NULL, callback, params, 1); jsvUnLock(params[0]); } return errorMessage==0; }