/*JSON{ "type":"method", "class": "Serial", "name" : "setup", "description" : ["Setup this Serial port with the given baud rate and options.", "If not specified in options, the default pins are used (usually the lowest numbered pins on the lowest port that supports this peripheral)"], "generate" : "jswrap_serial_setup", "params" : [ [ "baudrate", "JsVar", "The baud rate - the default is 9600"], [ "options", "JsVar", ["An optional structure containing extra information on initialising the serial port.", "```{rx:pin,tx:pin,bytesize:8,parity:null/'none'/'o'/'odd'/'e'/'even',stopbits:1}```", "You can find out which pins to use by looking at [your board's reference page](#boards) and searching for pins with the `UART`/`USART` markers.", "Note that even after changing the RX and TX pins, if you have called setup before then the previous RX and TX pins will still be connected to the Serial port as well - until you set them to something else using digitalWrite" ] ] ] }*/ void jswrap_serial_setup(JsVar *parent, JsVar *baud, JsVar *options) { IOEventFlags device = jsiGetDeviceFromClass(parent); if (!DEVICE_IS_USART(device)) return; JshUSARTInfo inf; jshUSARTInitInfo(&inf); if (!jsvIsUndefined(baud)) { int b = (int)jsvGetInteger(baud); if (b<=100 || b > 10000000) jsExceptionHere(JSET_ERROR, "Invalid baud rate specified"); else inf.baudRate = b; } if (jsvIsObject(options)) { inf.pinRX = jshGetPinFromVarAndUnLock(jsvObjectGetChild(options, "rx", 0)); inf.pinTX = jshGetPinFromVarAndUnLock(jsvObjectGetChild(options, "tx", 0)); JsVar *v; v = jsvObjectGetChild(options, "bytesize", 0); if (jsvIsInt(v)) inf.bytesize = (unsigned char)jsvGetInteger(v); jsvUnLock(v); inf.parity = 0; v = jsvObjectGetChild(options, "parity", 0); if(jsvIsString(v)) { if(jsvIsStringEqual(v, "o") || jsvIsStringEqual(v, "odd")) inf.parity = 1; else if(jsvIsStringEqual(v, "e") || jsvIsStringEqual(v, "even")) inf.parity = 2; } else if(jsvIsInt(v)) { inf.parity = (unsigned char)jsvGetInteger(v); } jsvUnLock(v); if (inf.parity>2) { jsExceptionHere(JSET_ERROR, "Invalid parity %d", inf.parity); return; } v = jsvObjectGetChild(options, "stopbits", 0); if (jsvIsInt(v)) inf.stopbits = (unsigned char)jsvGetInteger(v); jsvUnLock(v); } jshUSARTSetup(device, &inf); // Set baud rate in object, so we can initialise it on startup if (inf.baudRate != DEFAULT_BAUD_RATE) { jsvUnLock(jsvObjectSetChild(parent, USART_BAUDRATE_NAME, jsvNewFromInteger(inf.baudRate))); } else jsvRemoveNamedChild(parent, USART_BAUDRATE_NAME); // Do the same for options if (options) jsvUnLock(jsvSetNamedChild(parent, options, DEVICE_OPTIONS_NAME)); else jsvRemoveNamedChild(parent, DEVICE_OPTIONS_NAME); }
/*JSON{ "type":"method", "class": "Object", "name" : "toString", "description" : "Convert the Object to a string", "generate" : "jswrap_object_toString", "params" : [ [ "radix", "JsVar", "If the object is an integer, the radix (between 2 and 36) to use. NOTE: Setting a radix does not work on floating point numbers."] ], "return" : ["JsVar", "A String representing the object"] }*/ JsVar *jswrap_object_toString(JsVar *parent, JsVar *arg0) { if (jsvIsInt(arg0) && jsvIsInt(parent)) { JsVarInt radix = jsvGetInteger(arg0); if (radix>=2 && radix<=36) { char buf[JS_NUMBER_BUFFER_SIZE]; itoa(parent->varData.integer, buf, (unsigned int)radix); return jsvNewFromString(buf); } } return jsvAsString(parent, false); }
/*JSON{ "type" : "method", "class" : "Object", "name" : "toString", "generate" : "jswrap_object_toString", "params" : [ ["radix","JsVar","If the object is an integer, the radix (between 2 and 36) to use. NOTE: Setting a radix does not work on floating point numbers."] ], "return" : ["JsVar","A String representing the object"] } Convert the Object to a string */ JsVar *jswrap_object_toString(JsVar *parent, JsVar *arg0) { if (jsvIsInt(arg0) && jsvIsNumeric(parent)) { JsVarInt radix = jsvGetInteger(arg0); if (radix>=2 && radix<=36) { char buf[JS_NUMBER_BUFFER_SIZE]; if (jsvIsInt(parent)) itostr(jsvGetInteger(parent), buf, (unsigned int)radix); else ftoa_bounded_extra(jsvGetFloat(parent), buf, sizeof(buf), (int)radix, -1); return jsvNewFromString(buf); } } return jsvAsString(parent, false); }
/*JSON{ "type" : "method", "class" : "Array", "name" : "reduce", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_array_reduce", "params" : [ ["callback","JsVar","Function used to reduce the array"], ["initialValue","JsVar","if specified, the initial value to pass to the function"] ], "return" : ["JsVar","The value returned by the last function called"] } Execute `previousValue=initialValue` and then `previousValue = callback(previousValue, currentValue, index, array)` for each element in the array, and finally return previousValue. */ JsVar *jswrap_array_reduce(JsVar *parent, JsVar *funcVar, JsVar *initialValue) { const char *name = "reduce"; if (!jsvIsIterable(parent)) { jsExceptionHere(JSET_ERROR, "Array.%s can only be called on something iterable", name); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "Array.%s's first argument should be a function", name); return 0; } JsVar *previousValue = initialValue ? jsvLockAgain(initialValue) : 0; JsvIterator it; jsvIteratorNew(&it, parent); if (!previousValue) { bool isDone = false; while (!isDone && jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { previousValue = jsvIteratorGetValue(&it); isDone = true; } jsvUnLock(index); jsvIteratorNext(&it); } if (!previousValue) { jsExceptionHere(JSET_ERROR, "Array.%s without initial value required non-empty array", name); } } while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[4]; args[0] = previousValue; args[1] = jsvIteratorGetValue(&it); args[2] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[3] = parent; previousValue = jspeFunctionCall(funcVar, 0, 0, false, 4, args); jsvUnLock(args[0]); jsvUnLock(args[1]); jsvUnLock(args[2]); } jsvUnLock(index); jsvIteratorNext(&it); } jsvIteratorFree(&it); return previousValue; }
/*JSON{ "type" : "function", "name" : "show", "generate" : "jswrap_microbit_show", "params" : [ ["image","JsVar","The image to show"] ] } Show an image on the in-built 5x5 LED screen. Image can be: * A number where each bit represents a pixel (so 25 bits) */ void jswrap_microbit_show(JsVar *image) { if (!jsvIsInt(image)) { jsError("Expecting a number, got %t\n", image); return; } uint32_t newState = jsvGetInteger(image); if ((newState!=0) && (microbitLEDState==0)) { // we want to display something but we don't have an interval jstExecuteFn(jswrap_microbit_display_callback, jshGetTimeFromMilliseconds(5)); // and also set pins to outputs nrf_gpio_cfg_output(MB_LED_COL1); nrf_gpio_cfg_output(MB_LED_COL2); nrf_gpio_cfg_output(MB_LED_COL3); nrf_gpio_cfg_output(MB_LED_COL4); nrf_gpio_cfg_output(MB_LED_COL5); nrf_gpio_cfg_output(MB_LED_COL6); nrf_gpio_cfg_output(MB_LED_COL7); nrf_gpio_cfg_output(MB_LED_COL8); nrf_gpio_cfg_output(MB_LED_COL9); nrf_gpio_cfg_output(MB_LED_ROW1); nrf_gpio_cfg_output(MB_LED_ROW2); nrf_gpio_cfg_output(MB_LED_ROW3); } else if ((newState==0) && (microbitLEDState!=0)) { jswrap_microbit_stopDisplay(); } microbitLEDState = newState; }
JsVar *jswrap_typedarray_constructor(JsVarDataArrayBufferViewType type, JsVar *arr, JsVarInt byteOffset, JsVarInt length) { JsVar *arrayBuffer = 0; if (jsvIsArrayBuffer(arr)) { arrayBuffer = jsvLockAgain(arr); } else if (jsvIsInt(arr)) { length = jsvGetInteger(arr); byteOffset = 0; arrayBuffer = jswrap_arraybuffer_constructor(JSV_ARRAYBUFFER_GET_SIZE(type)*length); } else if (jsvIsArray(arr)) { length = jsvGetArrayLength(arr); byteOffset = 0; arrayBuffer = jswrap_arraybuffer_constructor(JSV_ARRAYBUFFER_GET_SIZE(type)*length); // later on we'll populate this } if (!arrayBuffer) { jsError("Unsupported first argument\n"); return 0; } if (length<=0) length = (JsVarInt)jsvGetArrayBufferLength(arrayBuffer) / JSV_ARRAYBUFFER_GET_SIZE(type); JsVar *typedArr = jsvNewWithFlags(JSV_ARRAYBUFFER); if (typedArr) { typedArr->varData.arraybuffer.type = type; typedArr->varData.arraybuffer.byteOffset = (unsigned short)byteOffset; typedArr->varData.arraybuffer.length = (unsigned short)length; typedArr->firstChild = jsvGetRef(jsvRef(arrayBuffer)); if (jsvIsArray(arr)) { // if we were given an array, populate this ArrayBuffer JsvArrayIterator it; jsvArrayIteratorNew(&it, arr); while (jsvArrayIteratorHasElement(&it)) { JsVar *idx = jsvArrayIteratorGetIndex(&it); if (jsvIsInt(idx)) { JsVar *val = jsvArrayIteratorGetElement(&it); jsvArrayBufferSet(typedArr, jsvGetInteger(idx), val); jsvUnLock(val); } jsvUnLock(idx); jsvArrayIteratorNext(&it); } jsvArrayIteratorFree(&it); } } jsvUnLock(arrayBuffer); return typedArr; }
/*JSON{ "type" : "constructor", "class" : "Pin", "name" : "Pin", "generate" : "jswrap_pin_constructor", "params" : [ ["value", "JsVar", "A value to be converted to a pin. Can be a number, pin, or String."] ], "return" : ["JsVar","A Pin object"] } Creates a pin from the given argument (or returns undefined if no argument) */ JsVar *jswrap_pin_constructor(JsVar *val) { Pin pin = jshGetPinFromVar(val); if (!jshIsPinValid(pin)) return 0; #ifdef ESP8266 if (jsvIsInt(val) && !jsvIsPin(val)) jsWarn("The Pin() constructor is deprecated. Please use `D%d`, or NodeMCU.Dx instead", pin); #endif return jsvNewFromPin(pin); }
/*JSON{ "type":"method", "class": "Serial", "name" : "setup", "description" : "Setup this Serial port with the given baud rate and options", "generate" : "jswrap_serial_setup", "params" : [ [ "baudrate", "int", "The baud rate - the default is 9600"], [ "options", "JsVar", ["An optional structure containing extra information on initialising the serial port.", "```{rx:pin,tx:pin}```", "Note that even after changing the RX and TX pins, if you have called setup before then the previous RX and TX pins will still be connected to the Serial port as well - until you set them to something else using digitalWrite" ] ] ] }*/ void jswrap_serial_setup(JsVar *parent, JsVarInt baud, JsVar *options) { IOEventFlags device = jsiGetDeviceFromClass(parent); JshUSARTInfo inf; jshUSARTInitInfo(&inf); if (baud>0) inf.baudRate = (int)baud; if (jsvIsObject(options)) { inf.pinRX = jshGetPinFromVarAndUnLock(jsvObjectGetChild(options, "rx", 0)); inf.pinTX = jshGetPinFromVarAndUnLock(jsvObjectGetChild(options, "tx", 0)); inf.bytesize = (unsigned char)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "bytesize", 0)); JsVar *v; v = jsvObjectGetChild(options, "parity", 0); if(jsvIsNull(v)) { inf.parity = 0; } else if(jsvIsString(v)) { inf.parity = 0xFF; char s[8] = ""; jsvGetString(v, s, sizeof(s) - 1); if(!strcmp(s, "o") || !strcmp(s, "odd")) { inf.parity = 1; } else if(!strcmp(s, "e") || !strcmp(s, "even")) { inf.parity = 2; } } else if(jsvIsInt(v)) { inf.parity = (unsigned char)jsvGetInteger(v); } jsvUnLock(v); v = jsvObjectGetChild(options, "stopbits", 0); inf.stopbits = (unsigned char)jsvGetInteger(v); jsvUnLock(v); } jshUSARTSetup(device, &inf); // Set baud rate in object, so we can initialise it on startup if (baud != DEFAULT_BAUD_RATE) { JsVar *baudVar = jsvNewFromInteger(baud); jsvUnLock(jsvSetNamedChild(parent, baudVar, USART_BAUDRATE_NAME)); jsvUnLock(baudVar); } else jsvRemoveNamedChild(parent, USART_BAUDRATE_NAME); // Do the same for options if (options) jsvUnLock(jsvSetNamedChild(parent, options, DEVICE_OPTIONS_NAME)); else jsvRemoveNamedChild(parent, DEVICE_OPTIONS_NAME); }
/*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, '.'); }
/*JSON{ "type" : "method", "class" : "ArrayBufferView", "name" : "map", "generate" : "jswrap_arraybufferview_map", "params" : [ ["function","JsVar","Function used to map one item to another"], ["thisArg","JsVar","if specified, the function is called with 'this' set to thisArg (optional)"] ], "return" : ["JsVar","An array containing the results"], "return_object" : "ArrayBufferView" } Return an array which is made from the following: ```A.map(function) = [function(A[0]), function(A[1]), ...]``` **Note:** This returns an ArrayBuffer of the same type it was called on. To get an Array, use `Array.prototype.map` */ JsVar *jswrap_arraybufferview_map(JsVar *parent, JsVar *funcVar, JsVar *thisVar) { if (!jsvIsArrayBuffer(parent)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map can only be called on an ArrayBufferView"); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's first argument should be a function"); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's second argument should be undefined, or an object"); return 0; } // create ArrayBuffer result JsVarDataArrayBufferViewType arrayBufferType = parent->varData.arraybuffer.type; JsVar *arrayBufferLength = jsvNewFromInteger((JsVarInt)jsvGetArrayBufferLength(parent)); JsVar *array = jswrap_typedarray_constructor(arrayBufferType, arrayBufferLength, 0, 0); jsvUnLock(arrayBufferLength); if (!array) return 0; // now iterate JsvIterator it; // TODO: if we really are limited to ArrayBuffers, this could be an ArrayBufferIterator. jsvIteratorNew(&it, parent); JsvArrayBufferIterator itdst; jsvArrayBufferIteratorNew(&itdst, array, 0); while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *mapped; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); if (mapped) { jsvArrayBufferIteratorSetValue(&itdst, mapped); jsvUnLock(mapped); } } jsvUnLock(index); jsvIteratorNext(&it); jsvArrayBufferIteratorNext(&itdst); } jsvIteratorFree(&it); jsvArrayBufferIteratorFree(&itdst); return array; }
JsVar *jswrap_typedarray_constructor(JsVarDataArrayBufferViewType type, JsVar *arr, JsVarInt byteOffset, JsVarInt length) { JsVar *arrayBuffer = 0; // Only allow use of byteOffset/length if we're passing an ArrayBuffer - NOT A VIEW. bool copyData = false; if (jsvIsArrayBuffer(arr) && arr->varData.arraybuffer.type==ARRAYBUFFERVIEW_ARRAYBUFFER) { arrayBuffer = jsvLockAgain(arr); } else if (jsvIsNumeric(arr)) { length = jsvGetInteger(arr); byteOffset = 0; arrayBuffer = jswrap_arraybuffer_constructor((int)JSV_ARRAYBUFFER_GET_SIZE(type)*length); } else if (jsvIsArray(arr) || jsvIsArrayBuffer(arr)) { length = (JsVarInt)jsvGetLength(arr); byteOffset = 0; arrayBuffer = jswrap_arraybuffer_constructor((int)JSV_ARRAYBUFFER_GET_SIZE(type)*length); copyData = true; // so later on we'll populate this } if (!arrayBuffer) { jsExceptionHere(JSET_ERROR, "Unsupported first argument of type %t\n", arr); return 0; } if (length==0) { length = ((JsVarInt)jsvGetArrayBufferLength(arrayBuffer)-byteOffset) / (JsVarInt)JSV_ARRAYBUFFER_GET_SIZE(type); if (length<0) length=0; } JsVar *typedArr = jsvNewWithFlags(JSV_ARRAYBUFFER); if (typedArr) { typedArr->varData.arraybuffer.type = type; typedArr->varData.arraybuffer.byteOffset = (unsigned short)byteOffset; typedArr->varData.arraybuffer.length = (unsigned short)length; jsvSetFirstChild(typedArr, jsvGetRef(jsvRef(arrayBuffer))); if (copyData) { // if we were given an array, populate this ArrayBuffer JsvIterator it; jsvIteratorNew(&it, arr, JSIF_DEFINED_ARRAY_ElEMENTS); while (jsvIteratorHasElement(&it)) { JsVar *idx = jsvIteratorGetKey(&it); if (jsvIsInt(idx)) { JsVar *val = jsvIteratorGetValue(&it); // TODO: This is horrible! We need to try and iterate properly... jsvArrayBufferSet(typedArr, (size_t)jsvGetInteger(idx), val); jsvUnLock(val); } jsvUnLock(idx); jsvIteratorNext(&it); } jsvIteratorFree(&it); } } jsvUnLock(arrayBuffer); return typedArr; }
JsVar *_jswrap_array_map_or_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool isMap) { const char *name = isMap ? "map":"forEach"; if (!jsvIsIterable(parent)) { jsError("Array.%s can only be called on something iterable", name); return 0; } if (!jsvIsFunction(funcVar)) { jsError("Array.%s's first argument should be a function", name); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsError("Array.%s's second argument should be undefined, or an object", name); return 0; } JsVar *array = 0; if (isMap) array = jsvNewWithFlags(JSV_ARRAY); if (array || !isMap) { JsvIterator it; jsvIteratorNew(&it, parent); while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *mapped; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); if (mapped) { if (isMap) { JsVar *name = jsvNewFromInteger(idxValue); if (name) { // out of memory? jsvMakeIntoVariableName(name, mapped); jsvAddName(array, name); jsvUnLock(name); } } jsvUnLock(mapped); } } jsvUnLock(index); jsvIteratorNext(&it); } jsvIteratorFree(&it); } return array; }
/*JSON{ "type" : "staticmethod", "class" : "ESP8266WiFi", "name" : "updateCPUFreq", "generate" : "jswrap_ESP8266WiFi_updateCPUFreq", "params" : [ ["freq", "JsVar", "Desired frequency - either 80 or 160."] ] } * Update the operating frequency of the ESP8266 processor. */ void jswrap_ESP8266WiFi_updateCPUFreq( JsVar *jsFreq //!< Operating frequency of the processor. Either 80 or 160. ) { if (!jsvIsInt(jsFreq)) { jsExceptionHere(JSET_ERROR, "Invalid frequency."); return; } int newFreq = jsvGetInteger(jsFreq); if (newFreq != 80 && newFreq != 160) { jsExceptionHere(JSET_ERROR, "Invalid frequency value, must be 80 or 160."); return; } system_update_cpu_freq(newFreq); }
/*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 (g_jsPingCallback != NULL) { jsvUnLock(g_jsPingCallback); } g_jsPingCallback = NULL; } else if (!jsvIsFunction(pingCallback)) { jsExceptionHere(JSET_ERROR, "Callback is not a function."); return; } else { if (g_jsPingCallback != NULL) { jsvUnLock(g_jsPingCallback); } g_jsPingCallback = pingCallback; jsvLockAgainSafe(g_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); }
/*JSON{ "type" : "constructor", "class" : "Array", "name" : "Array", "generate" : "jswrap_array_constructor", "params" : [ ["args","JsVarArray","The length of the array OR any number of items to add to the array"] ], "return" : ["JsVar","An Array"] } Create an Array. Either give it one integer argument (>=0) which is the length of the array, or any number of arguments */ JsVar *jswrap_array_constructor(JsVar *args) { assert(args); if (jsvGetArrayLength(args)==1) { JsVar *firstArg = jsvSkipNameAndUnLock(jsvGetArrayItem(args,0)); if (jsvIsInt(firstArg) && jsvGetInteger(firstArg)>=0) { JsVarInt count = jsvGetInteger(firstArg); if (count>0) { JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; // out of memory jsvSetArrayLength(arr, count, false); jsvUnLock(firstArg); return arr; } } jsvUnLock(firstArg); } // Otherwise, we just return the array! return jsvLockAgain(args); }
JsVar *_jswrap_array_map_or_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool isMap) { if (!jsvIsFunction(funcVar)) { jsError("Array.map's first argument should be a function"); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsError("Arraymap's second argument should be undefined, or an object"); return 0; } JsVar *array = 0; if (isMap) array = jsvNewWithFlags(JSV_ARRAY); if (array || !isMap) { JsVarRef childRef = parent->firstChild; while (childRef) { JsVar *child = jsvLock(childRef); if (jsvIsInt(child)) { JsVar *args[3], *mapped; args[0] = jsvLock(child->firstChild); // child is a variable name, create a new variable for the index args[1] = jsvNewFromInteger(jsvGetInteger(child)); args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); if (mapped) { if (isMap) { JsVar *name = jsvCopyNameOnly(child, false/*linkChildren*/, true/*keepAsName*/); if (name) { // out of memory? name->firstChild = jsvGetRef(jsvRef(mapped)); jsvAddName(array, name); jsvUnLock(name); } } jsvUnLock(mapped); } } childRef = child->nextSibling; jsvUnLock(child); } } return array; }
/*JSON{ "type":"constructor", "class": "Array", "name": "Array", "description" : "Create an Array. Either give it one integer argument (>=0) which is the length of the array, or any number of arguments ", "generate" : "jswrap_array_constructor", "params" : [ [ "args", "JsVarArray", "The length of the array OR any number of items to add to the array" ] ], "return" : [ "JsVar", "An Array" ] }*/ JsVar *jswrap_array_constructor(JsVar *args) { assert(args); if (jsvGetArrayLength(args)==1) { JsVar *firstArg = jsvSkipNameAndUnLock(jsvArrayGetLast(args)); // also the first! if (jsvIsInt(firstArg) && jsvGetInteger(firstArg)>=0) { JsVarInt count = jsvGetInteger(firstArg); // we cheat - no need to fill the array - just the last element if (count>0) { JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; // out of memory JsVar *idx = jsvMakeIntoVariableName(jsvNewFromInteger(count-1), 0); if (idx) { // could be out of memory jsvAddName(arr, idx); jsvUnLock(idx); } jsvUnLock(firstArg); return arr; } } jsvUnLock(firstArg); } // Otherwise, we just return the array! return jsvLockAgain(args); }
/*JSON{ "type" : "method", "class" : "Serial", "name" : "setup", "generate" : "jswrap_serial_setup", "params" : [ ["baudrate","JsVar","The baud rate - the default is 9600"], ["options","JsVar",["An optional structure containing extra information on initialising the serial port.","```{rx:pin,tx:pin,bytesize:8,parity:null/'none'/'o'/'odd'/'e'/'even',stopbits:1,flow:null/undefined/'none'/'xon'}```","You can find out which pins to use by looking at [your board's reference page](#boards) and searching for pins with the `UART`/`USART` markers.","Note that even after changing the RX and TX pins, if you have called setup before then the previous RX and TX pins will still be connected to the Serial port as well - until you set them to something else using digitalWrite"]] ] } Setup this Serial port with the given baud rate and options. If not specified in options, the default pins are used (usually the lowest numbered pins on the lowest port that supports this peripheral) */ void jswrap_serial_setup(JsVar *parent, JsVar *baud, JsVar *options) { IOEventFlags device = jsiGetDeviceFromClass(parent); if (!DEVICE_IS_USART(device)) return; JshUSARTInfo inf; jshUSARTInitInfo(&inf); if (jsvIsUndefined(options)) { options = jsvObjectGetChild(parent, DEVICE_OPTIONS_NAME, 0); } else jsvLockAgain(options); JsVar *parity = 0; JsVar *flow = 0; jsvConfigObject configs[] = { {"rx", JSV_PIN, &inf.pinRX}, {"tx", JSV_PIN, &inf.pinTX}, {"ck", JSV_PIN, &inf.pinCK}, {"bytesize", JSV_INTEGER, &inf.bytesize}, {"stopbits", JSV_INTEGER, &inf.stopbits}, {"parity", JSV_OBJECT /* a variable */, &parity}, {"flow", JSV_OBJECT /* a variable */, &flow}, }; if (!jsvIsUndefined(baud)) { int b = (int)jsvGetInteger(baud); if (b<=100 || b > 10000000) jsExceptionHere(JSET_ERROR, "Invalid baud rate specified"); else inf.baudRate = b; } bool ok = true; if (jsvReadConfigObject(options, configs, sizeof(configs) / sizeof(jsvConfigObject))) { // sort out parity inf.parity = 0; if(jsvIsString(parity)) { if (jsvIsStringEqual(parity, "o") || jsvIsStringEqual(parity, "odd")) inf.parity = 1; else if (jsvIsStringEqual(parity, "e") || jsvIsStringEqual(parity, "even")) inf.parity = 2; } else if (jsvIsInt(parity)) { inf.parity = (unsigned char)jsvGetInteger(parity); } if (inf.parity>2) { jsExceptionHere(JSET_ERROR, "Invalid parity %d", inf.parity); ok = false; } if (ok) { if (jsvIsUndefined(flow) || jsvIsNull(flow) || jsvIsStringEqual(flow, "none")) inf.xOnXOff = false; else if (jsvIsStringEqual(flow, "xon")) inf.xOnXOff = true; else { jsExceptionHere(JSET_ERROR, "Invalid flow control: %q", flow); ok = false; } } #ifdef LINUX if (ok && jsvIsObject(options)) jsvObjectSetChildAndUnLock(parent, "path", jsvObjectGetChild(options, "path", 0)); #endif } jsvUnLock(parity); jsvUnLock(flow); if (!ok) { jsvUnLock(options); return; } jshUSARTSetup(device, &inf); // Set baud rate in object, so we can initialise it on startup jsvObjectSetChildAndUnLock(parent, USART_BAUDRATE_NAME, jsvNewFromInteger(inf.baudRate)); // Do the same for options if (options) jsvObjectSetChildAndUnLock(parent, DEVICE_OPTIONS_NAME, options); else jsvRemoveNamedChild(parent, DEVICE_OPTIONS_NAME); }
/** Write the pin name to a string. String must have at least 8 characters (to be safe) */ void jshGetPinString(char *result, Pin pin) { result[0] = 0; // just in case #ifdef PIN_NAMES_DIRECT if (jshIsPinValid(pin)) { result[0]='A'+pinInfo[pin].port-JSH_PORTA; itostr(pinInfo[pin].pin-JSH_PIN0,&result[1],10); #else if ( #if JSH_PORTA_OFFSET!=0 pin>=JSH_PORTA_OFFSET && #endif pin<JSH_PORTA_OFFSET+JSH_PORTA_COUNT) { result[0]='A'; itostr(pin-JSH_PORTA_OFFSET,&result[1],10); } else if (pin>=JSH_PORTB_OFFSET && pin<JSH_PORTB_OFFSET+JSH_PORTB_COUNT) { result[0]='B'; itostr(pin-JSH_PORTB_OFFSET,&result[1],10); } else if (pin>=JSH_PORTC_OFFSET && pin<JSH_PORTC_OFFSET+JSH_PORTC_COUNT) { result[0]='C'; itostr(pin-JSH_PORTC_OFFSET,&result[1],10); } else if (pin>=JSH_PORTD_OFFSET && pin<JSH_PORTD_OFFSET+JSH_PORTD_COUNT) { result[0]='D'; itostr(pin-JSH_PORTD_OFFSET,&result[1],10); #if JSH_PORTE_OFFSET!=-1 } else if (pin>=JSH_PORTE_OFFSET && pin<JSH_PORTE_OFFSET+JSH_PORTE_COUNT) { result[0]='E'; itostr(pin-JSH_PORTE_OFFSET,&result[1],10); #endif #if JSH_PORTF_OFFSET!=-1 } else if (pin>=JSH_PORTF_OFFSET && pin<JSH_PORTF_OFFSET+JSH_PORTF_COUNT) { result[0]='F'; itostr(pin-JSH_PORTF_OFFSET,&result[1],10); #endif #if JSH_PORTG_OFFSET!=-1 } else if (pin>=JSH_PORTG_OFFSET && pin<JSH_PORTG_OFFSET+JSH_PORTG_COUNT) { result[0]='G'; itostr(pin-JSH_PORTG_OFFSET,&result[1],10); #endif #if JSH_PORTH_OFFSET!=-1 } else if (pin>=JSH_PORTH_OFFSET && pin<JSH_PORTH_OFFSET+JSH_PORTH_COUNT) { result[0]='H'; itostr(pin-JSH_PORTH_OFFSET,&result[1],10); #endif #endif } else { strncpy(result, "UNKNOWN", 8); } } /// Given a var, convert it to a pin ID (or -1 if it doesn't exist). safe for undefined! Pin jshGetPinFromVar(JsVar *pinv) { if (jsvIsString(pinv) && pinv->varData.str[5]==0/*should never be more than 4 chars!*/) { return jshGetPinFromString(&pinv->varData.str[0]); } else if (jsvIsInt(pinv) /* This also tests for the Pin datatype */) { return (Pin)jsvGetInteger(pinv); } else return PIN_UNDEFINED; } Pin jshGetPinFromVarAndUnLock(JsVar *pinv) { Pin pin = jshGetPinFromVar(pinv); jsvUnLock(pinv); return pin; }
/*JSON{ "type":"method", "class": "Array", "name" : "splice", "description" : "Both remove and add items to an array", "generate" : "jswrap_array_splice", "params" : [ [ "index", "int", "Index at which to start changing the array. If negative, will begin that many elements from the end"], [ "howMany", "JsVar", "An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed."], [ "element1", "JsVar", "A new item to add (optional)" ], [ "element2", "JsVar", "A new item to add (optional)" ], [ "element3", "JsVar", "A new item to add (optional)" ], [ "element4", "JsVar", "A new item to add (optional)" ], [ "element5", "JsVar", "A new item to add (optional)" ], [ "element6", "JsVar", "A new item to add (optional)" ] ], "return" : ["JsVar", "An array containing the removed elements. If only one element is removed, an array of one element is returned."] }*/ JsVar *jswrap_array_splice(JsVar *parent, JsVarInt index, JsVar *howManyVar, JsVar *element1, JsVar *element2, JsVar *element3, JsVar *element4, JsVar *element5, JsVar *element6) { JsVarInt len = jsvGetArrayLength(parent); if (index<0) index+=len; if (index<0) index=0; if (index>len) index=len; JsVarInt howMany = len; // how many to delete! if (jsvIsInt(howManyVar)) howMany = jsvGetInteger(howManyVar); if (howMany > len-index) howMany = len-index; JsVarInt newItems = 0; if (element1) newItems++; if (element2) newItems++; if (element3) newItems++; if (element4) newItems++; if (element5) newItems++; if (element6) newItems++; JsVarInt shift = newItems-howMany; bool needToAdd = false; JsVar *result = jsvNewWithFlags(JSV_ARRAY); JsArrayIterator it; jsvArrayIteratorNew(&it, parent); while (jsvArrayIteratorHasElement(&it) && !needToAdd) { bool goToNext = true; JsVar *idxVar = jsvArrayIteratorGetIndex(&it); if (idxVar && jsvIsInt(idxVar)) { JsVarInt idx = jsvGetInteger(idxVar); if (idx<index) { // do nothing... } else if (idx<index+howMany) { // must delete if (result) { // append to result array JsVar *el = jsvArrayIteratorGetElement(&it); jsvArrayPush(result, el); jsvUnLock(el); } // delete goToNext = false; JsVar *toRemove = jsvArrayIteratorGetIndex(&it); jsvArrayIteratorNext(&it); jsvRemoveChild(parent, toRemove); jsvUnLock(toRemove); } else { // we're greater than the amount we need to remove now needToAdd = true; goToNext = false; } } jsvUnLock(idxVar); if (goToNext) jsvArrayIteratorNext(&it); } // now we add everything JsVar *beforeIndex = jsvArrayIteratorGetIndex(&it); if (element1) jsvArrayInsertBefore(parent, beforeIndex, element1); if (element2) jsvArrayInsertBefore(parent, beforeIndex, element2); if (element3) jsvArrayInsertBefore(parent, beforeIndex, element3); if (element4) jsvArrayInsertBefore(parent, beforeIndex, element4); if (element5) jsvArrayInsertBefore(parent, beforeIndex, element5); if (element6) jsvArrayInsertBefore(parent, beforeIndex, element6); jsvUnLock(beforeIndex); // And finally renumber while (jsvArrayIteratorHasElement(&it)) { JsVar *idxVar = jsvArrayIteratorGetIndex(&it); if (idxVar && jsvIsInt(idxVar)) { jsvSetInteger(idxVar, jsvGetInteger(idxVar)+shift); } jsvUnLock(idxVar); jsvArrayIteratorNext(&it); } // free jsvArrayIteratorFree(&it); return result; }
/*JSON{ "type" : "method", "class" : "Array", "name" : "splice", "generate" : "jswrap_array_splice", "params" : [ ["index","int","Index at which to start changing the array. If negative, will begin that many elements from the end"], ["howMany","JsVar","An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed."], ["elements","JsVarArray","One or more items to add to the array"] ], "return" : ["JsVar","An array containing the removed elements. If only one element is removed, an array of one element is returned."] } Both remove and add items to an array */ JsVar *jswrap_array_splice(JsVar *parent, JsVarInt index, JsVar *howManyVar, JsVar *elements) { if (!jsvIsArray(parent)) return 0; JsVarInt len = jsvGetArrayLength(parent); if (index<0) index+=len; if (index<0) index=0; if (index>len) index=len; JsVarInt howMany = len; // how many to delete! if (jsvIsInt(howManyVar)) howMany = jsvGetInteger(howManyVar); if (howMany > len-index) howMany = len-index; JsVarInt newItems = jsvGetArrayLength(elements); JsVarInt shift = newItems-howMany; bool needToAdd = false; JsVar *result = jsvNewEmptyArray(); JsvObjectIterator it; jsvObjectIteratorNew(&it, parent); while (jsvObjectIteratorHasValue(&it) && !needToAdd) { bool goToNext = true; JsVar *idxVar = jsvObjectIteratorGetKey(&it); if (idxVar && jsvIsInt(idxVar)) { JsVarInt idx = jsvGetInteger(idxVar); if (idx<index) { // do nothing... } else if (idx<index+howMany) { // must delete if (result) { // append to result array JsVar *el = jsvObjectIteratorGetValue(&it); jsvArrayPushAndUnLock(result, el); } // delete goToNext = false; JsVar *toRemove = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(parent, toRemove); jsvUnLock(toRemove); } else { // we're greater than the amount we need to remove now needToAdd = true; goToNext = false; } } jsvUnLock(idxVar); if (goToNext) jsvObjectIteratorNext(&it); } // now we add everything JsVar *beforeIndex = jsvObjectIteratorGetKey(&it); JsvObjectIterator itElement; jsvObjectIteratorNew(&itElement, elements); while (jsvObjectIteratorHasValue(&itElement)) { JsVar *element = jsvObjectIteratorGetValue(&itElement); jsvArrayInsertBefore(parent, beforeIndex, element); jsvUnLock(element); jsvObjectIteratorNext(&itElement); } jsvObjectIteratorFree(&itElement); jsvUnLock(beforeIndex); // And finally renumber while (jsvObjectIteratorHasValue(&it)) { JsVar *idxVar = jsvObjectIteratorGetKey(&it); if (idxVar && jsvIsInt(idxVar)) { jsvSetInteger(idxVar, jsvGetInteger(idxVar)+shift); } jsvUnLock(idxVar); jsvObjectIteratorNext(&it); } // free jsvObjectIteratorFree(&it); // and reset array size jsvSetArrayLength(parent, len + shift, false); return result; }
JsVar *_jswrap_array_iterate_with_callback(const char *name, JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool wantArray, bool isBoolCallback, bool expectedValue) { if (!jsvIsIterable(parent)) { jsExceptionHere(JSET_ERROR, "Array.%s can only be called on something iterable", name); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "Array.%s's first argument should be a function", name); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsExceptionHere(JSET_ERROR, "Array.%s's second argument should be undefined, or an object", name); return 0; } JsVar *result = 0; if (wantArray) result = jsvNewEmptyArray(); bool isDone = false; if (result || !wantArray) { JsvIterator it; jsvIteratorNew(&it, parent); while (jsvIteratorHasElement(&it) && !isDone) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *cb_result; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; cb_result = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLockMany(2,args); if (cb_result) { bool matched; if (isBoolCallback) matched = (jsvGetBool(cb_result) == expectedValue); if (wantArray) { if (isBoolCallback) { // filter if (matched) { jsvArrayPushAndUnLock(result, jsvIteratorGetValue(&it)); } } else { // map JsVar *name = jsvNewFromInteger(idxValue); if (name) { // out of memory? jsvMakeIntoVariableName(name, cb_result); jsvAddName(result, name); jsvUnLock(name); } } } else { // break the loop early if expecting a particular value and didn't get it if (isBoolCallback && !matched) isDone = true; } jsvUnLock(cb_result); } } jsvUnLock(index); jsvIteratorNext(&it); } jsvIteratorFree(&it); } /* boolean result depends on whether the loop terminated early for 'some' or completed for 'every' */ if (!wantArray && isBoolCallback) { result = jsvNewFromBool(isDone != expectedValue); } return result; }