jswrap_io.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. /*
  2. * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
  3. *
  4. * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. *
  10. * ----------------------------------------------------------------------------
  11. * This file is designed to be parsed during the build process
  12. *
  13. * JavaScript Hardware IO Functions
  14. * ----------------------------------------------------------------------------
  15. */
  16. #include "jswrap_io.h"
  17. #include "jsvar.h"
  18. /*JSON{ "type":"function", "name" : "peek8",
  19. "description" : [ "Read 8 bits of memory at the given location - DANGEROUS!" ],
  20. "generate_full" : "(JsVarInt)*(unsigned char*)jsvGetInteger(addr)",
  21. "params" : [ [ "addr", "int", "The address in memory to read"] ],
  22. "return" : ["int", "The value of memory at the given location"]
  23. }*/
  24. /*JSON{ "type":"function", "name" : "poke8",
  25. "description" : [ "Write 8 bits of memory at the given location - VERY DANGEROUS!" ],
  26. "generate_full" : "(*(unsigned char*)jsvGetInteger(addr)) = (unsigned char)jsvGetInteger(value)",
  27. "params" : [ [ "addr", "int", "The address in memory to write"],
  28. [ "value", "int", "The value to write"] ]
  29. }*/
  30. /*JSON{ "type":"function", "name" : "peek16",
  31. "description" : [ "Read 16 bits of memory at the given location - DANGEROUS!" ],
  32. "generate_full" : "(JsVarInt)*(unsigned short*)jsvGetInteger(addr)",
  33. "params" : [ [ "addr", "int", "The address in memory to read"] ],
  34. "return" : ["int", "The value of memory at the given location"]
  35. }*/
  36. /*JSON{ "type":"function", "name" : "poke16",
  37. "description" : [ "Write 16 bits of memory at the given location - VERY DANGEROUS!" ],
  38. "generate_full" : "(*(unsigned short*)jsvGetInteger(addr)) = (unsigned short)jsvGetInteger(value)",
  39. "params" : [ [ "addr", "int", "The address in memory to write"],
  40. [ "value", "int", "The value to write"] ]
  41. }*/
  42. /*JSON{ "type":"function", "name" : "peek32",
  43. "description" : [ "Read 32 bits of memory at the given location - DANGEROUS!" ],
  44. "generate_full" : "(JsVarInt)*(unsigned int*)jsvGetInteger(addr)",
  45. "params" : [ [ "addr", "int", "The address in memory to read"] ],
  46. "return" : ["int", "The value of memory at the given location"]
  47. }*/
  48. /*JSON{ "type":"function", "name" : "poke32",
  49. "description" : [ "Write 32 bits of memory at the given location - VERY DANGEROUS!" ],
  50. "generate_full" : "(*(unsigned int*)jsvGetInteger(addr)) = (unsigned int)jsvGetInteger(value)",
  51. "params" : [ [ "addr", "int", "The address in memory to write"],
  52. [ "value", "int", "The value to write"] ]
  53. }*/
  54. /*JSON{ "type":"function", "name" : "analogRead",
  55. "description" : ["Get the analog value of the given pin",
  56. "This is different to Arduino which only returns an integer between 0 and 1023",
  57. "However only pins connected to an ADC will work (see the datasheet)"],
  58. "generate" : "jshPinAnalog",
  59. "params" : [ [ "pin", "pin", "The pin to use"] ],
  60. "return" : ["float", "The analog Value of the Pin between 0 and 1"]
  61. }*/
  62. /*JSON{ "type":"function", "name" : "analogWrite",
  63. "description" : "Set the analog Value of a pin. It will be output using PWM",
  64. "generate" : "jswrap_io_analogWrite",
  65. "params" : [ [ "pin", "pin", "The pin to use"],
  66. [ "value", "float", "A value between 0 and 1"],
  67. [ "options", "JsVar", ["An object containing options.",
  68. "Currently only freq (pulse frequency in Hz) is available: ```analogWrite(LED1,0.5,{ freq : 10 });``` ",
  69. "Note that specifying a frequency will force PWM output, even if the pin has a DAC"] ] ]
  70. }*/
  71. void jswrap_io_analogWrite(Pin pin, JsVarFloat value, JsVar *options) {
  72. JsVarFloat freq = 0;
  73. if (jsvIsObject(options)) {
  74. freq = jsvGetFloatAndUnLock(jsvObjectGetChild(options, "freq", 0));
  75. }
  76. jshPinAnalogOutput(pin, value, freq);
  77. }
  78. /*JSON{ "type":"function", "name" : "digitalPulse",
  79. "description" : ["Pulse the pin with the value for the given time in milliseconds",
  80. "eg. ```pulse(A0,1,5);``` pulses A0 high for 5ms",
  81. "digitalPulse is for SHORT pulses that need to be very accurate. If you're doing anything over a few milliseconds, use setTimeout instead" ],
  82. "generate" : "jswrap_io_digitalPulse",
  83. "params" : [ [ "pin", "pin", "The pin to use"],
  84. [ "value", "bool", "Whether to pulse high (true) or low (false)"],
  85. [ "time", "float", "A time in milliseconds"] ]
  86. }*/
  87. void jswrap_io_digitalPulse(Pin pin, bool value, JsVarFloat time) {
  88. if (time<=0) {
  89. jsWarn("Pulse Time given for digitalPulse is less that or equal to 0");
  90. } else {
  91. //jsPrintInt((JsVarInt)(time*1000));
  92. jshPinPulse(pin, value, time);
  93. }
  94. }
  95. /*JSON{ "type":"function", "name" : "digitalWrite",
  96. "description" : ["Set the digital value of the given pin",
  97. "If pin is an array of pins, eg. ```[A2,A1,A0]``` the value will be treated as an integer where the first array element is the MSB" ],
  98. "generate" : "jswrap_io_digitalWrite",
  99. "params" : [ [ "pin", "JsVar", "The pin to use"],
  100. [ "value", "int", "Whether to pulse high (true) or low (false)"] ]
  101. }*/
  102. void jswrap_io_digitalWrite(JsVar *pinVar, JsVarInt value) {
  103. if (jsvIsArray(pinVar)) {
  104. JsVarRef pinName = pinVar->lastChild; // NOTE: start at end and work back!
  105. while (pinName) {
  106. JsVar *pinNamePtr = jsvLock(pinName);
  107. JsVar *pinPtr = jsvSkipName(pinNamePtr);
  108. jshPinOutput(jshGetPinFromVar(pinPtr), value&1);
  109. jsvUnLock(pinPtr);
  110. pinName = pinNamePtr->prevSibling;
  111. jsvUnLock(pinNamePtr);
  112. value = value>>1; // next bit down
  113. }
  114. } else {
  115. Pin pin = jshGetPinFromVar(pinVar);
  116. jshPinOutput(pin, value!=0);
  117. }
  118. }
  119. /*JSON{ "type":"function", "name" : "digitalRead",
  120. "description" : ["Get the digital value of the given pin",
  121. "If pin is an array of pins, eg. ```[A2,A1,A0]``` the value will be treated as an integer where the first array element is the MSB" ],
  122. "generate" : "jswrap_io_digitalRead",
  123. "params" : [ [ "pin", "JsVar", "The pin to use"] ],
  124. "return" : ["int", "The digital Value of the Pin"]
  125. }*/
  126. JsVarInt jswrap_io_digitalRead(JsVar *pinVar) {
  127. if (jsvIsArray(pinVar)) {
  128. int pins = 0;
  129. JsVarInt value = 0;
  130. JsVarRef pinName = pinVar->firstChild;
  131. while (pinName) {
  132. JsVar *pinNamePtr = jsvLock(pinName);
  133. JsVar *pinPtr = jsvSkipName(pinNamePtr);
  134. value = (value<<1) | jshPinInput(jshGetPinFromVar(pinPtr));
  135. jsvUnLock(pinPtr);
  136. pinName = pinNamePtr->nextSibling;
  137. jsvUnLock(pinNamePtr);
  138. pins++;
  139. }
  140. if (pins==0) return 0; // return undefined if array empty
  141. return value;
  142. } else {
  143. Pin pin = jshGetPinFromVar(pinVar);
  144. return jshPinInput(pin);
  145. }
  146. }
  147. /*JSON{ "type":"function", "name" : "pinMode",
  148. "description" : ["Set the mode of the given pin - note that digitalRead/digitalWrite/etc set this automatically unless pinMode has been called first. If you want digitalRead/etc to set the pin mode automatically after you have called pinMode, simply call it again with no mode argument: ```pinMode(pin)```" ],
  149. "generate" : "jswrap_io_pinMode",
  150. "params" : [ [ "pin", "pin", "The pin to use"], [ "mode", "JsVar", "The mode - a string that is either 'input', 'output', 'input_pullup', or 'input_pulldown'. Do not include this argument if you want to revert to automatic pin mode setting."] ]
  151. }*/
  152. void jswrap_io_pinMode(Pin pin, JsVar *mode) {
  153. if (!jshIsPinValid(pin)) {
  154. jsError("Invalid pin");
  155. return;
  156. }
  157. JshPinState m = JSHPINSTATE_UNDEFINED;
  158. if (jsvIsString(mode)) {
  159. if (jsvIsStringEqual(mode, "input")) m = JSHPINSTATE_GPIO_IN;
  160. if (jsvIsStringEqual(mode, "input_pullup")) m = JSHPINSTATE_GPIO_IN_PULLUP;
  161. if (jsvIsStringEqual(mode, "input_pulldown")) m = JSHPINSTATE_GPIO_IN_PULLDOWN;
  162. if (jsvIsStringEqual(mode, "output")) m = JSHPINSTATE_GPIO_OUT;
  163. }
  164. if (m != JSHPINSTATE_UNDEFINED) {
  165. jshSetPinStateIsManual(pin, true);
  166. jshPinSetState(pin, m);
  167. } else {
  168. jshSetPinStateIsManual(pin, false);
  169. if (!jsvIsUndefined(mode)) {
  170. jsError("Unknown pin mode");
  171. }
  172. }
  173. }
  174. /*XXXX{ "type":"function", "name" : "bitBang",
  175. "description" : ["bitBang out a message in a one-wire style BROKEN CURRENTLY" ],
  176. "generate" : "jshBitBang",
  177. "params" : [ [ "pin", "pin", "The pin to use"],
  178. [ "t0h", "float", "The time (in milliseconds) to spend high for a 0"],
  179. [ "t0l", "float", "The time (in milliseconds) to spend low for a 0"],
  180. [ "t1h", "float", "The time (in milliseconds) to spend high for a 1"],
  181. [ "t1l", "float", "The time (in milliseconds) to spend low for a 1"],
  182. [ "data", "JsVar", "A string representing the data"] ]
  183. }*/
  184. /*JSON{ "type":"function", "name" : "setInterval",
  185. "description" : ["Call the function specified REPEATEDLY after the timeout in milliseconds.",
  186. "The function that is being called may also take an argument, which is an object containing a field called 'time' (the time in seconds at which the timer happened)",
  187. "for example: ```setInterval(function (e) { print(e.time); }, 1000);```",
  188. "This can also be removed using clearInterval" ],
  189. "generate" : "jswrap_interface_setInterval",
  190. "params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
  191. [ "timeout", "float", "The time between calls to the function" ] ],
  192. "return" : ["JsVar", "An ID that can be passed to clearInterval"]
  193. }*/
  194. /*JSON{ "type":"function", "name" : "setTimeout",
  195. "description" : ["Call the function specified ONCE after the timeout in milliseconds.",
  196. "The function that is being called may also take an argument, which is an object containing a field called 'time' (the time in seconds at which the timer happened)",
  197. "for example: ```setTimeout(function (e) { print(e.time); }, 1000);```",
  198. "This can also be removed using clearTimeout" ],
  199. "generate" : "jswrap_interface_setTimeout",
  200. "params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
  201. [ "timeout", "float", "The time until the function will be executed" ] ],
  202. "return" : ["JsVar", "An ID that can be passed to clearTimeout"]
  203. }*/
  204. JsVar *_jswrap_interface_setTimeoutOrInterval(JsVar *func, JsVarFloat interval, bool isTimeout) {
  205. JsVar *skippedFunc = jsvSkipName(func);
  206. JsVar *itemIndex = 0;
  207. if (!jsvIsFunction(skippedFunc) && !jsvIsString(skippedFunc)) {
  208. jsError("Function or String not supplied!");
  209. } else {
  210. // Create a new timer
  211. JsVar *timerPtr = jsvNewWithFlags(JSV_OBJECT);
  212. if (interval<TIMER_MIN_INTERVAL) interval=TIMER_MIN_INTERVAL;
  213. JsVar *v;
  214. v = jsvNewFromInteger(jshGetSystemTime() + jshGetTimeFromMilliseconds(interval));
  215. jsvUnLock(jsvAddNamedChild(timerPtr, v, "time"));
  216. jsvUnLock(v);
  217. v = jsvNewFromFloat(interval);
  218. jsvUnLock(jsvAddNamedChild(timerPtr, v, "interval"));
  219. jsvUnLock(v);
  220. v = jsvNewFromBool(!isTimeout);
  221. jsvUnLock(jsvAddNamedChild(timerPtr, v, "recur"));
  222. jsvUnLock(v);
  223. jsvUnLock(jsvAddNamedChild(timerPtr, func, "callback"));
  224. //jsPrint("TIMER BEFORE ADD\n"); jsvTrace(timerArray,5);
  225. JsVar *timerArrayPtr = jsvLock(timerArray);
  226. itemIndex = jsvNewFromInteger(jsvArrayPushWithInitialSize(timerArrayPtr, timerPtr, 1) - 1);
  227. //jsPrint("TIMER AFTER ADD\n"); jsvTrace(timerArray,5);
  228. jsvUnLock(timerArrayPtr);
  229. jsvUnLock(timerPtr);
  230. }
  231. jsvUnLock(skippedFunc);
  232. //jsvTrace(jsiGetParser()->root, 0);
  233. return itemIndex;
  234. }
  235. JsVar *jswrap_interface_setInterval(JsVar *func, JsVarFloat timeout) {
  236. return _jswrap_interface_setTimeoutOrInterval(func, timeout, false);
  237. }
  238. JsVar *jswrap_interface_setTimeout(JsVar *func, JsVarFloat timeout) {
  239. return _jswrap_interface_setTimeoutOrInterval(func, timeout, true);
  240. }
  241. /*JSON{ "type":"function", "name" : "setWatch",
  242. "description" : ["Call the function specified when the pin changes",
  243. "The function may also take an argument, which is an object containing a field called 'time', which is the time in seconds at which the pin changed state, and 'state', which is the current state of the pin",
  244. " This can also be removed using clearWatch" ],
  245. "generate" : "jswrap_interface_setWatch",
  246. "params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
  247. [ "pin", "pin", "The pin to watch" ],
  248. [ "options", "JsVar", ["If this is a boolean or integer, it determines whether to call this once (false = default) or every time a change occurs (true)",
  249. "If this is an object, it can contain the following information: ```{ repeat: true/false(default), edge:'rising'/'falling'/'both'(default)}```" ] ] ],
  250. "return" : ["JsVar", "An ID that can be passed to clearWatch"]
  251. }*/
  252. JsVar *jswrap_interface_setWatch(JsVar *funcVar, Pin pin, JsVar *repeatOrObject) {
  253. bool repeat = false;
  254. int edge = 0;
  255. if (jsvIsObject(repeatOrObject)) {
  256. JsVar *v;
  257. repeat = jsvGetBoolAndUnLock(jsvObjectGetChild(repeatOrObject, "repeat", 0));
  258. v = jsvObjectGetChild(repeatOrObject, "edge", 0);
  259. if (jsvIsString(v)) {
  260. if (jsvIsStringEqual(v, "rising")) edge=1;
  261. else if (jsvIsStringEqual(v, "falling")) edge=-1;
  262. else if (jsvIsStringEqual(v, "both")) edge=0;
  263. else jsWarn("'edge' in setWatch should be a string - either 'rising', 'falling' or 'both'");
  264. } else if (!jsvIsUndefined(v))
  265. jsWarn("'edge' in setWatch should be a string - either 'rising', 'falling' or 'both'");
  266. jsvUnLock(v);
  267. } else
  268. repeat = jsvGetBool(repeatOrObject);
  269. JsVarInt itemIndex = -1;
  270. JsVar *skippedFunc = jsvSkipName(funcVar);
  271. if (!jsvIsFunction(skippedFunc) && !jsvIsString(skippedFunc)) {
  272. jsError("Function or String not supplied!");
  273. } else {
  274. // Create a new watch
  275. JsVar *watchPtr = jsvNewWithFlags(JSV_OBJECT);
  276. JsVar *v;
  277. v = jsvNewFromPin(pin);
  278. jsvUnLock(jsvAddNamedChild(watchPtr, v, "pin"));
  279. jsvUnLock(v);
  280. v = jsvNewFromBool(repeat);
  281. jsvUnLock(jsvAddNamedChild(watchPtr, v, "recur"));
  282. jsvUnLock(v);
  283. v = jsvNewFromInteger(edge);
  284. jsvUnLock(jsvAddNamedChild(watchPtr, v, "edge"));
  285. jsvUnLock(v);
  286. jsvUnLock(jsvAddNamedChild(watchPtr, funcVar, "callback"));
  287. JsVar *watchArrayPtr = jsvLock(watchArray);
  288. itemIndex = jsvArrayPushWithInitialSize(watchArrayPtr, watchPtr, 1) - 1;
  289. jsvUnLock(watchArrayPtr);
  290. jsvUnLock(watchPtr);
  291. jshPinWatch(pin, true);
  292. }
  293. jsvUnLock(skippedFunc);
  294. return (itemIndex>=0) ? jsvNewFromInteger(itemIndex) : 0/*undefined*/;
  295. }
  296. /*JSON{ "type":"function", "name" : "clearInterval",
  297. "description" : ["Clear the Interval that was created with setInterval, for example:",
  298. "```var id = setInterval(function () { print('foo'); }, 1000);```",
  299. "```clearInterval(id);```",
  300. "If no argument is supplied, all timers and intervals are stopped" ],
  301. "generate" : "jswrap_interface_clearInterval",
  302. "params" : [ [ "id", "JsVar", "The id returned by a previous call to setInterval"] ]
  303. }*/
  304. /*JSON{ "type":"function", "name" : "clearTimeout",
  305. "description" : ["Clear the Timeout that was created with setTimeout, for example:",
  306. "```var id = setTimeout(function () { print('foo'); }, 1000);```",
  307. "```clearTimeout(id);```",
  308. "If no argument is supplied, all timers and intervals are stopped" ],
  309. "generate" : "jswrap_interface_clearTimeout",
  310. "params" : [ [ "id", "JsVar", "The id returned by a previous call to setTimeout"] ]
  311. }*/
  312. void _jswrap_interface_clearTimeoutOrInterval(JsVar *idVar, bool isTimeout) {
  313. if (jsvIsUndefined(idVar)) {
  314. JsVar *timerArrayPtr = jsvLock(timerArray);
  315. jsvRemoveAllChildren(timerArrayPtr);
  316. jsvUnLock(timerArrayPtr);
  317. } else {
  318. JsVar *child = jsvIsBasic(idVar) ? jsvFindChildFromVarRef(timerArray, idVar, false) : 0;
  319. if (child) {
  320. JsVar *timerArrayPtr = jsvLock(timerArray);
  321. jsvRemoveChild(timerArrayPtr, child);
  322. jsvUnLock(child);
  323. jsvUnLock(timerArrayPtr);
  324. } else {
  325. jsError(isTimeout ? "Unknown Timeout" : "Unknown Interval");
  326. }
  327. }
  328. }
  329. void jswrap_interface_clearInterval(JsVar *idVar) {
  330. _jswrap_interface_clearTimeoutOrInterval(idVar, false);
  331. }
  332. void jswrap_interface_clearTimeout(JsVar *idVar) {
  333. _jswrap_interface_clearTimeoutOrInterval(idVar, true);
  334. }
  335. /*JSON{ "type":"function", "name" : "changeInterval",
  336. "description" : ["Change the Interval on a callback created with setInterval, for example:",
  337. "```var id = setInterval(function () { print('foo'); }, 1000); // every second```",
  338. "```changeInterval(id, 1500); // now runs every 1.5 seconds```",
  339. "This takes effect the text time the callback is called (so it is not immediate)."],
  340. "generate" : "jswrap_interface_changeInterval",
  341. "params" : [ [ "id", "JsVar", "The id returned by a previous call to setInterval"],
  342. [ "time","float","The new time period in ms" ] ]
  343. }*/
  344. void jswrap_interface_changeInterval(JsVar *idVar, JsVarFloat interval) {
  345. if (interval<TIMER_MIN_INTERVAL) interval=TIMER_MIN_INTERVAL;
  346. JsVar *timerName = jsvIsBasic(idVar) ? jsvFindChildFromVarRef(timerArray, idVar, false) : 0;
  347. if (timerName) {
  348. JsVar *timer = jsvSkipNameAndUnLock(timerName);
  349. JsVar *v;
  350. v = jsvNewFromFloat(interval);
  351. jsvUnLock(jsvSetNamedChild(timer, v, "interval"));
  352. jsvUnLock(v);
  353. v = jsvNewFromInteger(jshGetSystemTime() + jshGetTimeFromMilliseconds(interval));
  354. jsvUnLock(jsvSetNamedChild(timer, v, "time"));
  355. jsvUnLock(v);
  356. jsvUnLock(timer);
  357. // timerName already unlocked
  358. } else {
  359. jsError("Unknown Interval");
  360. }
  361. }
  362. /*JSON{ "type":"function", "name" : "clearWatch",
  363. "description" : [ "Clear the Watch that was created with setWatch. If no parameter is supplied, all watches will be removed." ],
  364. "generate" : "jswrap_interface_clearWatch",
  365. "params" : [ [ "id", "JsVar", "The id returned by a previous call to setWatch"] ]
  366. }*/
  367. void jswrap_interface_clearWatch(JsVar *idVar) {
  368. if (jsvIsUndefined(idVar)) {
  369. JsVar *watchArrayPtr = jsvLock(watchArray);
  370. // unwatch all pins
  371. JsVarRef watch = watchArrayPtr->firstChild;
  372. while (watch) {
  373. JsVar *watchNamePtr = jsvLock(watch); // effectively the array index
  374. JsVar *pinVar = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
  375. jshPinWatch(jshGetPinFromVar(pinVar), false); // 'unwatch' pin because we know that we're removing ALL watches
  376. jsvUnLock(pinVar);
  377. watch = watchNamePtr->nextSibling;
  378. jsvUnLock(watchNamePtr);
  379. }
  380. // remove all items
  381. jsvRemoveAllChildren(watchArrayPtr);
  382. jsvUnLock(watchArrayPtr);
  383. } else {
  384. JsVar *watchNamePtr = jsvFindChildFromVarRef(watchArray, idVar, false);
  385. if (watchNamePtr) { // child is a 'name'
  386. JsVar *pinVar = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
  387. Pin pin = jshGetPinFromVar(pinVar);
  388. jsvUnLock(pinVar);
  389. JsVar *watchArrayPtr = jsvLock(watchArray);
  390. jsvRemoveChild(watchArrayPtr, watchNamePtr);
  391. jsvUnLock(watchNamePtr);
  392. // Now check if this pin is still being watched
  393. bool stillWatched = false;
  394. JsArrayIterator it;
  395. jsvArrayIteratorNew(&it, watchArrayPtr);
  396. while (jsvArrayIteratorHasElement(&it)) {
  397. JsVar *watchPtr = jsvArrayIteratorGetElement(&it);
  398. JsVar *pinVar = jsvObjectGetChild(watchPtr, "pin", 0);
  399. if (jshGetPinFromVar(pinVar) == pin)
  400. stillWatched = true;
  401. jsvUnLock(pinVar);
  402. jsvUnLock(watchPtr);
  403. jsvArrayIteratorNext(&it);
  404. }
  405. jsvArrayIteratorFree(&it);
  406. jsvUnLock(watchArrayPtr);
  407. if (!stillWatched)
  408. jshPinWatch(pin, false); // 'unwatch' pin
  409. } else {
  410. jsError("Unknown Watch");
  411. }
  412. }
  413. }