diff --git a/.gdbinit b/.gdbinit index 312b370b6..bfd815b79 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,7 +1,7 @@ break jsAssertFail break jsError define jsvTrace - print jsvTrace(execInfo.root, 0) + print jsvTrace($arg0, 0) end define whereami print jslPrintPosition(jsiConsolePrintString, 0, lex->tokenLastStart) @@ -31,4 +31,48 @@ end define wdt_off p (*(uint32_t*)0x4001050C)=1 end +define execflags + if execInfo.execute==0 + printf "EXEC_NO\n" + end + if execInfo.execute&EXEC_YES + printf "EXEC_YES\n" + end + if execInfo.execute&EXEC_BREAK + printf "EXEC_BREAK\n" + end + if execInfo.execute&EXEC_CONTINUE + printf "EXEC_CONTINUE\n" + end + if execInfo.execute&EXEC_RETURN + printf "EXEC_RETURN\n" + end + if execInfo.execute&EXEC_INTERRUPTED + printf "EXEC_INTERRUPTED\n" + end + if execInfo.execute&EXEC_EXCEPTION + printf "EXEC_EXCEPTION\n" + end + if execInfo.execute&EXEC_ERROR + printf "EXEC_ERROR\n" + end + if execInfo.execute&EXEC_ERROR_LINE_REPORTED + printf "EXEC_ERROR_LINE_REPORTED\n" + end + if execInfo.execute&EXEC_FOR_INIT + printf "EXEC_FOR_INIT\n" + end + if execInfo.execute&EXEC_IN_LOOP + printf "EXEC_IN_LOOP\n" + end + if execInfo.execute&EXEC_IN_SWITCH + printf "EXEC_IN_SWITCH\n" + end + if execInfo.execute&EXEC_CTRL_C + printf "EXEC_CTRL_C\n" + end + if execInfo.execute&EXEC_CTRL_C_WAIT + printf "EXEC_CTRL_C_WAIT\n" + end +end diff --git a/.gitignore b/.gitignore index 7134d335b..46c615b1b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.o *.elf /*.hex +/*.app_hex *.lst *.srec *.bin @@ -28,6 +29,7 @@ node_modules /build/ esp_iot_sdk_v1.5.0 esp_iot_sdk_v2.0.0.p1 +ESP8266_NONOS_SDK-2.2.1 xtensa-lx106-elf app esp-idf @@ -39,7 +41,18 @@ gcc-arm-none-eabi* targetlibs/nrf5x_12/examples targetlibs/nrf5x_12/documentation/ targetlibs/nrf5x_14 +targetlibs/nrf5x_15/components +targetlibs/nrf5x_15/config +targetlibs/nrf5x_15/example_config +targetlibs/nrf5x_15/examples +targetlibs/nrf5x_15/external +targetlibs/nrf5x_15/external_tools +targetlibs/nrf5x_15/integration +targetlibs/nrf5x_15/modules +targetlibs/nrf5x_mesh +targetlibs/raspberrypi /.vscode /CURRENT_BOARD.make /topreadonly /topstrings + diff --git a/.travis.yml b/.travis.yml index fb77ef886..7fac72e93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,9 @@ before_script: - python --version after_script: - - ls *.bin *.hex *.tgz *.zip | xargs -I {} curl -v -F "binary=@{}" "http://www.espruino.com/travis_upload.php?commit=$TRAVIS_COMMIT&branch=$TRAVIS_BRANCH" + - "if [[ -n \"$UPLOADTOKEN\" ]]; then + ls *.bin *.hex *.tgz *.zip | xargs -I {} curl -v -F \"binary=@{}\" \"http://www.espruino.com/travis_upload.php?commit=$TRAVIS_COMMIT&branch=$TRAVIS_BRANCH&token=$UPLOADTOKEN\"; + fi" # upload to an S3 bucket, requires S3_BUCKET, AWS_ACCESS_KEY_ID and AWS_SECRET_KEY to be set # in environment using travis' repository settings - "if [[ -n \"$S3_BUCKET\" && -n \"$AWS_ACCESS_KEY_ID\" ]]; then @@ -27,32 +29,27 @@ env: - V=0 - RELEASE=1 matrix: + - BOARD=LINUX - BOARD=ESPRUINOBOARD PAD_FOR_BOOTLOADER=1 - BOARD=PICO_R1_3 PAD_FOR_BOOTLOADER=1 - BOARD=ESPRUINOWIFI PAD_FOR_BOOTLOADER=1 - BOARD=PUCKJS DFU_UPDATE_BUILD=1 + - BOARD=PIXLJS DFU_UPDATE_BUILD=1 + - BOARD=HEXBADGE DFU_UPDATE_BUILD=1 + - BOARD=MDBT42Q DFU_UPDATE_BUILD=1 - BOARD=ESP32 - BOARD=ESP8266_BOARD - BOARD=ESP8266_4MB - BOARD=MICROBIT - BOARD=NRF52832DK - - BOARD=OLIMEXINO_STM32 - - BOARD=MAPLERET6_STM32 - - BOARD=HYSTM32_24 - - BOARD=HYSTM32_28 - - BOARD=HYSTM32_32 - - BOARD=STM32VLDISCOVERY - - BOARD=STM32F3DISCOVERY - - BOARD=STM32F4DISCOVERY - - BOARD=NUCLEOF401RE - - BOARD=NUCLEOF411RE - - BOARD=LINUX - - BOARD=EFM32GGSTK - - BOARD=NUCLEOL476RG - BOARD=RUUVITAG DFU_UPDATE_BUILD=1 - BOARD=WIO_LTE - - BOARD=HEXBADGE DFU_UPDATE_BUILD=1 - - BOARD=MDBT42Q DFU_UPDATE_BUILD=1 + - BOARD=THINGY52 + - BOARD=THINGY52 DFU_UPDATE_BUILD=1 + - BOARD=SMARTIBOT DFU_UPDATE_BUILD=1 + - BOARD=STM32L496GDISCOVERY + - BOARD=RAK8211 + - BOARD=RAK8212 script: make diff --git a/ChangeLog b/ChangeLog index da34499fd..2191b6a8c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,261 @@ - ESP8266: no callback if SSID is not available (fix #1297) + Fix string index calculation when using some regexes (fix #1602) + Ensure Function.replaceWith puts scope after parameters (fix #1601) + Ensure Graphics.* return the Graphics instance, to allow call chaining (fix #1580) + Ensure DataView.byteOffset/byteLength is always set even if not specified (fix #1567) + Added HttpServerResponse.setHeader + HttpServerResponse now automatically sends `Connection:close` unless overridden (fix #1596) + Fix sign of RSSI reporting with setRSSIHandler + nRF52: Add docs for properties that weren't documented before + nRF52: Add ability to connect to device with passkey authentication + nRF52: Allow startNotifications to use Indicate if Notify doesn't exist (as per spec) + nRF52: Add option for static passkey pairing with NRF.setSecurity + ESP32: update EspruinoBuildTools to esp-idf V3.1.2 - fix Wifi connect issues (multiple ssid) + Revert PR #1459 as it broke neopixel functionality that worked previously + nRF52: Disconnect RX pin after UART test at boot (saves power when in deep sleep) + Serial.unsetup now sets pin state to STATE_UNDEFINED, which disconnects the pins internally + Removed modulo on `new Date` h/m/s/ms arguments as it seems desktop JS is fine with out of range values + Added `active` option to NRF.setScan/findDevices/requestDevices to allow scan response packets to be requested + Add I2C/SPI baud rate checks (#1619) + STM32: Add `E.setRTCPrescaler` to allow the RTC to be calibrated on Espruino Pico (fix #1607) + nRF52: Fix slow Bluetooth connection if previously disconnected while using low power connection interval (fix #1605) + Allow `Graphics.clear(true)` to reset state (font, color, etc) to default as well as clearing the screen (fix #1615) + nRF52: Fix setScanResponse regression from 2v00 + nRF5x: Execute SWI1_IRQHandler when radio turns off instead of on+off. More efficient, fixed multiple advertising. + Smartibot build added + ESP32: update EspruinoBuildTools to esp-idf V3.1.3 + nRF52: Add FAT Filesystem support to MDBT42Q module + Now save file modification time with FAT + + 2v01 : ESP32: update to esp-idf V3.1 + Fix issues with Class Extends + Improve Tab Completions for extended classes + Fix Storage.readJSON/readArrayBuffer memory leak (fix #1532) + Fix potential out of bounds Graphics.scroll + Serial.setConsole now warns if not used on hardware Serial + ESP8266: implement hw_timer (fix #1511) to make soft serial and pwm work + Fix Storage write error when skipping pages (fix #1539) + nRF5x: When scanning, only use as much of IO queue as is needed + nRF5x: If BLE/NFC/etc data won't fit in IO queue, drop whole packet + Allow `a in b` to search 'fake' objects (#1534) + Improve fast path when iterating over Uint8Array/ArrayBuffer + Allow deletion of function properties (fix #1549) + Add `{callback:...}` option for `.write` style functions - improve docs. + ESP8266: deepSleep invalid microseconds (fix #1547) + ESP8266: ESP_FLASH_MAX for ESP8266_4MB is wrong (fix #1551, #1553) + Fix buffer overflow if bytesize/stopbits used in `Serial.setup` (fix #1510) + Ensure jsvNewFlatStringOfLength tries twice (even if GC doesn't release memory, it reorders the free list) (#1559) + ESP8266: Missing variable Flash Mode (--flash_mode, -fm) in ESP8266.make files (fix #1563) + ESP8266: Remove hack added to get around SDK 1.4 bug (fix #1568) + Reduce available hardware SPI/I2C instances to 1 on nRF52 (since this is all we implement atm) + Add E.dumpFragmentation to show memory fragmentation (only for debug builds) + Fix parsing of dates from before 1970 + nRF52: Fix some regressions in requestDevice (flagged up by asserts in debug build) + Allow built-in objects to be created with `new X()` + nRF52: Ensure Bluetooth stack doesn't do a reboot for non-fatal errors (just report them to console) + BluetoothRemoteGATTServer.disconnect now returns a Promise + nRF52: Jump out of low power mode after less BLE activity (2 reads/writes in 10 radio packets) (#1546) + nRF5x: Fix NRF.setConnectionInterval when there's no connection (fix #1546) + Explicitly mention Espruino not supporting CASE after DEFAULT in error (fix #1570) + Ensure scope is saved for Class constructors (fix #1576) + Add setNBCellOn for RAK8211-NB (fix #1581) + Now escape chars <8 as octal, and add escape of vertical tab + Add Graphics.createArrayBuffer(... {interleavex:true}) to allow faster support for P3 LED panels + Add Graphics.fill/drawEllipse and move fill/drawCircle to use the same code + CC3000-specific build removed on Original Espruino board (lack of space) + + 2v00 : Allow changeInterval with large (>32 bit) intervals (fix #1438) + changeInterval now changes the interval immediately when it's called inside the interval it is changing (fix #1440) + Fix parsing of try..catch when not executing (fix #1439) + Add extra ReferenceError checks, even if variable is not used + Allow Ctrl-C out of while...continue loop (fix #1441) + Fix bug if using an undefined member of an object for for..in (fix #1437) + Allow for..in to iterate over prototype chains down from Array and Object + Add for(var i of array) to iterate over elements + Added getter and setter support + Stop parsing blocks if not executing (fix #572) + Fix stack overflow if interpreting a file full of '{' (fix #1448) + Fix exception when performing record access on object from getter (fix #1454) + Switch to non-recursive StringExt copy (fix #1451) + Fix rounding errors in fillPoly -> improve vector font rendering + Fix issue that caused 'dump()' not to report variables/functions on Pixl.js + Add E.lookupNoCase to allow searching case-insensitively for Object keys + Fix HTTP Chunked transfers when the server uses lowercase headers (fix #1458) + Fix TypedArray.indexOf (fix #1468) + Allow require('Storage').write('a','',0,15) (zero length data) (fix #1465) + edit() now chooses the shortest way to describe the function + Fixed bug when RegExp.match/test called on non-strings + Added Global isFinite + Add missing ArrayBufferView.filter + Added Array.find and findIndex (also for ArrayBufferViews) + Fix unreliable ArrayBufferView.indexOf (#1468) + Added String.startsWith/endsWith/includes (#1302) + parseFloat(".s") now returns NaN + Fixed /\S+/.test(" ") + Added Storage.getFree() to return available space + Enable E.setTimeZone on boards with very little flash memory + Lower saved code area to 2k on micro:bit (from 3) + Remove RGB colour handling in setColor on devices with low flash + Reduce available variable count on STM32VL - we were too low on RAM + Added Graphics.asBMP/asURL/dump - allowing easy debugging of Graphics via IDE + Allow '.then' on already-resolved promise (fix #1476) + Stop atob adding trailing 0s when strings are not a multiple of 3 long + Reconstruct start and end newlines when dumping multi-line functions + Fix regression parsing methods in classes on embedded (fix #1479) + nRF5x: Add options argument to NRF.connect and BluetoothDevice.gatt.connect, allowing a connection interval to be specified + nRF5x: Start renegotiating speed after 0.1ms, not 5s. Massively improves connect speed. + nRF5x: Now queue up to 5 UART TX packets per transmit interval (was 1 previously) + nRF52: Add Dynamic Interval Adjustment - default to 2x connection speed, but idle at 10x slower if not used for 2 minutes + nRF5x: Add NRF.setConnectionInterval() to allow connection speed to be set manually + RuuviTag: invert LED1, LED2 & BTN in software so LED.set() does what you'd expect + Console now doesn't print quotes around object keys if it's not needed + Added `E.toJS` to allow very compact JS data stringification (similar to `JSON.stringify`) + Output the current timer number as a comment in `dump()` + Don't print `=undefined` to console if line is empty + Added RAK8212 (and include SMS/GPRS code in RAK8211/2 builds) + Graphics.stringWidth now takes account of newlines + nRF52: NRF.requestDevice now resolves as soon as a device is found. Faster and better in congested areas + Replace use of obsolete 'usleep' function in Linux builds (fix #1455) + Add Ethernet.getIP/setIP callbacks for Wiznet to bring them in line with WiFi (fix #1482) + Fix Math.round for numbers > 32 bit (fix #1485) + Pixl.js menu now resets font alignment, and down arrow icon is fixed + Now check for ReferenceErrors in global scope + Fix Array.shift (returned a NAME, rather than the value) + Add sanity check for names returned from Functions and fix Array.pop + Stop characters getting dropped when pasting large amounts of data into Linux build + nRF5x increase JsSysTime accuracy to 2^-20 from 2^-16 - drastically improves Util Timer accuracy + Added support for Software Serial ports (for low baud rates, eg. 9600) + Fix JS state restoration issue (eg. Pixl.menu inside switch would cause errors) + Added Graphics.drawPoly + Add Graphics.asImage to turn a Graphics instance into an Image that can be used with drawImage + Add Graphics.createImage to allow creation of a 1 bit image direct from a string + Use 32 bit floats for E.FFT, not 64 (fix #1443) + Automatically shut down UART if both pin states are changed + Fix `setDeviceClockCmd: Unknown Device` when using `LoopbackB.setConsole()` on WiFi board + Fix non-UART serial regressions (after software serial additions) + Pixl.js: Add Pixl.setLCDPower to allow the LCD to be powered off, more than halving power consumption + nRF5x: Allow NRF.setScan and NRF.findDevices to take the same search filters NRF.requestDevice does (fix #1496) + Fix buffer overrun if we have to reallocate a pointer to argument lists when calling a function (fix #1491) + Fix stack overflow if executing regex full of hundreds of open brackets (fix #1487) + Fix issue where STM32F4 USB could lock up if TX during heavy RX + Improve `E.mapInPlace` docs, and allow it to work with no map (eg pass straight through) + Added non-standard Uint24Array, because it's very useful for RGB + ESP8266: add CFLAGs to shrink binaray files (fix #1499) + ESP8266: fully integration of analog pin A0 (fix #1495) + ESP32: update sdk to esp-idf 3.0.1, set Espruino build tools back to master branch + Allow btoa to work for arrays as well as Strings (fix #1509) + Allow E.mapInPlace to merge bits from multiple source elements, also add option for msb/lsb first + Remove Graphics.scroll/drawCircle/fillCircle on devices with low flash to allow builds to fit again + Remove BluetoothRemoteGATTCharacteristic.writeValue on NRF51 (accidental inclusion - it's not required) + Double IO buffer size to 256 (1k bytes) on boards with 96k of RAM or more (or NRF52) + nRF5x: allow arbirtary baud rates to be specified for UART + On devices with low flash, ensure atan2 uses the slower/smaller atan implementation + Move FFT back to 64 bit if low flash (it uses less memory!) and optimise for flash space + Remove new Graphics.createImage/asBML/asURL/dump on Original Espruino Board (not enough space) + Remove Olimexino from build (too difficult to slim down build and very low usage) + Remove Software Serial from boards where we're low on flash + Increase size of saved code area from 3*4k to 10*4k on RAK821x boards + Fix 'Can't extend undefined' when using Object.setPrototypeOf on a function + nRF52: Added NRF.HID event for two-way BLE HID communications + nRF5x: Remove multiple writes per connection interval (more trouble than the speed improvement is worth) + Fixed hang if trying to allocate Storage greater than total storage size in a fully erased Storage area. + Pixl.js: Fix 30s pause when closing sockets on WIZnet W5100 (fix #1306) + Remove HASH/hashlib from all builds as it was confusingly in some and not others. Now use 'crypto' + require('crypto').SHA1 is now JS in Espruino Original to cut down on the flash required + Added 'heatshrink' library to expose built-in heatshrink compression to users + Fix assert fail when calling Function.apply with an Object with non-numeric keys + Fix issue when AT lib has to process multiple custom line handers in one packet + Espruino WiFi: Fix unreliable send when receiving lots of data on another socket + Espruino WiFi: Only rename `EspruinoWiFi` to `WiFi` if not found (allows easier debug) + Check Flash Storage for modules when using `require` + Add 'bits' option for Software SPI + STM32 reset pin IRQs before storing the state - makes lost setWatches far less likely + Ensure that setBusyIndicator updates output state after the very first initialisation. + MDBT42Q: Add LED2 var in the Espruino interpreter, but don't use it for the bootloader + ESP8266: release heap used by logDebug(true) (fix #1508) + ESP8266: remove SHA256 SHA512 (fix #1517) + Ensure `Date.getTimezoneOffset()` returns the correct timezone offset (fix #1515) + Search for and execute files '.boot0'/1/2/3 in Storage at boot time if they exist + Pixl.js: reduce saved code area to 9 x 4kb to allow for extra features + ESP8266: switch to SDK 2.2.1 (fix #1207) + Fix Serial port path regression on Linux, and add docs + microbit: remove line-by-line debug capability to free up some space + Added ES6 String.prototype.repeat + + 1v99 : Increase jslMatch error buffer size to handle "UNFINISHED TEMPLATE LITERAL" string (#1426) + nRF5x: Make FlashWrite cope with flash writes > 4k + Increase max size of native strings on platforms that support it - 16 bit to 32 bit (#1432) + Fix stack size detection on Linux (fix #1427) + Fix strncat/cpy bounding issues (fix #1425) + Promises now ignore a second resolve/reject (fix #1433) + Fix stack overflow if void void void... is repeated many times (fix #1434) + Fix font rendering issue caused by signed bit field handling by GCC on non-x86 platforms (fix #1436) + Added E.reboot() to allow hard reboots from software (fix #1429) + Added 'Graphics.getInstance()' for more platform independent graphics + Added VT100 'erase in Display' to Terminal + In REPL, use x.toString() for objects if we know their name and it is available + Pixl.js add BLE aerial test to self-test, now also start immediately on BTN4 at boot + + 1v98 : Allow Crypto SHA1 without SHA256/512 (for ESP8266 where flash is scarce) + Add better docs for the form of Wifi callback functions + Modify ESP8266/ESP32 callbacks to match the node.js style used elsewhere + nRF52: fix pin.toggle() on software-negated pins + Pixl.js: Reorder pins so 0..13 are also D0..13 for better Arduino compatibility + Fix dump() when used with code written using E.setBootCode(..), (fix #1398) + Allow parseInt/parseFloat to be used on very large strings if the number doesn't extend right to the end (fix #1397) + nRF5x: Fix memory leak on NRF.connect + Fix memory leak if an exception is thrown within a rejected promise + ESP8266: rewrite wifi.save and restore to use the storage lib (imp #1380) + ESP8266: Add missing option ssid_hidden for Wifi.startAP() (imp #1358) + Fixed double-connect issue for TCP sockets + Pixl.js: Ensure Pixl.menu changes to bitmap fonts + Pixl.js: tweaked bias/contrast to improve display quality + ESP32: update esp-idf to v3.0. BLE support - thanks to @jumjum. Erase flash before flashing. vars now 2500 + ESP8266: rearange rf_cal_sector (fix #1294) + ESP8266: Wifi.scan() now return authmode as text + ESP32: Fix accidental initialisation of UART3 when switching to Telnet (fix #1362) + nRF52: Added `NRF.setAddress` to allow the MAC address to be changed + Added Graphics.setFontAlign for font alignment and rotation + Make software I2C bitrate and waveform more accurate + Move default I2C bitrate to 100kbit/sec + Linux: don't create a espruino.flash file if we're not writing to flash + Add height check for Graphics.createArrayBuffer(...vertical_byte:true) (fix #1421) + Add sanity check for debug trace print statement (fix #1420) + Fix handling of exceptions in switch statements (fix #1352) + Fix 'return when not in function' regression when returning inside a catch block (fix #1422) + Don't load saved firmware images from different firmware versions - saved JS code still loaded (fix #1174) + Remove Graphics.setFontAlign and Graphics.getModified on devices with low flash memory + + 1v97 : nRF52: fix NRF.on('connect',...) issue + STM32: Fix setDeviceClockCmd error for USB.setConsole() + nRF5x: Fix getPinMode, which fixes SW I2C after save() + Thingy52: Don't report contents of 'Thingy' in 'dump()' + Thingy52: Allow multiple sounds to play at once + nRF5x: Ensure Waveform triggers a finished event + Thingy52: Enable simple bootloader, add travis build for DFU + Add Serial.inject to allow data to be added as if it was received from that device + Fix UDP handling so that it copes with packets not all being received in one go + STM32L496: increase variables - use full 320kB of RAM as it is contiguous + Add a maximum time for setTimeout/setInterval (100 years) + Fix Storage.write when writing partial file of the same length and initial contents + Fix corrupted timer channels returned by Pin.getInfo + Add command history to debugger + Remove process.env.EXPORTS (EXPTR does the same but takes less space) + Thingy52: Add 9 axis MPU support + Errors now store message in 'message', not 'msg' (fix #1366) + Ensure 'in' operator checks the prototype chain (fix #1365) + Promise.resolve now handles promises/thenables as arguments (fix #1363) + try..catch now creates exception in its own scope (fix #1367) + Thingy52,Pixl.js: add default NFC URL of the Espruino IDE + Add ArrayBuffer.byteLength property (fix #1374) + setWatch(..., {edge:"rising",debounce:25}) is now default for built-in buttons + Pixl.js: add Pixl.menu function for easy menus, build in graphical_menu.js + Fix regression in MDBT42Q advertised name + nRF52: Add E.getBattery as a more global battery percentage function, deprecate `Puck.getBatteryPercentage` + Fix '.buffer' regression in 'JSON.stringify(new Uint8Array([1,2,3,4]).buffer)' + Allow `typeof 123 .testing` without an exception (fix #1351) + Add crypto.AES to Puck.js and other nRF52 Espruino devices + + 1v96 : ESP8266: no callback if SSID is not available (fix #1297) ESP8266: esp8266 wifi getStatus doesn't show savedMode (fix #752) ESP8266: cleanup defines WIFI_DBG and NET_DBG for RELEASE ESP8266: switch to single ld file eagle.app.v6.new.2048.ld for ESP8266_4MB board @@ -41,6 +298,14 @@ Allow STM32LL port to write 32 bits to flash at a time to bring it in line with other ports Allow flash writes *from* unaligned addresses on nRF52 and ESP8266 (previously this crashed the ESP8266) Update process.ENV.EXPORTS to bring it in line with what the compiler uses + Now set 'this' correctly for Arrow Functions + Add ES6 classes and 'super' + nRF5x: Move all bluetooth events to event queue (removing MEMORY_BUSY issues) + Fix potential issue where EV_TYPE_MASK enum could be set incorrectly + setWatch's edge argument can also be an integer now + Add 'data' option to setWatch to allow clocked data to be decoded easily + nRF52: Increase flash available for stored code from 12kB for 40kB + Now store/display appreviated commit in process.env, remove build date 1v95 : nRF5x: Swap to UART fifo to avoid overrun errors at high baud rates Ensure Exceptions/errors are reported on a blank line diff --git a/Dockerfile b/Dockerfile index 78f71d0ae..9963bab4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,40 @@ -FROM ubuntu:14.04 +# Usage: +# +# Note: if you're in Linux you need to run socker with 'sudo'. +# But honestly if you're on linux you should just save yourself +# gigabytes of downloads and disk space and build Espruino directly. +# +# 1: Build the container image +# +# docker build . -t img_name +# +# 2: Run container image so it builds espruino +# +# docker run -e BOARD='PICO_R1_3' --name container_name img_name +# +# This will run the container and save build results into the container's filesystem. +# Near the end of the build the filename will be displayed, for example espruino_2v00_pico_1r3.bin +# +# 3: Copy build results from the container into your filesystem +# +# docker cp container_name:espruino/espruino_2v00_pico_1r3.bin ./ +# -RUN git clone https://github.com/espruino/Espruino espruino +FROM python:3 + +COPY . /espruino WORKDIR /espruino -# Change these to provision and compile for a different board -RUN source scripts/provision.sh PICO_R1_3 -ENV BOARD PICO_R1_3 +# Workaround add some stuff that the provision script uses +# in here so it doesn't have to use sudo +RUN apt-get update +RUN apt-get install -qq -y python3-pip +RUN pip install pyserial +RUN pip install nrfutil + +# This ensures ALL dependencies are installed beforehand +RUN bash -c "source scripts/provision.sh ALL" ENV RELEASE 1 +CMD ["bash", "-c", "source scripts/provision.sh ALL && make"] -CMD ["make"] diff --git a/Makefile b/Makefile index 54031463e..eaadef224 100755 --- a/Makefile +++ b/Makefile @@ -63,11 +63,11 @@ endif INCLUDE?=-I$(ROOT) -I$(ROOT)/targets -I$(ROOT)/src -I$(GENDIR) LIBS?= DEFINES?= -CFLAGS?=-Wall -Wextra -Wconversion -Werror=implicit-function-declaration -fno-strict-aliasing -Wno-packed-bitfield-compat -LDFLAGS?=-Winline +CFLAGS?=-Wall -Wextra -Wconversion -Werror=implicit-function-declaration -fno-strict-aliasing -Wno-packed-bitfield-compat -g +LDFLAGS?=-Winline -g OPTIMIZEFLAGS?= #-fdiagnostics-show-option - shows which flags can be used with -Werror -DEFINES+=-DGIT_COMMIT=$(shell git log -1 --format="%H") +DEFINES+=-DGIT_COMMIT=$(shell git log -1 --format="%h") ifeq ($(shell uname),Darwin) MACOSX=1 @@ -128,7 +128,7 @@ BASEADDRESS=0x08000000 ifeq ($(BOARD),) # Try and guess board names - ifeq ($(shell uname -m),armv6l) + ifneq ($(shell grep Raspbian /etc/os-release),) BOARD=RASPBERRYPI # just a guess else ifeq ($(shell uname -n),beaglebone) BOARD=BEAGLEBONE @@ -266,6 +266,7 @@ src/jsinteractive.c \ src/jsdevices.c \ src/jstimer.c \ src/jsi2c.c \ +src/jsserial.c \ src/jsspi.c \ src/jshardware_common.c \ $(WRAPPERFILE) @@ -307,12 +308,13 @@ SOURCES += \ libs/compression/heatshrink/heatshrink_encoder.c \ libs/compression/heatshrink/heatshrink_decoder.c \ libs/compression/compress_heatshrink.c - +WRAPPERSOURCES += \ +libs/compression/jswrap_heatshrink.c endif ifndef BOOTLOADER # ------------------------------------------------------------------------------ DON'T USE IN BOOTLOADER -ifdef USE_FILESYSTEM +ifeq ($(USE_FILESYSTEM),1) DEFINES += -DUSE_FILESYSTEM INCLUDE += -I$(ROOT)/libs/filesystem WRAPPERSOURCES += \ @@ -325,7 +327,7 @@ libs/filesystem/fat_sd/fattime.c \ libs/filesystem/fat_sd/ff.c \ libs/filesystem/fat_sd/option/unicode.c # for LFN support (see _USE_LFN in ff.h) -ifdef USE_FILESYSTEM_SDIO +ifeq ($(USE_FILESYSTEM_SDIO),1) DEFINES += -DUSE_FILESYSTEM_SDIO SOURCES += \ libs/filesystem/fat_sd/sdio_diskio.c \ @@ -355,7 +357,7 @@ else LIBS += -lm endif -ifdef USE_GRAPHICS +ifeq ($(USE_GRAPHICS),1) DEFINES += -DUSE_GRAPHICS INCLUDE += -I$(ROOT)/libs/graphics WRAPPERSOURCES += libs/graphics/jswrap_graphics.c @@ -365,7 +367,7 @@ libs/graphics/graphics.c \ libs/graphics/lcd_arraybuffer.c \ libs/graphics/lcd_js.c -ifdef USE_LCD_SDL +ifeq ($(USE_LCD_SDL),1) DEFINES += -DUSE_LCD_SDL SOURCES += libs/graphics/lcd_sdl.c LIBS += -lSDL @@ -377,18 +379,19 @@ ifdef USE_LCD_FSMC SOURCES += libs/graphics/lcd_fsmc.c endif -ifdef USE_TERMINAL + +ifeq ($(USE_TERMINAL),1) DEFINES += -DUSE_TERMINAL WRAPPERSOURCES += libs/graphics/jswrap_terminal.c endif endif -ifdef USE_USB_HID +ifeq ($(USE_USB_HID),1) DEFINES += -DUSE_USB_HID endif -ifdef USE_NET +ifeq ($(USE_NET),1) DEFINES += -DUSE_NET INCLUDE += -I$(ROOT)/libs/network -I$(ROOT)/libs/network -I$(ROOT)/libs/network/http WRAPPERSOURCES += \ @@ -511,7 +514,7 @@ ifdef USE_NET endif endif # USE_NET -ifdef USE_TV +ifeq ($(USE_TV),1) DEFINES += -DUSE_TV WRAPPERSOURCES += libs/tv/jswrap_tv.c INCLUDE += -I$(ROOT)/libs/tv @@ -519,7 +522,7 @@ ifdef USE_TV libs/tv/tv.c endif -ifdef USE_TRIGGER +ifeq ($(USE_TRIGGER),1) DEFINES += -DUSE_TRIGGER WRAPPERSOURCES += libs/trigger/jswrap_trigger.c INCLUDE += -I$(ROOT)/libs/trigger @@ -527,82 +530,35 @@ ifdef USE_TRIGGER libs/trigger/trigger.c endif -ifdef USE_HASHLIB - INCLUDE += -I$(ROOT)/libs/hashlib - WRAPPERSOURCES += \ - libs/hashlib/jswrap_hashlib.c - SOURCES += \ - libs/hashlib/sha2.c -endif - -ifdef USE_WIRINGPI +ifeq ($(USE_WIRINGPI),1) DEFINES += -DUSE_WIRINGPI LIBS += -lwiringPi INCLUDE += -I/usr/local/include -L/usr/local/lib endif -ifdef USE_BLUETOOTH +ifeq ($(USE_BLUETOOTH),1) DEFINES += -DBLUETOOTH INCLUDE += -I$(ROOT)/libs/bluetooth WRAPPERSOURCES += libs/bluetooth/jswrap_bluetooth.c SOURCES += libs/bluetooth/bluetooth_utils.c endif -ifdef USE_CRYPTO - DEFINES += -DUSE_CRYPTO - INCLUDE += -I$(ROOT)/libs/crypto - INCLUDE += -I$(ROOT)/libs/crypto/mbedtls - INCLUDE += -I$(ROOT)/libs/crypto/mbedtls/include - WRAPPERSOURCES += libs/crypto/jswrap_crypto.c - SOURCES += \ -libs/crypto/mbedtls/library/sha1.c \ -libs/crypto/mbedtls/library/sha256.c \ -libs/crypto/mbedtls/library/sha512.c - -ifdef USE_TLS - USE_AES=1 - DEFINES += -DUSE_TLS - SOURCES += \ -libs/crypto/mbedtls/library/bignum.c \ -libs/crypto/mbedtls/library/ctr_drbg.c \ -libs/crypto/mbedtls/library/debug.c \ -libs/crypto/mbedtls/library/ecp.c \ -libs/crypto/mbedtls/library/ecp_curves.c \ -libs/crypto/mbedtls/library/entropy.c \ -libs/crypto/mbedtls/library/entropy_poll.c \ -libs/crypto/mbedtls/library/md5.c \ -libs/crypto/mbedtls/library/pk.c \ -libs/crypto/mbedtls/library/pkparse.c \ -libs/crypto/mbedtls/library/pk_wrap.c \ -libs/crypto/mbedtls/library/rsa.c \ -libs/crypto/mbedtls/library/ssl_ciphersuites.c \ -libs/crypto/mbedtls/library/ssl_cli.c \ -libs/crypto/mbedtls/library/ssl_tls.c \ -libs/crypto/mbedtls/library/ssl_srv.c \ -libs/crypto/mbedtls/library/x509.c \ -libs/crypto/mbedtls/library/x509_crt.c -endif -ifdef USE_AES - DEFINES += -DUSE_AES - SOURCES += \ -libs/crypto/mbedtls/library/aes.c \ -libs/crypto/mbedtls/library/asn1parse.c \ -libs/crypto/mbedtls/library/cipher.c \ -libs/crypto/mbedtls/library/cipher_wrap.c \ -libs/crypto/mbedtls/library/md.c \ -libs/crypto/mbedtls/library/md_wrap.c \ -libs/crypto/mbedtls/library/oid.c \ -libs/crypto/mbedtls/library/pkcs5.c -endif +ifeq ($(USE_CRYPTO),1) + cryptofound:=$(shell if test -f make/crypto/$(FAMILY).make; then echo yes;fi) + ifeq ($(cryptofound),yes) + include make/crypto/$(FAMILY).make + else + include make/crypto/default.make + endif endif -ifdef USE_NEOPIXEL +ifeq ($(USE_NEOPIXEL),1) DEFINES += -DUSE_NEOPIXEL INCLUDE += -I$(ROOT)/libs/neopixel WRAPPERSOURCES += libs/neopixel/jswrap_neopixel.c endif -ifdef USE_NFC +ifeq ($(USE_NFC),1) DEFINES += -DUSE_NFC -DNFC_HAL_ENABLED=1 INCLUDE += -I$(NRF5X_SDK_PATH)/components/nfc/t2t_lib INCLUDE += -I$(NRF5X_SDK_PATH)/components/nfc/ndef/uri @@ -615,7 +571,7 @@ ifdef USE_NFC TARGETSOURCES += $(NRF5X_SDK_PATH)/components/nfc/t2t_lib/hal_t2t/hal_nfc_t2t.c endif -ifdef USE_WIO_LTE +ifeq ($(USE_WIO_LTE),1) INCLUDE += -I$(ROOT)/libs/wio_lte WRAPPERSOURCES += libs/wio_lte/jswrap_wio_lte.c SOURCES += targets/stm32/stm32_ws2812b_driver.c @@ -656,6 +612,7 @@ CFLAGS += $(OPTIMIZEFLAGS) -c $(ARCHFLAGS) $(DEFINES) $(INCLUDE) # -Wl,--gc-sections helps remove unused code # -Wl,--whole-archive checks for duplicates +# --specs=nano.specs uses newlib-nano ifdef NRF5X LDFLAGS += $(OPTIMIZEFLAGS) $(ARCHFLAGS) --specs=nano.specs -lc -lnosys else ifdef STM32 @@ -790,12 +747,12 @@ else # NO_COMPILE $(info WRAPPERSOURCES=$(WRAPPERSOURCES)); endif -lst: - $(PROJ_NAME).lst +lst: $(PROJ_NAME).lst clean: @echo Cleaning targets $(Q)find . -name \*.o | grep -v "./arm-bcm2708\|./gcc-arm-none-eabi" | xargs rm -f + $(Q)find . -name \*.d | grep -v "./arm-bcm2708\|./gcc-arm-none-eabi" | xargs rm -f $(Q)rm -f $(ROOT)/gen/*.c $(ROOT)/gen/*.h $(ROOT)/gen/*.ld $(Q)rm -f $(ROOT)/scripts/*.pyc $(ROOT)/boards/*.pyc $(Q)rm -f $(PROJ_NAME).elf diff --git a/README.md b/README.md index 4f0075138..706e07e38 100644 --- a/README.md +++ b/README.md @@ -77,38 +77,41 @@ There are a bunch of tests in the `tests` directory. See [`tests/README.md`](tes Current State ------------- -The officially supported boards are the [Original Espruino Board](http://www.espruino.com/EspruinoBoard) and the [Espruino Pico Board](http://www.espruino.com/Pico). The [Web IDE](http://www.espruino.com/webide) is able to automatically download and flash the latest version of Espruino for you. +The [officially supported boards](http://www.espruino.com/Order) are the best supported. They come pre-installed with Espruino and you are able to easily download and flash the latest versions of Espruino to them. While Espruino can run on other boards, we make no money from them and so cannot afford to test, fix or support the firmware on them. We're dependent on the community. -You can download binaries from http://www.espruino.com/Download (these aren't the latest, but are more likely to work with your board). +You can download binaries from http://www.espruino.com/Download -If you are a board manufacturer interested in getting your board officially supported, please [Contact Us](http://www.espruino.com/Contact+Us). + +If you are a board manufacturer interested in getting your board officially supported, please [check out this page](http://www.espruino.com/Business). * [Original Espruino Board](http://www.espruino.com/EspruinoBoard) - great support. * [Espruino Pico Board](http://www.espruino.com/Pico) - great support. * [Puck.js](http://www.espruino.com/Puck.js) - great support. +* [MDBT42Q module and breakout board](http://www.espruino.com/MDBT42Q) - great support. * [Espruino WiFi Board](http://www.espruino.com/WiFi) - great support. * Linux - WORKING -* [BBC micro:bit](http://www.espruino.com/MicroBit) - WORKING -* [STM32VLDISCOVERY](http://www.espruino.com/ReferenceSTM32VLDISCOVERY) - WORKING - limited memory so some features removed -* [STM32F3DISCOVERY](http://www.espruino.com/ReferenceSTM32F3DISCOVERY) - USB BROKEN -* [STM32F4DISCOVERY](http://www.espruino.com/ReferenceSTM32F4DISCOVERY) - WORKING +* [BBC micro:bit](http://www.espruino.com/MicroBit) - WORKING, builds available +* [STM32VLDISCOVERY](http://www.espruino.com/ReferenceSTM32VLDISCOVERY) - WORKING, builds available - limited memory so some features removed +* [STM32F3DISCOVERY](http://www.espruino.com/ReferenceSTM32F3DISCOVERY) - NO LONGER SUPPORTED +* [STM32F4DISCOVERY](http://www.espruino.com/ReferenceSTM32F4DISCOVERY) - WORKING, builds available * STM32F401CDISCOVERY - appears WORKING, but very little testing done * STM32F429IDISCOVERY - WORKING over serial (A9/A10). No USB and no LCD support * NRF51822 Development Kit - WORKING -* NRF52832 Development Kit - WORKING +* NRF52832 Development Kit - WORKING, builds available * [HY STM32 2.4"](http://www.espruino.com/ReferenceHYSTM32_24) - WORKING * [HY STM32 2.8"](http://www.espruino.com/ReferenceHYSTM32_28) - WORKING - limited memory so some features removed -* [HY STM32 3.2"](http://www.espruino.com/ReferenceHYSTM32_32) - WORKING -* [Olimexino STM32 / Leaflabs Maple](http://www.espruino.com/ReferenceOLIMEXINO_STM32) - WORKING - limited memory so some features removed * Carambola - WORKING - GPIO via filesystem (no I2C) * Raspberry Pi - WORKING - GPIO via filesystem or wiringPi (no I2C) * Sony SmartWatch - NOT WORKING - USB VCP support for F2 still needed * LC-TECH STM32F103RBT6 - WORKING, but with some issues (LED inverted logic, BTN needs pullup to work) * [ST NUCLEO-F401RE](http://www.espruino.com/ReferenceNUCLEOF401RE) - WORKING * ST NUCLEO-F411RE - WORKING -* ESP8266 - WORKING - Reasonably stable, but expect to find issues +* [ESP8266](http://www.espruino.com/EspruinoESP8266) - WORKING +* [ESP32](http://www.espruino.com/ESP32) - WORKING - Reasonably stable, but expect to find issues +* [HY STM32 3.2"](http://www.espruino.com/ReferenceHYSTM32_32) - NO LONGER SUPPORTED +* [Olimexino STM32 / Leaflabs Maple](http://www.espruino.com/ReferenceOLIMEXINO_STM32) - NO LONGER SUPPORTED * Arduino (AVR) - NOT POSSIBLE due to the Hardward architecture of AVRs, even though it would fit into an ATMEGA2560. If `avr-gcc` ever gains an easy way to emulate Von Neumann architecture then it might be portable, but for now it isn't. diff --git a/README_BuildProcess.md b/README_BuildProcess.md index d253cecee..2dfd51c28 100644 --- a/README_BuildProcess.md +++ b/README_BuildProcess.md @@ -99,9 +99,26 @@ These contain: * `binaries` - available binaries - this is used by the Web IDE to allow the user to choose which binary to upload * `build` - controls what gets build via the Makefile: * `optimizeflags` - flags like `-O3` to give to the compiler - * `libraries` - list of libraries to include + * `libraries` - list of libraries to include - these get transformed into `USE_LIBNAME` defines * `makefile` - list of commands/definitions to execute in the Makefile +#### info.makefile definitions + +This is a partial list of definitions that can be added in a `BOARD.py` file's `info.build.makefile` array, eg: `'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"Puck.js"\''` + +* `SAVE_ON_FLASH` - Remove some features (like any ES6 support) to target devices with ~128kB Flash +* `SAVE_ON_FLASH_MATH` - Remove some less-used Maths functions that use a bunch of Flash memory +* `SAVE_ON_FLASH_EXTREME` - Pull out as many features as possible to target devices with ~128kB Flash that also want things like Filesystem support +* `BLUETOOTH_NAME_PREFIX="..."` - Make the Bluetooth LE device's name `BLUETOOTH_NAME_PREFIX` followed by the last 2 bytes of the MAC address. +* `NFC_DEFAULT_URL="http://foo"` - If defined, set the advertised NFC URL to the one given, plus `?a=ble_address`. Only do it for a fresh boot - not when code has been saved. +* `PIN_NAMES_DIRECT=1` - Package skips out some pins (maybe there's `D0`,`D1`,`D3` but no `D2`), so the code must search rather than just offsetting based on pin number. +* `DUMP_IGNORE_VARIABLES="...\0"` - string containing zero-terminated list of global variable names to ignore when `dump()` is called. Must be explicityly zero-terminated so there are 2 trailing 0s +* `FSMC_BITBANG` - if using a built-in FSMC Graphics LCD, don't use the hardware but instead do it in software +* `FLASH_64BITS_ALIGNMENT=1` - For testing 64 bit flash writes on linux +* `JSMODULESOURCES+=libs/.../foo.min.js` - include the given JS file as a module that can be used via `require("foo")` +* `JSVAR_MALLOC` - Allocate space for variables at jsvInit time, rather than statically + + ### chip * `part` - Chip part number (this is defined in the compiler - eg if the part is `STM32F401CDU6`, `-DSTM32F401CDU6` is put on the GCC command-line) diff --git a/README_Building.md b/README_Building.md index e490ca78c..a65a335d6 100644 --- a/README_Building.md +++ b/README_Building.md @@ -214,16 +214,19 @@ Dependant on the board, either usb or bluetooth can be used to program the board * USB * the board appears as a drive to drop a hex on -#### for [Puck.js](http://www.espruino.com/Puck.js) +#### for [Puck.js](http://www.espruino.com/Puck.js) and [Pixl.js](http://www.espruino.com/Pixl.js) -The Puck.js is based on the nRF52 +The Puck.js and Pixl.js are based on the nRF52 ```bash +# Puck.js make clean && DFU_UPDATE_BUILD=1 BOARD=PUCKJS RELEASE=1 make +# Pixl.js +make clean && DFU_UPDATE_BUILD=1 BOARD=PIXLJS RELEASE=1 make ``` -The resulting file is a zip that has to be transferred to the puck.js via a Bluetooth low energy device. -See for information concerning transferring the zip to the puck.js. +The resulting file is a zip that has to be transferred via Bluetooth Low Energy. +See the [Puck.js](http://www.espruino.com/Puck.js#firmware-updates) and [Pixl.js](http://www.espruino.com/Pixl.js#firmware-updates) pages for information concerning transferring the ZIP. #### for [NRF52-DK](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK) diff --git a/boards/ARIETTA_G25.py b/boards/ARIETTA_G25.py index 9d93d7af7..a756c2d5f 100644 --- a/boards/ARIETTA_G25.py +++ b/boards/ARIETTA_G25.py @@ -25,9 +25,8 @@ info = { 'NET', 'GRAPHICS', 'FILESYSTEM', - 'CRYPTO', - 'TLS', - 'HASHLIB', + 'CRYPTO','SHA256','SHA512', + 'TLS', 'TELNET', ], 'makefile' : [ diff --git a/boards/BEAGLEBONE_BLACK.py b/boards/BEAGLEBONE_BLACK.py index c5a6162ab..3edf1fdaf 100644 --- a/boards/BEAGLEBONE_BLACK.py +++ b/boards/BEAGLEBONE_BLACK.py @@ -25,9 +25,8 @@ info = { 'NET', 'GRAPHICS', 'FILESYSTEM', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', - 'HASHLIB', 'TELNET', ], 'makefile' : [ diff --git a/boards/CARAMBOLA.py b/boards/CARAMBOLA.py index 0b880c50c..55f95503b 100644 --- a/boards/CARAMBOLA.py +++ b/boards/CARAMBOLA.py @@ -24,9 +24,8 @@ info = { 'NET', 'GRAPHICS', 'FILESYSTEM', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', - 'HASHLIB', 'TELNET', ], 'makefile' : [ diff --git a/boards/DPTBOARD.py b/boards/DPTBOARD.py index 355f206b6..d785f9e0b 100644 --- a/boards/DPTBOARD.py +++ b/boards/DPTBOARD.py @@ -24,9 +24,8 @@ info = { 'NET', 'GRAPHICS', 'FILESYSTEM', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', - 'HASHLIB', 'TELNET', ], 'makefile' : [ diff --git a/boards/EFM32GGSTK.py b/boards/EFM32GGSTK.py index de3696d1c..46baf31fc 100644 --- a/boards/EFM32GGSTK.py +++ b/boards/EFM32GGSTK.py @@ -7,7 +7,7 @@ info = { 'link': [ "https://www.silabs.com/products/mcu/lowpower/Pages/efm32gg-stk3700.aspx" ], 'variables': 1720, 'binary_name': 'espruino_%v_efm32ggstk.bin', - 'default_console' : "EV_SERIAL4", + 'default_console' : "EV_SERIAL1", 'default_console_tx' : "E0", 'default_console_rx' : "E1", 'default_console_baudrate' : "115200", diff --git a/boards/ESP32.py b/boards/ESP32.py index dfbf7028d..3e6004433 100755 --- a/boards/ESP32.py +++ b/boards/ESP32.py @@ -19,7 +19,7 @@ info = { 'espruino_page_link' : 'ESP32', 'default_console' : "EV_SERIAL1", 'default_console_baudrate' : "115200", - 'variables' : 5000, + 'variables' : 2500, # JSVAR_MALLOC is defined below - so this can vary depending on what is initialised 'binary_name' : 'espruino_%v_esp32.bin', 'build' : { 'optimizeflags' : '-Og', @@ -27,15 +27,17 @@ info = { 'ESP32', 'NET', 'GRAPHICS', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', 'TELNET', 'NEOPIXEL', 'FILESYSTEM', - 'FLASHFS' + 'FLASHFS', + 'BLUETOOTH' ], 'makefile' : [ - 'DEFINES+=-DESP_PLATFORM -DESP32=1' + 'DEFINES+=-DESP_PLATFORM -DESP32=1', + 'DEFINES+=-DJSVAR_MALLOC' # Allocate space for variables at jsvInit time ] } }; @@ -53,10 +55,10 @@ chip = { 'adc' : 2, 'dac' : 0, 'saved_code' : { - 'address' : 0x100000, + 'address' : 0x2C0000, 'page_size' : 4096, - 'pages' : 16, - 'flash_available' : 960, # firmware can be up to this size + 'pages' : 64, + 'flash_available' : 1344, # firmware can be up to this size - see paritions_espruino.csv }, }; devices = { diff --git a/boards/ESP8266_4MB.py b/boards/ESP8266_4MB.py index d11f6380c..4b161c97c 100644 --- a/boards/ESP8266_4MB.py +++ b/boards/ESP8266_4MB.py @@ -33,7 +33,7 @@ info = { ], 'makefile' : [ 'FLASH_4MB=1', - 'ESP_FLASH_MAX=962560', + 'ESP_FLASH_MAX=831488', 'FLASH_BAUD=460800' ] } @@ -111,10 +111,25 @@ board_esp01 = { boards = [ board_esp12 ]; def get_pins(): - pins = pinutils.generate_pins(0,16) - pinutils.findpin(pins, "PD0", True)["functions"]["LED_1"]=0; - pinutils.findpin(pins, "PD1", True)["functions"]["USART0_TX"]=0; - pinutils.findpin(pins, "PD2", True)["functions"]["USART1_TX"]=0; - pinutils.findpin(pins, "PD3", True)["functions"]["USART0_RX"]=0; - # just fake pins D0 .. D16 - return pins + # add pins D0 .. D16,A0 + pins = [ + { "name":"PD0", "sortingname":"D00", "port":"D", "num":"0", "functions":{"LED_1":0}, "csv":{} }, + { "name":"PD1", "sortingname":"D01", "port":"D", "num":"1", "functions":{"USART1_TX":0}, "csv":{} }, + { "name":"PD2", "sortingname":"D02", "port":"D", "num":"2", "functions":{"USART2_TX":0}, "csv":{} }, + { "name":"PD3", "sortingname":"D03", "port":"D", "num":"3", "functions":{"USART1_RX":0}, "csv":{} }, + { "name":"PD4", "sortingname":"D04", "port":"D", "num":"4", "functions":{}, "csv":{} }, + { "name":"PD5", "sortingname":"D05", "port":"D", "num":"5", "functions":{}, "csv":{} }, + { "name":"PD6", "sortingname":"D06", "port":"D", "num":"6", "functions":{}, "csv":{} }, + { "name":"PD7", "sortingname":"D07", "port":"D", "num":"7", "functions":{}, "csv":{} }, + { "name":"PD8", "sortingname":"D08", "port":"D", "num":"8", "functions":{}, "csv":{} }, + { "name":"PD9", "sortingname":"D09", "port":"D", "num":"9", "functions":{}, "csv":{} }, + { "name":"PD10", "sortingname":"D10", "port":"D", "num":"10", "functions":{}, "csv":{} }, + { "name":"PD11", "sortingname":"D11", "port":"D", "num":"11", "functions":{}, "csv":{} }, + { "name":"PD12", "sortingname":"D12", "port":"D", "num":"12", "functions":{}, "csv":{} }, + { "name":"PD13", "sortingname":"D13", "port":"D", "num":"13", "functions":{}, "csv":{} }, + { "name":"PD14", "sortingname":"D14", "port":"D", "num":"14", "functions":{}, "csv":{} }, + { "name":"PD15", "sortingname":"D15", "port":"D", "num":"15", "functions":{}, "csv":{} }, + { "name":"PD16", "sortingname":"D16", "port":"D", "num":"16", "functions":{}, "csv":{} }, + { "name":"PA0", "sortingname":"A00", "port":"A", "num":"17", "functions":{ "ADC1_IN0":0 }, "csv":{} } + ] + return pins; diff --git a/boards/ESP8266_BOARD.py b/boards/ESP8266_BOARD.py index 8c8af12f1..df35421a9 100644 --- a/boards/ESP8266_BOARD.py +++ b/boards/ESP8266_BOARD.py @@ -44,9 +44,9 @@ chip = { 'adc' : 1, 'dac' : 0, 'saved_code' : { - 'address' : 0x78000, + 'address' : 0x77000, 'page_size' : 4096, - 'pages' : 3, + 'pages' : 4, 'flash_available' : 468, # firmware can be up to this size }, }; @@ -132,10 +132,25 @@ board_esp01["_css"] = """ boards = [ board_esp12, board_esp01 ]; def get_pins(): - pins = pinutils.generate_pins(0,16) - pinutils.findpin(pins, "PD0", True)["functions"]["LED_1"]=0; - pinutils.findpin(pins, "PD1", True)["functions"]["USART0_TX"]=0; - pinutils.findpin(pins, "PD2", True)["functions"]["USART1_TX"]=0; - pinutils.findpin(pins, "PD3", True)["functions"]["USART0_RX"]=0; - # just fake pins D0 .. D16 - return pins + # add pins D0 .. D16,A0 + pins = [ + { "name":"PD0", "sortingname":"D00", "port":"D", "num":"0", "functions":{"LED_1":0}, "csv":{} }, + { "name":"PD1", "sortingname":"D01", "port":"D", "num":"1", "functions":{"USART1_TX":0}, "csv":{} }, + { "name":"PD2", "sortingname":"D02", "port":"D", "num":"2", "functions":{"USART2_TX":0}, "csv":{} }, + { "name":"PD3", "sortingname":"D03", "port":"D", "num":"3", "functions":{"USART1_RX":0}, "csv":{} }, + { "name":"PD4", "sortingname":"D04", "port":"D", "num":"4", "functions":{}, "csv":{} }, + { "name":"PD5", "sortingname":"D05", "port":"D", "num":"5", "functions":{}, "csv":{} }, + { "name":"PD6", "sortingname":"D06", "port":"D", "num":"6", "functions":{}, "csv":{} }, + { "name":"PD7", "sortingname":"D07", "port":"D", "num":"7", "functions":{}, "csv":{} }, + { "name":"PD8", "sortingname":"D08", "port":"D", "num":"8", "functions":{}, "csv":{} }, + { "name":"PD9", "sortingname":"D09", "port":"D", "num":"9", "functions":{}, "csv":{} }, + { "name":"PD10", "sortingname":"D10", "port":"D", "num":"10", "functions":{}, "csv":{} }, + { "name":"PD11", "sortingname":"D11", "port":"D", "num":"11", "functions":{}, "csv":{} }, + { "name":"PD12", "sortingname":"D12", "port":"D", "num":"12", "functions":{}, "csv":{} }, + { "name":"PD13", "sortingname":"D13", "port":"D", "num":"13", "functions":{}, "csv":{} }, + { "name":"PD14", "sortingname":"D14", "port":"D", "num":"14", "functions":{}, "csv":{} }, + { "name":"PD15", "sortingname":"D15", "port":"D", "num":"15", "functions":{}, "csv":{} }, + { "name":"PD16", "sortingname":"D16", "port":"D", "num":"16", "functions":{}, "csv":{} }, + { "name":"PA0", "sortingname":"A00", "port":"A", "num":"17", "functions":{ "ADC1_IN0":0 }, "csv":{} } + ] + return pins; diff --git a/boards/ESPRUINOBOARD.py b/boards/ESPRUINOBOARD.py index 383a6e6e7..add2d7e75 100644 --- a/boards/ESPRUINOBOARD.py +++ b/boards/ESPRUINOBOARD.py @@ -16,7 +16,7 @@ import pinutils; info = { 'name' : "Original Espruino Board rev 1.3/1.4", - 'link' : [ "http://www.espruino.com/EspruinoBoard" ], + 'link' : [ "http://www.espruino.com/Original" ], 'espruino_page_link' : "EspruinoBoard", 'default_console' : "EV_SERIAL1", 'default_console_tx' : "A9", @@ -26,8 +26,8 @@ info = { 'serial_bootloader' : True, 'binary_name' : 'espruino_%v_espruino_1r3.bin', 'binaries' : [ - { 'filename' : 'espruino_%v_espruino_1r3_wiznet.bin', 'description' : "WIZNet W5500 Ethernet Networking"}, - { 'filename' : 'espruino_%v_espruino_1r3.bin', 'description' : "TI CC3000 WiFi Networking"}, + { 'filename' : 'espruino_%v_espruino_1r3_wiznet.bin', 'description' : "WIZNet W5500 Ethernet Networking (no crypto lib)"}, + { 'filename' : 'espruino_%v_espruino_1r3.bin', 'description' : "AT Command Networking only"}, ], 'build' : { 'optimizeflags' : '-Os', @@ -35,7 +35,7 @@ info = { 'NET', 'GRAPHICS', 'NEOPIXEL', - 'HASHLIB', + 'CRYPTO','SHA1_JS', # 'TV', # TV had to be removed because of flash usage 'FILESYSTEM' ], diff --git a/boards/ESPRUINOWIFI.py b/boards/ESPRUINOWIFI.py index 9aab5a011..cfaf9e69f 100644 --- a/boards/ESPRUINOWIFI.py +++ b/boards/ESPRUINOWIFI.py @@ -34,9 +34,8 @@ info = { 'NET', 'GRAPHICS', 'TV', - 'HASHLIB', 'FILESYSTEM', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', 'NEOPIXEL' ], @@ -46,7 +45,7 @@ info = { 'WIZNET=1', # Add support for W5500 by default (not CC3000) 'STLIB=STM32F411xE', 'PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f4/lib/startup_stm32f401xx.o', - 'JSMODULESOURCES+=libs/js/espruino_wifi/AT.min.js', + 'JSMODULESOURCES+=libs/js/AT.min.js', 'JSMODULESOURCES+=libs/js/espruino_wifi/Wifi.min.js', ] } diff --git a/boards/HEXBADGE.py b/boards/HEXBADGE.py index a4fa8d5e5..0b504e53f 100644 --- a/boards/HEXBADGE.py +++ b/boards/HEXBADGE.py @@ -55,15 +55,15 @@ chip = { 'flash' : 512, 'speed' : 64, 'usart' : 1, - 'spi' : 3, - 'i2c' : 2, + 'spi' : 1, + 'i2c' : 1, 'adc' : 1, 'dac' : 0, 'saved_code' : { - 'address' : ((118 - 5) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'address' : ((118 - 10) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 'page_size' : 4096, - 'pages' : 5, - 'flash_available' : 512 - ((31 + 8 + 1 + 5)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 1, code 5. Each page is 4 kb. + 'pages' : 10, + 'flash_available' : 512 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. }, }; diff --git a/boards/LINUX.py b/boards/LINUX.py index deb8eee1e..a2f619710 100644 --- a/boards/LINUX.py +++ b/boards/LINUX.py @@ -24,9 +24,8 @@ info = { 'NET', 'GRAPHICS', 'FILESYSTEM', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', - 'HASHLIB', 'TELNET', ], 'makefile' : [ diff --git a/boards/MAPLERET6_STM32.py b/boards/MAPLERET6_STM32.py index 2e023ce57..4801ac41a 100644 --- a/boards/MAPLERET6_STM32.py +++ b/boards/MAPLERET6_STM32.py @@ -27,7 +27,6 @@ info = { 'GRAPHICS', 'FILESYSTEM', 'TV', - 'HASHLIB' ], 'makefile' : [ 'STLIB=STM32F10X_HD', diff --git a/boards/MDBT42Q.py b/boards/MDBT42Q.py index 84c642e35..56c281b10 100644 --- a/boards/MDBT42Q.py +++ b/boards/MDBT42Q.py @@ -17,6 +17,8 @@ import pinutils; info = { 'name' : "MDBT42Q Module", + 'link' : [ "http://www.espruino.com/MDBT42Q" ], + 'espruino_page_link' : 'MDBT42Q', 'default_console' : "EV_SERIAL1", 'default_console_tx' : "D6", 'default_console_rx' : "D8", @@ -30,17 +32,17 @@ info = { 'BLUETOOTH', 'NET', 'GRAPHICS', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', + 'AES', 'NFC', - 'NEOPIXEL' - #'HASHLIB' - #'FILESYSTEM' + 'NEOPIXEL', + 'FILESYSTEM' #'TLS' ], 'makefile' : [ 'DEFINES+=-DHAL_NFC_ENGINEERING_BC_FTPAN_WORKAROUND=1', # Looks like proper production nRF52s had this issue 'DEFINES+=-DCONFIG_GPIO_AS_PINRESET', # Allow the reset pin to work - 'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"MQBT42Q"\'', + 'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"MDBT42Q"\'', 'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem', 'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C' ] @@ -55,53 +57,121 @@ chip = { 'flash' : 512, 'speed' : 64, 'usart' : 1, - 'spi' : 3, - 'i2c' : 2, + 'spi' : 1, + 'i2c' : 1, 'adc' : 1, 'dac' : 0, 'saved_code' : { - 'address' : ((118 - 3) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'address' : ((118 - 10) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 'page_size' : 4096, - 'pages' : 3, - 'flash_available' : 512 - ((31 + 8 + 1 + 3)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 1, code 3. Each page is 4 kb. + 'pages' : 10, + 'flash_available' : 512 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. }, }; devices = { 'LED1' : { 'pin' : 'D1' }, + 'LED2' : { 'pin' : 'D2', 'no_bootloader':True }, # don't use LED2 in the bootloader since we may be using a bare module 'BTN1' : { 'pin' : 'D0', 'pinstate' : 'IN_PULLDOWN' }, 'NFC': { 'pin_a':'D9', 'pin_b':'D10' }, # Pin D22 is used for clock when driving neopixels - as not specifying a pin seems to break things }; # left-right, or top-bottom order -board = { +board_module = { 'left' : [ 'GND','','','','D25','D26','D27','D28','D29','D30','D31','DEC4','DCC','VDD'], - 'right' : [ 'GND','D22','SWDIO','SWDCLK','D21/NRST','D20','D19','D18','D17','D16','D15','D14','D13','D12','D11' ], + 'right2' : [ 'D24', '', 'D23'], + 'right' : [ 'GND','D22','SWDIO','SWDCLK','D21','D20','D19','D18','D17','D16','D15','D14','D13','D12','D11' ], 'bottom' : [ 'GND','D0','D1','D2','D3','D4','D5','D6','D7','D8','D9','D10','GND' ], + '_notes' : { + 'D21' : "Also NRST if configured" + } }; -board["_css"] = """ +board_module["_css"] = """ #board { - width: 800px; - height: 800px; + width: 359px; + height: 484px; top: 0px; - left : 0px; + left : 200px; background-image: url(img/MDBT42Q.jpg); } #boardcontainer { - height: 900px; + height: 650px; } -#bottom { - top: 639px; - left: 291px; +#board #bottom { + top: 440px; + left: 56px; } -#right { - top: 304px; - left: 640px; +#board #left { + top: 115px; + right: 316px; } +#board #right2 { + top: 115px; + right: 110px; +} +#board #right { + top: 115px; + left: 316px; +} + +#board .leftpin { height: 17px; } +#board .left2pin { height: 17px; } +#board .rightpin { height: 17px; } +#board .bottompin { width: 15px; padding:0px; } """; +board_breakout = { + 'left' : [ 'D25','D26','D27','D28','D29','D30','D31','D3','D4','D5','D11' ], + 'right' : [ 'D22','D20','D19','D18','D17','D16','D15','D14','3.3','Vin','GND'], + 'bottom' : [ 'D6','D8','D7','Vin','GND' ], + 'top' : [ 'D9','D10' ], + '_hide_not_on_connectors' : True, + '_class' : "board_breakout", + '_notes' : { + 'D8' : "Serial Console RX when Bluetooth disconnected", + 'D6' : "Serial Console TX when Bluetooth disconnected", + } +}; + +board_breakout["_css"] = """ +#board { + width: 255px; + height: 400px; + top: 0px; + left : 200px; + background-image: url(img/MDBT42Q_BREAKOUT.png); +} +#boardcontainer { + height: 600px; +} +#board #bottom { + top: 410px; + left: 40px; +} +#board #top { + bottom: 75px; + left: 167px; +} +#board #left { + top: 17px; + right: 256px; +} +#board #right { + top: 17px; + left: 256px; +} + +#board .leftpin { height: 33px; } +#board .rightpin { height: 33px; } +#board .toppin { width: 15px; padding:0px; } +#board .bottompin { width: 31px; padding:0px; } +"""; + +boards = [board_module, board_breakout]; + + def get_pins(): pins = pinutils.generate_pins(0,31) # 32 General Purpose I/O Pins. pinutils.findpin(pins, "PD0", True)["functions"]["XL1"]=0; diff --git a/boards/MICROBIT.py b/boards/MICROBIT.py index ba1b90309..e3d7088e6 100644 --- a/boards/MICROBIT.py +++ b/boards/MICROBIT.py @@ -34,7 +34,7 @@ info = { 'makefile' : [ 'SAVE_ON_FLASH=1', 'DEFINES+=-DCONFIG_GPIO_AS_PINRESET', # Allow the reset pin to work - 'DEFINES+=-DUSE_DEBUGGER -DUSE_TAB_COMPLETE', + 'DEFINES+=-DUSE_TAB_COMPLETE', # Removed -DUSE_DEBUGGER due to firmware size issues 'INCLUDE += -I$(ROOT)/libs/microbit', 'WRAPPERSOURCES += libs/microbit/jswrap_microbit.c' ] @@ -55,10 +55,10 @@ chip = { # If using DFU bootloader, it sits at 0x3C000 - 0x40000 (0x40000 is end of flash) # Might want to change 256 -> 240 in the code below 'saved_code' : { - 'address' : ((256 - 3) * 1024), + 'address' : ((256 - 2) * 1024), 'page_size' : 1024, - 'pages' : 3, - 'flash_available' : (256 - 108 - 3) # total flash pages - softdevice - saved code + 'pages' : 2, + 'flash_available' : (256 - 108 - 2) # total flash pages - softdevice - saved code } }; @@ -107,18 +107,18 @@ board["_css"] = """ def get_pins(): pins = [ - { "name":"PD0", "sortingname":"D00", "port":"D", "num":"1", "functions":{ "ADC1_IN4":0 }, "csv":{} }, + { "name":"PD0", "sortingname":"D00", "port":"D", "num":"1", "functions":{ "ADC1_IN2":0 }, "csv":{} }, { "name":"PD1", "sortingname":"D01", "port":"D", "num":"2", "functions":{ "ADC1_IN3":0 }, "csv":{} }, - { "name":"PD2", "sortingname":"D02", "port":"D", "num":"3", "functions":{ "ADC1_IN2":0 }, "csv":{} }, + { "name":"PD2", "sortingname":"D02", "port":"D", "num":"3", "functions":{ "ADC1_IN4":0 }, "csv":{} }, { "name":"PD3", "sortingname":"D03", "port":"D", "num":"4", "functions":{ "ADC1_IN5":0 }, "csv":{} }, # LED col 1 - { "name":"PD4", "sortingname":"D04", "port":"D", "num":"5", "functions":{}, "csv":{} }, # BTNA - { "name":"PD5", "sortingname":"D05", "port":"D", "num":"17", "functions":{ "ADC1_IN6":0 }, "csv":{} }, # LED col 2 + { "name":"PD4", "sortingname":"D04", "port":"D", "num":"5", "functions":{ "ADC1_IN6":0 }, "csv":{} }, # BTNA + { "name":"PD5", "sortingname":"D05", "port":"D", "num":"17", "functions":{}, "csv":{} }, # LED col 2 { "name":"PD6", "sortingname":"D06", "port":"D", "num":"12", "functions":{}, "csv":{} }, # LED row 2 { "name":"PD7", "sortingname":"D07", "port":"D", "num":"11", "functions":{}, "csv":{} }, # LED row 1 { "name":"PD8", "sortingname":"D08", "port":"D", "num":"18", "functions":{}, "csv":{} }, { "name":"PD9", "sortingname":"D09", "port":"D", "num":"10", "functions":{}, "csv":{} }, # LED row 3 { "name":"PD10", "sortingname":"D10", "port":"D", "num":"6", "functions":{ "ADC1_IN7":0 }, "csv":{} }, # LED col 3 - { "name":"PD11", "sortingname":"D11", "port":"D", "num":"26", "functions":{}, "csv":{} }, # BTNB + { "name":"PD11", "sortingname":"D11", "port":"D", "num":"26", "functions":{ "ADC1_IN0":0 }, "csv":{} }, # BTNB { "name":"PD12", "sortingname":"D12", "port":"D", "num":"20", "functions":{}, "csv":{} }, { "name":"PD13", "sortingname":"D13", "port":"D", "num":"23", "functions":{ "SPI1_SCK":0 }, "csv":{} }, { "name":"PD14", "sortingname":"D14", "port":"D", "num":"22", "functions":{ "SPI1_MISO":0 }, "csv":{} }, diff --git a/boards/NRF52832DK.py b/boards/NRF52832DK.py index 0c8910293..d0ccfcf0c 100644 --- a/boards/NRF52832DK.py +++ b/boards/NRF52832DK.py @@ -16,8 +16,9 @@ import pinutils; info = { - 'name' : "nRF52 Preview Development Kit", - 'link' : [ "https://www.nordicsemi.com/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832" ], + 'name' : "nRF52 Development Kit", + 'link' : [ "https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK" ], + 'espruino_page_link' : 'nRF52832DK', # This is the PCA10036 'default_console' : "EV_SERIAL1", 'default_console_tx' : "D6", @@ -25,7 +26,7 @@ info = { 'default_console_baudrate' : "9600", 'variables' : 2250, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile. # 'bootloader' : 1, - 'binary_name' : 'espruino_%v_nrf52832.bin', + 'binary_name' : 'espruino_%v_nrf52832.hex', 'build' : { 'optimizeflags' : '-Os', 'libraries' : [ @@ -51,15 +52,15 @@ chip = { 'flash' : 512, 'speed' : 64, 'usart' : 1, - 'spi' : 3, - 'i2c' : 2, + 'spi' : 1, + 'i2c' : 1, 'adc' : 1, 'dac' : 0, 'saved_code' : { - 'address' : ((118 - 3) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'address' : ((118 - 10) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 'page_size' : 4096, - 'pages' : 3, - 'flash_available' : 512 - ((31 + 8 + 1 + 3)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 1, code 3. Each page is 4 kb. + 'pages' : 10, + 'flash_available' : 512 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. }, }; @@ -81,10 +82,39 @@ devices = { # left-right, or top-bottom order board = { - 'left' : [ 'VDD', 'VDD', 'RESET', 'VDD','5V','GND','GND','PD3','PD4','PD28','PD29','PD30','PD31'], - 'right' : [ 'PD27', 'PD26', 'PD2', 'GND', 'PD25','PD24','PD23', 'PD22','PD20','PD19','PD18','PD17','PD16','PD15','PD14','PD13','PD12','PD11','PD10','PD9','PD8','PD7','PD6','PD5','PD21','PD1','PD0'], + 'left' : [ 'VDD', 'VDD', 'RESET', 'VDD','5V','GND','GND','','','D3','D4','D28','D29','D30','D31'], + 'right' : [ + 'D27', 'D26', 'D2', 'GND', 'D25','D24','D23', 'D22','D20','D19','', + 'D18','D17','D16','D15','D14','D13','D12','D11','', + 'D10','D9','D8','D7','D6','D5','D21','D1','D0'], + '_notes' : { + 'D6' : "Serial console RX", + 'D8' : "Serial console TX" + } }; board["_css"] = """ +#board { + width: 528px; + height: 800px; + top: 0px; + left : 200px; + background-image: url(img/NRF52832DK.jpg); +} +#boardcontainer { + height: 900px; +} + +#left { + top: 219px; + right: 466px; +} +#right { + top: 150px; + left: 466px; +} + +.leftpin { height: 17px; } +.rightpin { height: 17px; } """; def get_pins(): @@ -114,7 +144,7 @@ def get_pins(): pinutils.findpin(pins, "PD18", True)["functions"]["NEGATED"]=0; pinutils.findpin(pins, "PD19", True)["functions"]["NEGATED"]=0; pinutils.findpin(pins, "PD20", True)["functions"]["NEGATED"]=0; - + # everything is non-5v tolerant for pin in pins: pin["functions"]["3.3"]=0; diff --git a/boards/NUCLEOF401RE.py b/boards/NUCLEOF401RE.py index 444b93e81..a2fd4a52c 100644 --- a/boards/NUCLEOF401RE.py +++ b/boards/NUCLEOF401RE.py @@ -60,7 +60,7 @@ chip = { 'page_size' : 131072, # size of pages : on STM32F401, last 2 pages are 128 Kbytes # we use the last flash page only, furthermore it persists after a firmware flash of the board 'pages' : 1, # count of pages we're using to save RAM to Flash, - 'flash_available' : 512 # binary will have a hole in it, so we just want to test against full size + 'flash_available' : 384 # we use the last 128k page }, #'place_text_section' : 0x08010000, # note flash_available above # TODO USELESS }; diff --git a/boards/NUCLEOF411RE.py b/boards/NUCLEOF411RE.py index 7a58b8a31..ffa1e95db 100644 --- a/boards/NUCLEOF411RE.py +++ b/boards/NUCLEOF411RE.py @@ -60,7 +60,7 @@ chip = { 'page_size' : 131072, # size of pages : on STM32F411, last 2 pages are 128 Kbytes # we use the last flash page only, furthermore it persists after a firmware flash of the board 'pages' : 1, # count of pages we're using to save RAM to Flash, - 'flash_available' : 512 # binary will have a hole in it, so we just want to test against full size + 'flash_available' : 384 # we use the last 128k page }, #'place_text_section' : 0x08010000, # note flash_available above # TODO USELESS }; diff --git a/boards/NUCLEOF413ZH.py b/boards/NUCLEOF413ZH.py index 152bca2da..f816e9820 100644 --- a/boards/NUCLEOF413ZH.py +++ b/boards/NUCLEOF413ZH.py @@ -60,7 +60,7 @@ chip = { 'page_size' : 131072, # size of pages : on STM32F411, last 2 pages are 128 Kbytes # we use the last flash page only, furthermore it persists after a firmware flash of the board 'pages' : 1, # count of pages we're using to save RAM to Flash, - 'flash_available' : 512 # binary will have a hole in it, so we just want to test against full size + 'flash_available' : 384 # we use the last 128k page }, #'place_text_section' : 0x08010000, # note flash_available above # TODO USELESS }; diff --git a/boards/OLIMEXINO_STM32_RE.py b/boards/OLIMEXINO_STM32_RE.py index a85d06fcd..ab3b6eb11 100644 --- a/boards/OLIMEXINO_STM32_RE.py +++ b/boards/OLIMEXINO_STM32_RE.py @@ -27,7 +27,6 @@ info = { 'GRAPHICS', 'FILESYSTEM', 'TV', - 'HASHLIB' ], 'makefile' : [ 'STLIB=STM32F10X_HD', diff --git a/boards/PICO_R1_3.py b/boards/PICO_R1_3.py index ef0ba90ee..b265a76d4 100644 --- a/boards/PICO_R1_3.py +++ b/boards/PICO_R1_3.py @@ -35,9 +35,8 @@ info = { 'NET', 'GRAPHICS', 'TV', - 'HASHLIB', 'FILESYSTEM', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', 'NEOPIXEL' ], diff --git a/boards/PIXLJS.py b/boards/PIXLJS.py index 09d665566..dfff9a227 100644 --- a/boards/PIXLJS.py +++ b/boards/PIXLJS.py @@ -24,7 +24,7 @@ info = { 'default_console_baudrate' : "9600", 'variables' : 2500, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile. 'bootloader' : 1, - 'binary_name' : 'espruino_%v_pixljs.bin', + 'binary_name' : 'espruino_%v_pixljs.hex', 'build' : { 'optimizeflags' : '-Os', 'libraries' : [ @@ -34,16 +34,23 @@ info = { 'NFC', 'NEOPIXEL', 'PIXLJS', + 'CRYPTO','SHA256','SHA512', + 'AES', + 'FILESYSTEM', 'TERMINAL' ], 'makefile' : [ + 'WIZNET=1','W5100=1', # Add WIZnet support - W5100 is the most common Arduino shield 'DEFINES+=-DHAL_NFC_ENGINEERING_BC_FTPAN_WORKAROUND=1', # Looks like proper production nRF52s had this issue # 'DEFINES+=-DCONFIG_GPIO_AS_PINRESET', # Allow the reset pin to work 'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"Pixl.js"\'', + 'DEFINES+=-DNFC_DEFAULT_URL=\'"https://www.espruino.com/ide"\'', + 'DEFINES+=-DDUMP_IGNORE_VARIABLES=\'"g\\0"\'', 'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem', 'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C', 'INCLUDE += -I$(ROOT)/libs/pixljs', - 'WRAPPERSOURCES += libs/pixljs/jswrap_pixljs.c' + 'WRAPPERSOURCES += libs/pixljs/jswrap_pixljs.c', + 'JSMODULESOURCES += libs/js/graphical_menu.min.js' ] } }; @@ -57,15 +64,15 @@ chip = { 'flash' : 512, 'speed' : 64, 'usart' : 1, - 'spi' : 3, - 'i2c' : 2, + 'spi' : 1, + 'i2c' : 1, 'adc' : 1, 'dac' : 0, 'saved_code' : { - 'address' : ((118 - 5) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'address' : ((118 - 9) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 'page_size' : 4096, - 'pages' : 5, - 'flash_available' : 512 - ((31 + 8 + 1 + 5)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 1, code 5. Each page is 4 kb. + 'pages' : 9, + 'flash_available' : 512 - ((31 + 8 + 2 + 9)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. }, }; @@ -89,19 +96,50 @@ devices = { # left-right, or top-bottom order board = { + 'top' : [ 'D0','D1','D2','D3','D4','D5','D6','D7','','D8','D9','D10','D11','D12','D13','GND','','A4','A5' ], + 'bottom' : [ 'A5','A4','A3','A2','A1','A0','','Vin','GND','GND','5V','3.3V','RST','3.3V','','','GND','Vin'], + 'bottom2' : [ '3.3V','SWIO','SWCK','GND'], + + '_notes' : { + 'D0' : "Serial Console RX when Bluetooth disconnected", + 'D1' : "Serial Console TX when Bluetooth disconnected", + 'A4' : "Also used for the pin marked SDA", + 'A5' : "Also used for the pin marked SCL", + '5V' : "Pixl.js has no 5v rail so this is not connected by default. A solder jumper can be used to connect it to Vin or 3.3v" + } }; board["_css"] = """ +#board { + width: 650px; + height: 568px; + top: 100px; + left : 100px; + background-image: url(img/PIXLJS.png); +} +#boardcontainer { + height: 800px; +} +#top { + bottom: 553px; + left: 74px; +} +#bottom { + top: 555px; + left: 74px; +} +#bottom2 { + top: 374px; + left: 64px; +} + +.toppin { width: 21px; } +.bottompin { width: 21px; } +.bottom2pin { width: 21px; } + """; def get_pins(): pins = [ - { "name":"PA0", "sortingname":"A00", "port":"A", "num":"2", "functions":{ "ADC1_IN0":0 }, "csv":{} }, - { "name":"PA1", "sortingname":"A01", "port":"A", "num":"3", "functions":{ "ADC1_IN1":0 }, "csv":{} }, - { "name":"PA2", "sortingname":"A02", "port":"A", "num":"4", "functions":{ "ADC1_IN2":0 }, "csv":{} }, - { "name":"PA3", "sortingname":"A03", "port":"A", "num":"5", "functions":{ "ADC1_IN3":0 }, "csv":{} }, - { "name":"PA4", "sortingname":"A04", "port":"A", "num":"28", "functions":{ "ADC1_IN4":0 }, "csv":{} }, - { "name":"PA5", "sortingname":"A05", "port":"A", "num":"29", "functions":{ "ADC1_IN5":0 }, "csv":{} }, - { "name":"PD0", "sortingname":"D00", "port":"D", "num":"25", "functions":{}, "csv":{} }, { "name":"PD1", "sortingname":"D01", "port":"D", "num":"26", "functions":{}, "csv":{} }, { "name":"PD2", "sortingname":"D02", "port":"D", "num":"27", "functions":{}, "csv":{} }, @@ -117,11 +155,18 @@ def get_pins(): { "name":"PD12", "sortingname":"D12", "port":"D", "num":"19", "functions":{}, "csv":{} }, { "name":"PD13", "sortingname":"D13", "port":"D", "num":"20", "functions":{}, "csv":{} }, + { "name":"PA0", "sortingname":"A00", "port":"A", "num":"2", "functions":{ "ADC1_IN0":0 }, "csv":{} }, + { "name":"PA1", "sortingname":"A01", "port":"A", "num":"3", "functions":{ "ADC1_IN1":0 }, "csv":{} }, + { "name":"PA2", "sortingname":"A02", "port":"A", "num":"4", "functions":{ "ADC1_IN2":0 }, "csv":{} }, + { "name":"PA3", "sortingname":"A03", "port":"A", "num":"5", "functions":{ "ADC1_IN3":0 }, "csv":{} }, + { "name":"PA4", "sortingname":"A04", "port":"A", "num":"28", "functions":{ "ADC1_IN4":0 }, "csv":{} }, + { "name":"PA5", "sortingname":"A05", "port":"A", "num":"29", "functions":{ "ADC1_IN5":0 }, "csv":{} }, + { "name":"PH0", "sortingname":"H0", "port":"H", "num":"16", "functions":{}, "csv":{} }, # LED - { "name":"PH1", "sortingname":"H1", "port":"H", "num":"24", "functions":{}, "csv":{} }, # BTN1 - { "name":"PH2", "sortingname":"H2", "port":"H", "num":"23", "functions":{}, "csv":{} }, # 2 + { "name":"PH1", "sortingname":"H1", "port":"H", "num":"23", "functions":{}, "csv":{} }, # BTN1 + { "name":"PH2", "sortingname":"H2", "port":"H", "num":"21", "functions":{}, "csv":{} }, # 2 { "name":"PH3", "sortingname":"H3", "port":"H", "num":"22", "functions":{}, "csv":{} }, # 3 - { "name":"PH4", "sortingname":"H4", "port":"H", "num":"21", "functions":{}, "csv":{} }, # 4 + { "name":"PH4", "sortingname":"H4", "port":"H", "num":"24", "functions":{}, "csv":{} }, # 4 { "name":"PH5", "sortingname":"H5", "port":"H", "num":"13", "functions":{}, "csv":{} }, # LCD DC { "name":"PH6", "sortingname":"H6", "port":"H", "num":"12", "functions":{}, "csv":{} }, # LCD CS { "name":"PH7", "sortingname":"H7", "port":"H", "num":"11", "functions":{}, "csv":{} }, # LCD RST diff --git a/boards/PUCKJS.py b/boards/PUCKJS.py index 02fa20705..181f45582 100644 --- a/boards/PUCKJS.py +++ b/boards/PUCKJS.py @@ -16,7 +16,7 @@ import pinutils; info = { - 'name' : "PuckJS", + 'name' : "Puck.js", 'link' : [ "http://www.espruino.com/PuckJS" ], 'default_console' : "EV_SERIAL1", 'default_console_tx' : "D28", @@ -31,10 +31,10 @@ info = { 'BLUETOOTH', 'NET', 'GRAPHICS', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', + 'AES', 'NFC', - 'NEOPIXEL' - #'HASHLIB' + 'NEOPIXEL', #'FILESYSTEM' #'TLS' ], @@ -42,6 +42,7 @@ info = { 'DEFINES+=-DHAL_NFC_ENGINEERING_BC_FTPAN_WORKAROUND=1', # Looks like proper production nRF52s had this issue 'DEFINES+=-DCONFIG_GPIO_AS_PINRESET', # Allow the reset pin to work 'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"Puck.js"\'', + 'DEFINES+=-DNFC_DEFAULT_URL=\'"https://puck-js.com/go"\'', 'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem', 'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C', 'INCLUDE += -I$(ROOT)/libs/puckjs', @@ -58,15 +59,15 @@ chip = { 'flash' : 512, 'speed' : 64, 'usart' : 1, - 'spi' : 3, - 'i2c' : 2, + 'spi' : 1, + 'i2c' : 1, 'adc' : 1, 'dac' : 0, 'saved_code' : { - 'address' : ((118 - 3) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'address' : ((118 - 10) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 'page_size' : 4096, - 'pages' : 3, - 'flash_available' : 512 - ((31 + 8 + 1 + 3)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 1, code 3. Each page is 4 kb. + 'pages' : 10, + 'flash_available' : 512 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. }, }; diff --git a/boards/RAK8211.py b/boards/RAK8211.py new file mode 100644 index 000000000..820b5a960 --- /dev/null +++ b/boards/RAK8211.py @@ -0,0 +1,163 @@ +#!/bin/false +# This file is part of Espruino, a JavaScript interpreter for Microcontrollers +# +# Copyright (C) 2018 Gordon Williams +# +# 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/. +# +# ---------------------------------------------------------------------------------------- +# This file contains information for a specific board - the available pins, and where LEDs, +# Buttons, and other in-built peripherals are. It is used to build documentation as well +# as various source and header files for Espruino. +# ---------------------------------------------------------------------------------------- + +import pinutils; + +# Schematic http://docs.rakwireless.com/en/RAK8211/Hardware%20Design/RAK8211_iTRACKER_M35_V20_20171203.pdf +# GPS HW https://www.quectel.com/UploadImage/Downlad/L70_Hardware_Design_V2.0.pdf +# GPS SW http://docs-europe.electrocomponents.com/webdocs/147d/0900766b8147dbdd.pdf + +info = { + 'name' : "iTracker RAK8211", + #https://github.com/RAKWireless/ITRACKER-Arduino-SDK + 'link' : [ "http://www.rakwireless.com/en/download/Cellular/RAK8211" ], + 'espruino_page_link' : 'RAK8211', + 'default_console' : "EV_SERIAL1", + 'default_console_tx' : "D29", + 'default_console_rx' : "D28", + 'default_console_baudrate' : "9600", + 'variables' : 2500, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile. + 'bootloader' : 1, + 'binary_name' : 'espruino_%v_RAK8211.hex', + 'build' : { + 'optimizeflags' : '-Os', + 'libraries' : [ + 'BLUETOOTH', + 'NET' + ], + 'makefile' : [ + 'DEFINES+=-DCONFIG_GPIO_AS_PINRESET', # Allow the reset pin to work + 'DEFINES+=-DCONFIG_NFCT_PINS_AS_GPIOS', # Don't use NFC - the pins are used for GPS + 'DEFINES+=-DHAL_NFC_ENGINEERING_BC_FTPAN_WORKAROUND=1', # Looks like proper production nRF52s had this issue + 'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"iTracker"\'', + 'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem', + 'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C', + 'JSMODULESOURCES += libs/js/AT.min.js', + 'JSMODULESOURCES += libs/js/GPS.min.js', + 'JSMODULESOURCES += libs/js/BME280.min.js', + 'JSMODULESOURCES += libs/js/LIS2MDL.min.js', + 'JSMODULESOURCES += libs/js/LIS3DH.min.js', + 'JSMODULESOURCES += libs/js/OPT3001.min.js', + 'JSMODULESOURCES += libs/js/ATSMS.min.js', + 'JSMODULESOURCES += libs/js/QuectelM35.min.js', + 'JSMODULESOURCES += iTracker:libs/js/rak/RAK8211.min.js' + ] + } +}; + +chip = { + 'part' : "NRF52832", + 'family' : "NRF52", + 'package' : "QFN48", + 'ram' : 64, + 'flash' : 512, + 'speed' : 64, + 'usart' : 3, + 'spi' : 1, + 'i2c' : 1, + 'adc' : 1, + 'dac' : 0, + 'saved_code' : { + 'address' : ((118 - 10) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'page_size' : 4096, + 'pages' : 10, + 'flash_available' : 512 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. + }, +}; + +devices = { + 'BTN1' : { 'pin' : 'D30', 'pinstate' : 'IN_PULLDOWN' }, + 'GPRS' : {'pin_tx' : 'D12', 'pin_rx' : 'D20', 'pin_reset' : 'D14', 'pin_pwrkey' : 'D15', 'pin_pwron' : 'D6'}, + 'GPS' : {'pin_tx' : 'D9', 'pin_rx' : 'D8', 'pin_standby' : 'D7', 'pin_pwron' : 'D10', 'pin_reset' : 'D31'}, + 'BME' : {'pin_cs' : 'D2', 'pin_sdi' : 'D3', 'pin_sck': 'D4', 'pin_sdo' : 'D5'}, + 'LIS2MDL' : {'pin_scl' : 'D11', 'pin_sda': 'D13', 'pin_int' : 'D16'}, + 'LIS3DH' : {'pin_scl' : 'D18', 'pin_sda' : 'D19', 'pin_int1' : 'D25', 'pin_res' : 'D26', 'pin_int2' : 'D27'}, + 'OPT' : {'pin_sda' : '21', 'pin_scl' : 'D23', 'pin_int' : 'D22'} + # Pin D22 is used for clock when driving neopixels - as not specifying a pin seems to break things +}; + +# left-right, or top-bottom order +board = { + 'right' : [ 'D21', '3V', 'GND', 'D30', '', + 'GND', '3V', 'D29', 'D28', '', + '3V', 'SWDIO', 'SWDCLK', 'GND' ], + '_hide_not_on_connectors' : True, + '_notes' : { + 'D21' : "Also RESET if configured", + 'D30' : "Labelled TILT_DOUT", + 'D29' : "Labelled SENSOR_DOUT2", + 'D28' : "Labelled SENSOR_DOUT1" + } +}; + +board["_css"] = """ +#board { + width: 400px; + height: 506px; + top: 0px; + left : 0px; + background-image: url(img/RAK8211.png); +} +#boardcontainer { + height: 506px; +} +#right { + top: 73px; + left: 405px; +} +.rightpin { + height: 18px; +} +"""; + +def get_pins(): + pins = pinutils.generate_pins(0,31) # 32 General Purpose I/O Pins. + pinutils.findpin(pins, "PD0", True)["functions"]["XL1"]=0; + pinutils.findpin(pins, "PD1", True)["functions"]["XL2"]=0; + pinutils.findpin(pins, "PD28", True)["functions"]["ADC1_IN4"]=0; + pinutils.findpin(pins, "PD28", True)["functions"]["USART1_TX"]=0; + pinutils.findpin(pins, "PD29", True)["functions"]["USART1_RX"]=0; + pinutils.findpin(pins, "PD12", True)["functions"]["GPRS_TX"]=0; + pinutils.findpin(pins, "PD20", True)["functions"]["GPRS_RX"]=0; + pinutils.findpin(pins, "PD14", True)["functions"]["GPRS_RESET"]=0; + pinutils.findpin(pins, "PD15", True)["functions"]["GPRS_PWRKEY"]=0; + pinutils.findpin(pins, "PD6", True)["functions"]["GPRS_PWN_ON"]=0; + pinutils.findpin(pins, "PD9", True)["functions"]["GPS_TX"]=0; + pinutils.findpin(pins, "PD8", True)["functions"]["GPS_RX"]=0; + pinutils.findpin(pins, "PD7", True)["functions"]["GPS_STANDBY"]=0; + pinutils.findpin(pins, "PD10", True)["functions"]["GPS_PWR_ON"]=0; + pinutils.findpin(pins, "PD31", True)["functions"]["GPS_RESET"]=0; + pinutils.findpin(pins, "PD2", True)["functions"]["BME_CS"]=0; + pinutils.findpin(pins, "PD3", True)["functions"]["BME_SDI"]=0; + pinutils.findpin(pins, "PD4", True)["functions"]["BME_SCK"]=0; + pinutils.findpin(pins, "PD5", True)["functions"]["BME_SDO"]=0; + pinutils.findpin(pins, "PD11", True)["functions"]["LIS2MDL_SCL"]=0; + pinutils.findpin(pins, "PD13", True)["functions"]["LIS2MDL_SDA"]=0; + pinutils.findpin(pins, "PD16", True)["functions"]["LIS2MDL_INT"]=0; + pinutils.findpin(pins, "PD18", True)["functions"]["LIS3DH_SCL"]=0; + pinutils.findpin(pins, "PD19", True)["functions"]["LIS3DH_SDA"]=0; + pinutils.findpin(pins, "PD25", True)["functions"]["LIS3DH_INT1"]=0; + pinutils.findpin(pins, "PD27", True)["functions"]["LIS3DH_INT2"]=0; + pinutils.findpin(pins, "PD26", True)["functions"]["OPT_SDA"]=0; + pinutils.findpin(pins, "PD23", True)["functions"]["OPT_SCL"]=0; + pinutils.findpin(pins, "PD22", True)["functions"]["OPT_INT"]=0; + + + # everything is non-5v tolerant + for pin in pins: + pin["functions"]["3.3"]=0; + + #The boot/reset button will function as a reset button in normal operation. Pin reset on PD21 needs to be enabled on the nRF52832 device for this to work. + return pins diff --git a/boards/RAK8212.py b/boards/RAK8212.py new file mode 100644 index 000000000..3505f5dc8 --- /dev/null +++ b/boards/RAK8212.py @@ -0,0 +1,163 @@ +#!/bin/false +# This file is part of Espruino, a JavaScript interpreter for Microcontrollers +# +# Copyright (C) 2018 Gordon Williams +# +# 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/. +# +# ---------------------------------------------------------------------------------------- +# This file contains information for a specific board - the available pins, and where LEDs, +# Buttons, and other in-built peripherals are. It is used to build documentation as well +# as various source and header files for Espruino. +# ---------------------------------------------------------------------------------------- + +import pinutils; + +# Schematic http://docs.rakwireless.com/en/Cellular/RAK8212/Hardware%20Specification/RAK8212_iTRACKER_BG96_V30_20180322.pdf + +info = { + 'name' : "iTracker RAK8212", + #https://github.com/RAKWireless/ITRACKER-Arduino-SDK + 'link' : [ "http://www.rakwireless.com/en/download/Cellular/RAK8212" ], + 'espruino_page_link' : 'RAK8212', + 'default_console' : "EV_SERIAL1", + 'default_console_tx' : "D29", + 'default_console_rx' : "D28", + 'default_console_baudrate' : "9600", + 'variables' : 2500, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile. + 'bootloader' : 1, + 'binary_name' : 'espruino_%v_RAK8212.hex', + 'build' : { + 'optimizeflags' : '-Os', + 'libraries' : [ + 'BLUETOOTH', + 'NET' + ], + 'makefile' : [ + 'DEFINES+=-DCONFIG_GPIO_AS_PINRESET', # Allow the reset pin to work + 'DEFINES+=-DCONFIG_NFCT_PINS_AS_GPIOS', # Don't use NFC - the pins are used for GPS + 'DEFINES+=-DHAL_NFC_ENGINEERING_BC_FTPAN_WORKAROUND=1', # Looks like proper production nRF52s had this issue + 'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"iTracker"\'', + 'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem', + 'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C', + 'JSMODULESOURCES += libs/js/AT.min.js', + 'JSMODULESOURCES += libs/js/GPS.min.js', + 'JSMODULESOURCES += libs/js/BME280.min.js', + 'JSMODULESOURCES += libs/js/LIS2MDL.min.js', + 'JSMODULESOURCES += libs/js/LIS3DH.min.js', + 'JSMODULESOURCES += libs/js/OPT3001.min.js', + 'JSMODULESOURCES += libs/js/ATSMS.min.js', + 'JSMODULESOURCES += libs/js/QuectelBG96.min.js', + 'JSMODULESOURCES += iTracker:libs/js/rak/RAK8212.min.js' + ] + } +}; + +chip = { + 'part' : "NRF52832", + 'family' : "NRF52", + 'package' : "QFN48", + 'ram' : 64, + 'flash' : 512, + 'speed' : 64, + 'usart' : 3, + 'spi' : 1, + 'i2c' : 1, + 'adc' : 1, + 'dac' : 0, + 'saved_code' : { + 'address' : ((118 - 10) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'page_size' : 4096, + 'pages' : 10, + 'flash_available' : 512 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. + }, +}; + +devices = { + 'LED1' : { 'pin' : 'D12' }, # Pin negated in software + 'BTN1' : { 'pin' : 'D30', 'pinstate' : 'IN_PULLDOWN' }, + 'GPRS' : {'pin_tx' : 'D12', 'pin_rx' : 'D20', 'pin_reset' : 'D14', 'pin_pwrkey' : 'D15', 'pin_pwron' : 'D6'}, + 'GPS' : {'pin_tx' : 'D9', 'pin_rx' : 'D8', 'pin_standby' : 'D7', 'pin_pwron' : 'D10', 'pin_reset' : 'D31'}, + 'BME' : {'pin_cs' : 'D2', 'pin_sdi' : 'D3', 'pin_sck': 'D4', 'pin_sdo' : 'D5'}, + 'LIS2MDL' : {'pin_scl' : 'D11', 'pin_sda': 'D13', 'pin_int' : 'D16'}, + 'LIS3DH' : {'pin_scl' : 'D18', 'pin_sda' : 'D19', 'pin_int1' : 'D25', 'pin_res' : 'D26', 'pin_int2' : 'D27'}, + 'OPT' : {'pin_sda' : '21', 'pin_scl' : 'D23', 'pin_int' : 'D22'}, + # Pin D22 is used for clock when driving neopixels - as not specifying a pin seems to break things +}; + +# left-right, or top-bottom order +board = { + 'right' : [ 'D21', '3V', 'GND', 'D30', '', + 'GND', '3V', 'D29', 'D28', '', + '3V', 'SWDIO', 'SWDCLK', 'GND' ], + '_hide_not_on_connectors' : True, + '_notes' : { + 'D21' : "Also RESET if configured", + 'D30' : "Labelled TILT_DOUT", + 'D29' : "Labelled SENSOR_DOUT2", + 'D28' : "Labelled SENSOR_DOUT1" + } +}; + +board["_css"] = """ +#board { + width: 480px; + height: 526px; + top: 0px; + left : 0px; + background-image: url(img/RAK8212.png); +} +#boardcontainer { + height: 506px; +} +#right { + top: 59px; + left: 480px; +} +.rightpin { + height: 26px; +} +"""; + +def get_pins(): + pins = pinutils.generate_pins(0,31) # 32 General Purpose I/O Pins. + # Software negation LED1 + pinutils.findpin(pins, "PD12", True)["functions"]["NEGATED"]=0; + # Add pin functions + pinutils.findpin(pins, "PD0", True)["functions"]["XL1"]=0; + pinutils.findpin(pins, "PD1", True)["functions"]["XL2"]=0; + pinutils.findpin(pins, "PD28", True)["functions"]["ADC1_IN4"]=0; + pinutils.findpin(pins, "PD7", True)["functions"]["LTE_RX"]=0; + pinutils.findpin(pins, "PD8", True)["functions"]["LTE_CTS"]=0; + pinutils.findpin(pins, "PD9", True)["functions"]["LTE_TX"]=0; + pinutils.findpin(pins, "PD10", True)["functions"]["LTE_RTS"]=0; + pinutils.findpin(pins, "PD28", True)["functions"]["USART1_TX"]=0; + pinutils.findpin(pins, "PD29", True)["functions"]["USART1_RX"]=0; + pinutils.findpin(pins, "PD14", True)["functions"]["GPRS_RESET"]=0; + pinutils.findpin(pins, "PD15", True)["functions"]["GPRS_PWRKEY"]=0; + pinutils.findpin(pins, "PD6", True)["functions"]["GPRS_PWN_ON"]=0; + pinutils.findpin(pins, "PD31", True)["functions"]["GPS_RESET"]=0; + pinutils.findpin(pins, "PD2", True)["functions"]["BME_CS"]=0; + pinutils.findpin(pins, "PD3", True)["functions"]["BME_SDI"]=0; + pinutils.findpin(pins, "PD4", True)["functions"]["BME_SCK"]=0; + pinutils.findpin(pins, "PD5", True)["functions"]["BME_SDO"]=0; + pinutils.findpin(pins, "PD11", True)["functions"]["LIS2MDL_SCL"]=0; + pinutils.findpin(pins, "PD13", True)["functions"]["LIS2MDL_SDA"]=0; + pinutils.findpin(pins, "PD16", True)["functions"]["LIS2MDL_INT"]=0; + pinutils.findpin(pins, "PD18", True)["functions"]["LIS3DH_SCL"]=0; + pinutils.findpin(pins, "PD19", True)["functions"]["LIS3DH_SDA"]=0; + pinutils.findpin(pins, "PD25", True)["functions"]["LIS3DH_INT1"]=0; + pinutils.findpin(pins, "PD27", True)["functions"]["LIS3DH_INT2"]=0; + pinutils.findpin(pins, "PD26", True)["functions"]["OPT_SDA"]=0; + pinutils.findpin(pins, "PD23", True)["functions"]["OPT_SCL"]=0; + pinutils.findpin(pins, "PD22", True)["functions"]["OPT_INT"]=0; + + + # everything is non-5v tolerant + for pin in pins: + pin["functions"]["3.3"]=0; + + #The boot/reset button will function as a reset button in normal operation. Pin reset on PD21 needs to be enabled on the nRF52832 device for this to work. + return pins diff --git a/boards/RASPBERRYPI.py b/boards/RASPBERRYPI.py index 686a07a54..dbf00012c 100644 --- a/boards/RASPBERRYPI.py +++ b/boards/RASPBERRYPI.py @@ -25,9 +25,8 @@ info = { 'NET', 'GRAPHICS', 'FILESYSTEM', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', - 'HASHLIB', 'TELNET', ], 'makefile' : [ diff --git a/boards/RUUVITAG.py b/boards/RUUVITAG.py index f188fcc22..8db77a8b8 100644 --- a/boards/RUUVITAG.py +++ b/boards/RUUVITAG.py @@ -32,10 +32,9 @@ info = { 'BLUETOOTH', 'NET', 'GRAPHICS', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'NFC', 'NEOPIXEL' - #'HASHLIB' #'FILESYSTEM' #'TLS' ], @@ -57,22 +56,22 @@ chip = { 'flash' : 512, 'speed' : 64, 'usart' : 1, - 'spi' : 3, - 'i2c' : 2, + 'spi' : 1, + 'i2c' : 1, 'adc' : 1, 'dac' : 0, 'saved_code' : { - 'address' : ((115 - 3) * 4096), # Bootloader takes pages 117-127 on RuuviTag, FS takes 115-116 + 'address' : ((115 - 10) * 4096), # Bootloader takes pages 117-127 on RuuviTag, FS takes 115-116 'page_size' : 4096, - 'pages' : 3, - 'flash_available' : 512 - ((31 + 11 + 1 + 3)*4) # Softdevice uses 31 pages of flash, bootloader 11, code 3. Each page is 4 kb. + 'pages' : 10, + 'flash_available' : 512 - ((31 + 11 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 11, fs 2, code 10. Each page is 4 kb. }, }; devices = { - 'LED1' : { 'pin' : 'D17', 'inverted' : True }, - 'LED2' : { 'pin' : 'D19', 'inverted' : True }, - 'BTN1' : { 'pin' : 'D13', 'inverted' : True, 'pinstate' : 'IN_PULLUP' }, + 'LED1' : { 'pin' : 'D17' }, # Pin negated in software (see below) + 'LED2' : { 'pin' : 'D19' }, # Pin negated in software (see below) + 'BTN1' : { 'pin' : 'D13', 'pinstate' : 'IN_PULLDOWN' }, # Pin negated in software (see below) 'CSBME' : { 'pin' : 'D3', 'inverted' : True, 'pinstate' : 'IN_PULLUP' }, 'CSLIS' : { 'pin' : 'D8', 'inverted' : True, 'pinstate' : 'IN_PULLUP' }, 'RX_PIN_NUMBER' : { 'pin' : 'D5'}, @@ -117,5 +116,11 @@ def get_pins(): pinutils.findpin(pins, "PD29", True)["functions"]["ADC1_IN5"]=0; pinutils.findpin(pins, "PD30", True)["functions"]["ADC1_IN6"]=0; pinutils.findpin(pins, "PD31", True)["functions"]["ADC1_IN7"]=0; - #The boot/reset button will function as a reset button in normal operation. Pin reset on PD21 needs to be enabled on the nRF52832 device for this to work. + # The boot/reset button will function as a reset button in normal operation. Pin reset on PD21 needs to be enabled on the nRF52832 device for this to work. + + # Make buttons and LEDs negated + pinutils.findpin(pins, "PD17", True)["functions"]["NEGATED"]=0; + pinutils.findpin(pins, "PD19", True)["functions"]["NEGATED"]=0; + pinutils.findpin(pins, "PD13", True)["functions"]["NEGATED"]=0; + return pins diff --git a/boards/SMARTIBOT.py b/boards/SMARTIBOT.py new file mode 100644 index 000000000..510d24f50 --- /dev/null +++ b/boards/SMARTIBOT.py @@ -0,0 +1,115 @@ +#!/bin/false +# This file is part of Espruino, a JavaScript interpreter for Microcontrollers +# +# Copyright (C) 2013 Gordon Williams +# +# 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/. +# +# ---------------------------------------------------------------------------------------- +# This file contains information for a specific board - the available pins, and where LEDs, +# Buttons, and other in-built peripherals are. It is used to build documentation as well +# as various source and header files for Espruino. +# ---------------------------------------------------------------------------------------- + +import pinutils; + +info = { + 'name' : "Smartibot", + 'link' : [ "http://thecraftyrobot.net" ], + 'espruino_page_link' : 'Smartibot', + 'bootloader' : 1, + 'default_console' : "EV_SERIAL1", + 'default_console_tx' : "D22", + 'default_console_rx' : "D23", + 'default_console_baudrate' : "9600", + 'variables' : 2250, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile. + 'binary_name' : 'espruino_%v_smartibot.hex', + 'build' : { + 'optimizeflags' : '-Os', + 'libraries' : [ + 'BLUETOOTH', + 'GRAPHICS', + 'NEOPIXEL' + ], + 'makefile' : [ + 'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem', + 'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C', + 'DEFINES += -DCONFIG_GPIO_AS_PINRESET', # Allow the reset pin to work + 'DEFINES += -DCONFIG_NFCT_PINS_AS_GPIOS=1', # Use NFC for GPIOs +# 'JSMODULESOURCES+=libs/js/PCA9685.min.js', +# 'JSMODULESOURCES+=libs/js/Smartibot.min.js' + ] + } +}; + + +chip = { + 'part' : "NRF52832", + 'family' : "NRF52", + 'package' : "QFN48", + 'ram' : 64, + 'flash' : 512, + 'speed' : 64, + 'usart' : 1, + 'spi' : 1, + 'i2c' : 1, + 'adc' : 1, + 'dac' : 0, + 'saved_code' : { + 'address' : ((118 - 10) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'page_size' : 4096, + 'pages' : 10, + 'flash_available' : 512 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. + }, +}; + +devices = { + 'BTN1' : { 'pin' : 'D29', 'pinstate' : 'IN_PULLUP' }, # Pin negated in software + 'BTN2' : { 'pin' : 'D13', 'pinstate' : 'IN_PULLUP' }, # Pin negated in software + 'LED1' : { 'pin' : 'D20' }, + 'RX_PIN_NUMBER' : { 'pin' : 'D23'}, + 'TX_PIN_NUMBER' : { 'pin' : 'D22'}, +}; + +# left-right, or top-bottom order +board = { + '_notes' : { + 'D23' : "Serial console RX", + 'D22' : "Serial console TX" + } +}; +board["_css"] = """ +#board { + width: 600px; + height: 595px; + background-image: url(img/SMARTIBOT.jpg); +} +"""; + +def get_pins(): + pins = pinutils.generate_pins(0,31) # 32 General Purpose I/O Pins. + pinutils.findpin(pins, "PD0", True)["functions"]["XL1"]=0; + pinutils.findpin(pins, "PD1", True)["functions"]["XL2"]=0; + pinutils.findpin(pins, "PD5", True)["functions"]["RTS"]=0; + pinutils.findpin(pins, "PD22", True)["functions"]["TXD"]=0; + pinutils.findpin(pins, "PD7", True)["functions"]["CTS"]=0; + pinutils.findpin(pins, "PD23", True)["functions"]["RXD"]=0; + pinutils.findpin(pins, "PD2", True)["functions"]["ADC1_IN0"]=0; + pinutils.findpin(pins, "PD3", True)["functions"]["ADC1_IN1"]=0; + pinutils.findpin(pins, "PD4", True)["functions"]["ADC1_IN2"]=0; + pinutils.findpin(pins, "PD5", True)["functions"]["ADC1_IN3"]=0; + pinutils.findpin(pins, "PD28", True)["functions"]["ADC1_IN4"]=0; + pinutils.findpin(pins, "PD29", True)["functions"]["ADC1_IN5"]=0; + pinutils.findpin(pins, "PD30", True)["functions"]["ADC1_IN6"]=0; + pinutils.findpin(pins, "PD31", True)["functions"]["ADC1_IN7"]=0; + # Make BTN1 + BTN2 negated + pinutils.findpin(pins, "PD29", True)["functions"]["NEGATED"]=0; # BTN1 + pinutils.findpin(pins, "PD13", True)["functions"]["NEGATED"]=0; # BTN2 + + # everything is non-5v tolerant + for pin in pins: + pin["functions"]["3.3"]=0; + #The boot/reset button will function as a reset button in normal operation. Pin reset on PD21 needs to be enabled on the nRF52832 device for this to work. + return pins diff --git a/boards/STM32F3DISCOVERY.py b/boards/STM32F3DISCOVERY.py index d3da12f54..4794739e7 100644 --- a/boards/STM32F3DISCOVERY.py +++ b/boards/STM32F3DISCOVERY.py @@ -134,6 +134,15 @@ board["_css"] = """ top: 320px; left: 530px; } +/* prevent pins overlay legends from overlapping when hovering */ +#boardcontainer:hover #left { opacity: 0.1; } +#boardcontainer:hover #left2 { opacity: 0.1; } +#boardcontainer:hover #right { opacity: 0.1; } +#boardcontainer:hover #right2 { opacity: 0.1; } +#boardcontainer:hover #left:hover { opacity: 1; z-index: 10; } +#boardcontainer:hover #left2:hover { opacity: 1; z-index: 10; } +#boardcontainer:hover #right2:hover { opacity: 1; z-index: 10; } +#boardcontainer:hover #right:hover { opacity: 1; z-index: 10; } """; def get_pins(): diff --git a/boards/STM32L496GDISCOVERY.py b/boards/STM32L496GDISCOVERY.py index 9011e65ff..2d0bb5b56 100644 --- a/boards/STM32L496GDISCOVERY.py +++ b/boards/STM32L496GDISCOVERY.py @@ -18,10 +18,12 @@ import pinutils; info = { 'name' : "STM32L496 Discovery", - 'link' : [ "www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-eval-tools/mcu-eval-tools/stm32-mcu-eval-tools/stm32-mcu-discovery-kits/32l496gdiscovery.html" ], - 'default_console' : "EV_USBSERIAL", - - 'variables' : 15359, # variables computed from available RAM size : (256-16)*1024/16-1 + 'link' : [ "http://www.st.com/en/evaluation-tools/32l496gdiscovery.html" ], + 'espruino_page_link' : 'STM32L496GDISCOVERY', + 'default_console' : "EV_SERIAL2", + 'default_console_tx' : "A2", + 'default_console_rx' : "D6", + 'variables' : 19200, # variables computed from available RAM size : (256-16)*1024/16-1 'binary_name' : 'espruino_%v_stm32l496gdiscovery.bin', 'build' : { 'optimizeflags' : '-O3', @@ -29,7 +31,7 @@ info = { 'NET', 'GRAPHICS', 'NEOPIXEL', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS', 'FILESYSTEM' ], @@ -59,7 +61,7 @@ chip = { # so we have some left room for Espruino firmware and no risk to clear it while saving 'address' : 0x080D0000, # We have 0 -> 0xFFFFF (1MB), so this is at the end of flash 'page_size' : 2048, # size of pages, 256 pages on bank1, 256 pages on bank2 - 'pages' : 64, # count of pages we're using to save RAM to Flash + 'pages' : 64, # count of pages we're using to save RAM to Flash 'flash_available' : 832 # kb - quantity reserved to receive the Firmware }, }; @@ -103,49 +105,56 @@ board = { 'top2' : [ 'G11', 'B6', 'G10', 'G12', 'GND','5V', 'B8', 'I3', 'D3', 'B7' ], 'top' : [ 'H2', 'B2', 'A4', 'A0', '5V','GND', 'C7', 'C2', 'B12', 'C2' ], # PMOD+ connector - 'right' : [ 'G11','B6','G10','G12','GND','3V3' ], - 'right2' : [ 'H2','B2','NC','NC','GND','3V3' ], + 'right' : [ 'H2','B2','NC','NC','GND','3V3' ], + 'right2' : [ 'G11','B6','G10','G12','GND','3V3' ], # verso side - # ARDUIMO connector - 'bottom' : [ 'C4','C1','C3','F10','C1','C4','','VIN','GND','5V','3V3','RESET','VDD (IOREF)','' ], - 'bottom2' : [ 'G8','G7','G13','H15','I11','B9','I6','G6', '', 'G15','H13','A15','B5','B4','A5','GND','AVDD','B7','B8' ], + # ARDUINO connector + 'bottom2' : [ 'C4','C1','C3','F10','C1','C4','','VIN','GND','5V','3V3','RESET','VDD (IOREF)','' ], + 'bottom' : [ 'G8','G7','G13','H15','I11','B9','I6','G6', '', 'G15','H13','A15','B5','B4','A5','GND','AVDD','B7','B8' ], }; board["_css"] = """ #board { - width: 1203px; - height: 1500px; - top: 0px; - background-image: url(img/DISCOL496G.jpg); + width: 800px; + height: 864px; + top: 250px; + background-image: url(img/STM32L496GDISCOVERY.jpg); } #boardcontainer { - height: 1500px; + height: 1450px; } #top { - top: 100px; - left: 100px; + bottom: 843px; + left: 518px; } #top2 { - top: 100px; - left: 500px; + top: 20px; + left: 518px; } #right { - top: 50px; - left: 1000px; + top: 55px; + left: 782px; } #right2 { - top: 50px; - left: 1400px + top: 55px; + right: 20px } #bottom { - top: 500px; - right: 100px; + top: 796px; + right: 221px; } #bottom2 { - top: 500px; - right: 500px; + bottom: 360px; + right: 283px; } + +.toppin { width: 15px; padding:0px; } +.top2pin { width: 15px; padding:0px; } +.rightpin { height: 15px; } +.right2pin { height: 15px; } +.bottompin { width: 15px; padding:0px; } +.bottom2pin { width: 15px; padding:0px; } """; def get_pins(): diff --git a/boards/STM32L496STEMCELL.py b/boards/STM32L496STEMCELL.py index 895c780ea..7690febcf 100644 --- a/boards/STM32L496STEMCELL.py +++ b/boards/STM32L496STEMCELL.py @@ -29,7 +29,7 @@ info = { 'NET', 'GRAPHICS', 'NEOPIXEL', - 'CRYPTO', + 'CRYPTO','SHA256','SHA512', 'TLS' ], 'makefile' : [ diff --git a/boards/STM32VLDISCOVERY.py b/boards/STM32VLDISCOVERY.py index 8c342a412..15f075607 100644 --- a/boards/STM32VLDISCOVERY.py +++ b/boards/STM32VLDISCOVERY.py @@ -17,7 +17,7 @@ import pinutils; info = { 'name' : "STM32 VL Discovery", 'link' : [ "http://www.st.com/stm32-discovery" ], - 'variables' : 500, + 'variables' : 400, 'binary_name' : 'espruino_%v_stm32vldiscovery.bin', 'build' : { 'optimizeflags' : '-Os', diff --git a/boards/THINGY52.py b/boards/THINGY52.py index c6ba4d0fa..9b717a0a0 100644 --- a/boards/THINGY52.py +++ b/boards/THINGY52.py @@ -18,34 +18,39 @@ import pinutils; info = { 'name' : "Nordic Thingy:52", 'link' : [ "https://www.nordicsemi.com/eng/Products/Nordic-Thingy-52" ], + 'espruino_page_link' : 'Thingy52', 'default_console' : "EV_SERIAL1", 'default_console_tx' : "D2", 'default_console_rx' : "D3", 'default_console_baudrate' : "9600", 'variables' : 2500, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile. -# 'bootloader' : 1, - 'binary_name' : 'espruino_%v_thingy52.bin', + 'bootloader' : 1, + 'binary_name' : 'espruino_%v_thingy52.hex', 'build' : { 'optimizeflags' : '-Os', 'libraries' : [ 'BLUETOOTH', # 'NET', 'GRAPHICS', - 'NFC' + 'NFC', + 'NEOPIXEL' ], 'makefile' : [ 'DEFINES+=-DHAL_NFC_ENGINEERING_BC_FTPAN_WORKAROUND=1', # Looks like proper production nRF52s had this issue 'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"Thingy"\'', + 'DEFINES+=-DNFC_DEFAULT_URL=\'"https://www.espruino.com/ide"\'', + 'DEFINES+=-DDUMP_IGNORE_VARIABLES=\'"Thingy\\0"\'', 'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem', 'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C', 'INCLUDE += -I$(ROOT)/libs/nordic_thingy', 'WRAPPERSOURCES += libs/nordic_thingy/jswrap_thingy.c', - 'JSMODULESOURCES+=libs/nordic_thingy/LIS2DH12.min.js', - 'JSMODULESOURCES+=libs/nordic_thingy/LPS22HB.min.js', - 'JSMODULESOURCES+=libs/nordic_thingy/HTS221.min.js', - 'JSMODULESOURCES+=libs/nordic_thingy/CCS811.min.js', - 'JSMODULESOURCES+=libs/nordic_thingy/BH1745.min.js', - 'JSMODULESOURCES+=libs/nordic_thingy/Thingy.min.js' + 'JSMODULESOURCES+=libs/js/LIS2DH12.min.js', + 'JSMODULESOURCES+=libs/js/MPU9250.min.js', + 'JSMODULESOURCES+=libs/js/LPS22HB.min.js', + 'JSMODULESOURCES+=libs/js/HTS221.min.js', + 'JSMODULESOURCES+=libs/js/CCS811.min.js', + 'JSMODULESOURCES+=libs/js/BH1745.min.js', + 'JSMODULESOURCES+=libs/js/nordic/Thingy.min.js' ] } }; @@ -59,15 +64,15 @@ chip = { 'flash' : 512, 'speed' : 64, 'usart' : 1, - 'spi' : 3, - 'i2c' : 2, + 'spi' : 1, + 'i2c' : 1, 'adc' : 1, 'dac' : 0, 'saved_code' : { - 'address' : ((118 - 3) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 + 'address' : ((118 - 10) * 4096), # Bootloader takes pages 120-127, FS takes 118-119 'page_size' : 4096, - 'pages' : 3, - 'flash_available' : 512 - ((31 + 8 + 1 + 3)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 1, code 3. Each page is 4 kb. + 'pages' : 10, + 'flash_available' : 512 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb. }, }; @@ -163,7 +168,7 @@ def get_pins(): pinutils.findpin(pins, "PV5", True)["functions"]["NEGATED"]=0; pinutils.findpin(pins, "PV6", True)["functions"]["NEGATED"]=0; pinutils.findpin(pins, "PV7", True)["functions"]["NEGATED"]=0; - + # everything is non-5v tolerant for pin in pins: diff --git a/boards/WIO_LTE.py b/boards/WIO_LTE.py index 9185746dd..4a6847b3b 100644 --- a/boards/WIO_LTE.py +++ b/boards/WIO_LTE.py @@ -17,7 +17,8 @@ import pinutils; info = { 'name' : "WIO TRACKER LTE", - 'link' : [ "http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1199/PF252419" ], + 'link' : [ "http://www.espruino.com/WioLTE" ], + 'espruino_page_link' : 'WioLTE', 'default_console' : "EV_SERIAL2", # FIXME: This was S2 because of pin conflict. Not sure if it's really an issue? 'variables' : 5450, 'binary_name' : 'espruino_%v_Wio_LTE.bin', @@ -93,24 +94,24 @@ board = { }; board["_css"] = """ #board { - width: 457px; - height: 480px; + width: 500px; + height: 588px; left: 200px; background-image: url(img/WIO_LTE.jpg); } #boardcontainer { - height: 468px; + height: 588px; } #left { - top: 98px; - right: 352px; + top: 123px; + right: 412px; } #right { - top: 98px; - left: 352px; + top: 123px; + left: 412px; } -.leftpin { height: 18px; padding:0px; } -.rightpin { height: 18px; padding:0px; } +.leftpin { height: 23px; padding:0px; } +.rightpin { height: 23px; padding:0px; } """; def get_pins(): diff --git a/boards/img/DISCOL496G.jpg b/boards/img/DISCOL496G.jpg deleted file mode 100644 index 1f5f39b76..000000000 Binary files a/boards/img/DISCOL496G.jpg and /dev/null differ diff --git a/boards/img/MDBT42Q.jpg b/boards/img/MDBT42Q.jpg new file mode 100644 index 000000000..39c616873 Binary files /dev/null and b/boards/img/MDBT42Q.jpg differ diff --git a/boards/img/MDBT42Q_BREAKOUT.png b/boards/img/MDBT42Q_BREAKOUT.png new file mode 100644 index 000000000..b176a71a2 Binary files /dev/null and b/boards/img/MDBT42Q_BREAKOUT.png differ diff --git a/boards/img/NRF52832DK.jpg b/boards/img/NRF52832DK.jpg new file mode 100644 index 000000000..50bd1fd65 Binary files /dev/null and b/boards/img/NRF52832DK.jpg differ diff --git a/boards/img/PIXLJS.jpg b/boards/img/PIXLJS.jpg new file mode 100644 index 000000000..b77acb291 Binary files /dev/null and b/boards/img/PIXLJS.jpg differ diff --git a/boards/img/PIXLJS.png b/boards/img/PIXLJS.png new file mode 100644 index 000000000..a264c5b03 Binary files /dev/null and b/boards/img/PIXLJS.png differ diff --git a/boards/img/RAK8211.png b/boards/img/RAK8211.png new file mode 100644 index 000000000..363163e5c Binary files /dev/null and b/boards/img/RAK8211.png differ diff --git a/boards/img/RAK8212.png b/boards/img/RAK8212.png new file mode 100644 index 000000000..f46c4a1ae Binary files /dev/null and b/boards/img/RAK8212.png differ diff --git a/boards/img/SMARTIBOT.jpg b/boards/img/SMARTIBOT.jpg new file mode 100644 index 000000000..667eccc4b Binary files /dev/null and b/boards/img/SMARTIBOT.jpg differ diff --git a/boards/img/STM32L496GDISCOVERY.jpg b/boards/img/STM32L496GDISCOVERY.jpg new file mode 100644 index 000000000..e6889aa36 Binary files /dev/null and b/boards/img/STM32L496GDISCOVERY.jpg differ diff --git a/boards/img/THINGY52.jpg b/boards/img/THINGY52.jpg new file mode 100644 index 000000000..0c18f2a00 Binary files /dev/null and b/boards/img/THINGY52.jpg differ diff --git a/boards/img/WIO_LTE.jpg b/boards/img/WIO_LTE.jpg index c1c4680b6..82e5e1586 100644 Binary files a/boards/img/WIO_LTE.jpg and b/boards/img/WIO_LTE.jpg differ diff --git a/dist_readme.txt b/dist_readme.txt index 54f4ce067..f98c28fb0 100644 --- a/dist_readme.txt +++ b/dist_readme.txt @@ -35,6 +35,14 @@ espruino_#v##_puckjs.zip - The firmware image for Espruino Puck.js Devices See http://www.espruino.com/Puck.js#firmware-updates for more information +espruino_#v##_pixljs.zip + - The firmware image for Espruino Pixl.js Devices + See http://www.espruino.com/Pixl.js#firmware-updates for more information + +espruino_#v##_mdbt42q.zip + - The firmware image for Espruino MDBT42Q modules + See http://www.espruino.com/MDBT42Q#firmware-updates for more information + espruino_#v##_hystm32_24_ve.bin - 'HY'STM32F103VET6 ARM with 2.4" LCD display This is available from eBay @@ -43,22 +51,12 @@ espruino_#v##_hystm32_28_rb.bin - 'HY'STM32F103RBT6 ARM with 2.8" LCD display This is available from eBay -espruino_#v##_hystm32_32_vc.bin - - 'HY'STM32F103VCT6 ARM with 3.2" LCD display - This is available from eBay - -espruino_#v##_olimexino_stm32.bin - - You will need to overwrite the Maple bootloader to install this. - Espruino is now too large to fit in flash alongside it. - - Olimexino-STM32 Arduino form factor board - - Leaf Labs Maple Arduino form factor board +espruino_#v##_stm32l496gdiscovery.bin + - STM32F496GDISCOVERY board espruino_#v##_stm32vldiscovery.bin - STM32VLDISCOVERY board -espruino_#v##_stm32f3discovery.bin - - STM32F3DISCOVERY board - espruino_#v##_stm32f4discovery.bin - STM32F4DISCOVERY board @@ -86,6 +84,30 @@ espruino_#v##_Wio_LTE.zip - The firmware image for Seed Wio LTE Devices See http://www.espruino.com/WioLTE for more information +espruino_#v##_thingy52.hex + - The firmware image for Nordic Thing:52 Devices + See http://www.espruino.com/Thingy52 for more information + +espruino_#v##nrf52832.hex + - The firmware image for Nordic Thing:52 Devices + See http://www.espruino.com/nRF52832DK for more information + +espruino_#v##stm32l496gdiscovery.bin + - The firmware image for the STM32L496G Discovery Board + See http://www.espruino.com/STM32L496GDISCOVERY for more information + +espruino_#v##rak8211.hex + - The firmware image for the RAK8211 iTracker + See http://www.espruino.com/RAK8211 for more information + +espruino_#v##rak8212.hex + - The firmware image for the RAK8212 iTracker + See http://www.espruino.com/RAK8212 for more information + +espruino_#v##smartibot.zip + - The firmware image for the Smartibot robot board + See http://www.espruino.com/Smartibot for more information + ESP8266 / ESP32 diff --git a/libs/bluetooth/bluetooth.h b/libs/bluetooth/bluetooth.h index c7bbaddcb..97d3f6154 100644 --- a/libs/bluetooth/bluetooth.h +++ b/libs/bluetooth/bluetooth.h @@ -18,12 +18,18 @@ #include "jsdevices.h" #ifdef NRF5X +#if NRF_SD_BLE_API_VERSION>5 +#include "nrf_sdh_ble.h" +#define BLE_GAP_ADV_MAX_SIZE BLE_GAP_ADV_SET_DATA_SIZE_MAX +#else #include "ble.h" +#endif #include "ble_advdata.h" #else typedef struct { uint16_t uuid; - uint8_t type; + uint8_t type; //see BLE_UUID_TYPE_... definitions + uint8_t uuid128[16]; //BLE knows 16/32/128 bit uuids. Espruino supports 16/128. } PACKED_FLAGS ble_uuid_t; typedef struct { //uint8_t addr_id_peer; @@ -44,10 +50,13 @@ typedef struct { #define BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA 0xFF #define BLE_UUID_TYPE_UNKNOWN (0) #define BLE_UUID_TYPE_BLE (1) +#define BLE_UUID_TYPE_128 2 #define MSEC_TO_UNITS(MS,MEH) MS +#define GATT_MTU_SIZE_DEFAULT 23 +#define BLE_NUS_MAX_DATA_LEN 20 //GATT_MTU_SIZE_DEFAULT - 3 #endif -#ifdef NRF52 +#if defined(NRF52) || defined(ESP32) // nRF52 gets the ability to connect to other #define CENTRAL_LINK_COUNT 1 /**0 extern volatile uint16_t m_central_conn_handle; /**< Handle for central mode connection */ #endif @@ -115,12 +145,12 @@ extern volatile uint16_t m_central_conn_handle; /**< Han void jsble_init(); /** Completely deinitialise the BLE stack */ void jsble_kill(); -/** Add a task to the queue to be executed (to be called mainly from IRQ-land) */ -void jsble_queue_pending(BLEPending blep); -/** Add a task to the queue to be executed (to be called mainly from IRQ-land) */ -void jsble_queue_pending_d(BLEPending blep, uint16_t data); -/** Execute a task that was added by jsble_queue_pending - this is done outside of IRQ land*/ -void jsble_exec_pending(IOEvent *event); +/** Add a task to the queue to be executed (to be called mainly from IRQ-land) - with a buffer of data */ +void jsble_queue_pending_buf(BLEPending blep, uint16_t data, char *ptr, size_t len); +/** Add a task to the queue to be executed (to be called mainly from IRQ-land) - with simple data */ +void jsble_queue_pending(BLEPending blep, uint16_t data); +/** Execute a task that was added by jsble_queue_pending - this is done outside of IRQ land. Returns number of events handled */ +int jsble_exec_pending(IOEvent *event); /** Stop and restart the softdevice so that we can update the services in it - * both user-defined as well as UART/HID */ @@ -137,13 +167,20 @@ bool jsble_has_connection(); bool jsble_has_central_connection(); /** Is BLE connected to a server device at all (eg, the simple, 'slave' mode)? */ -bool jsble_has_simple_connection(); +bool jsble_has_peripheral_connection(); + +/** Call this when something happens on BLE with this as + * a peripheral - used with Dynamic Interval Adjustment */ +void jsble_peripheral_activity(); /// Checks for error and reports an exception if there was one. Return true on error bool jsble_check_error(uint32_t err_code); -/// Scanning for advertisign packets -uint32_t jsble_set_scanning(bool enabled); +/** Set the connection interval of the peripheral connection. Returns an error code */ +uint32_t jsble_set_periph_connection_interval(JsVarFloat min, JsVarFloat max); + +/// Scanning for advertising packets +uint32_t jsble_set_scanning(bool enabled, bool activeScan); /// returning RSSI values for current connection uint32_t jsble_set_rssi_scan(bool enabled); @@ -159,6 +196,9 @@ uint32_t jsble_disconnect(uint16_t conn_handle); /// For BLE HID, send an input report to the receiver. Must be <= HID_KEYS_MAX_LEN void jsble_send_hid_input_report(uint8_t *data, int length); +/// Update the current security settings from the info in hiddenRoot.BLE_NAME_SECURITY +void jsble_update_security(); + // ------------------------------------------------- lower-level utility fns #ifdef NRF5X @@ -199,8 +239,14 @@ void jsble_nfc_send_rsp(const uint8_t data, size_t len); #endif #if CENTRAL_LINK_COUNT>0 -/// Connect to the given peer address. When done call bleCompleteTask -void jsble_central_connect(ble_gap_addr_t peer_addr); +/** Connect to the given peer address. When done call bleCompleteTask. + options is an optional object containing optional fields: + { + minInterval // min connection interval in milliseconds, 7.5 ms to 4 s + maxInterval // max connection interval in milliseconds, 7.5 ms to 4 s + } + See BluetoothRemoteGATTServer.connect docs for more docs */ +void jsble_central_connect(ble_gap_addr_t peer_addr, JsVar *options); /// Get primary services. Filter by UUID unless UUID is invalid, in which case return all. When done call bleCompleteTask void jsble_central_getPrimaryServices(ble_uuid_t uuid); /// Get characteristics. Filter by UUID unless UUID is invalid, in which case return all. When done call bleCompleteTask @@ -219,8 +265,10 @@ void jsble_central_startBonding(bool forceRePair); JsVar *jsble_central_getSecurityStatus(); /// RSSI monitoring in central mode uint32_t jsble_set_central_rssi_scan(bool enabled); -// Set whether or not the whitelist is enabled +/// Set whether or not the whitelist is enabled void jsble_central_setWhitelist(bool whitelist); +/// Send a passkey if one was requested (passkey = 6 bytes long) +uint32_t jsble_central_send_passkey(char *passkey); #endif #endif // BLUETOOTH_H diff --git a/libs/bluetooth/bluetooth_utils.c b/libs/bluetooth/bluetooth_utils.c index d8c2f9d99..0320e2d7d 100644 --- a/libs/bluetooth/bluetooth_utils.c +++ b/libs/bluetooth/bluetooth_utils.c @@ -24,7 +24,20 @@ /// Return true if two UUIDs are equal bool bleUUIDEqual(ble_uuid_t a, ble_uuid_t b) { - return a.type==b.type && a.uuid==b.uuid; +#ifdef NRF5X + return a.type==b.type && a.uuid==b.uuid; +#else + switch(a.type){ + case BLE_UUID_TYPE_UNKNOWN: + return a.type == b.type; + case BLE_UUID_TYPE_BLE: + return a.type == b.type && a.uuid == b.uuid; + case BLE_UUID_TYPE_128: + return a.type == b.type && a.uuid128 == b.uuid128; + default: + return false; + } +#endif } JsVar *bleUUID128ToStr(const uint8_t *data) { @@ -52,8 +65,7 @@ JsVar *bleUUIDToStr(ble_uuid_t uuid) { assert(dataLen==16); // it should always be 16 as we checked above return bleUUID128ToStr(&data[0]); #else - jsiConsolePrintf("FIXME\n"); - return 0; + return bleUUID128ToStr(&uuid.uuid128); #endif } @@ -73,9 +85,9 @@ bool bleVarToAddr(JsVar *mac, ble_gap_addr_t *addr) { for (i=0;i<6;i++) addr->addr[5-i] = (chtod(jsvGetCharInString(mac, i*3))<<4) | chtod(jsvGetCharInString(mac, (i*3)+1)); if (jsvGetStringLength(mac)!=17) { - if (jsvIsStringEqualOrStartsWithOffset(mac, " public", false, 17)) + if (jsvIsStringEqualOrStartsWithOffset(mac, " public", false, 17, false)) addr->addr_type = BLE_GAP_ADDR_TYPE_PUBLIC; // default - else if (jsvIsStringEqualOrStartsWithOffset(mac, " random", false, 17)) + else if (jsvIsStringEqualOrStartsWithOffset(mac, " random", false, 17, false)) addr->addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC; else return false; } @@ -163,7 +175,10 @@ const char *bleVarToUUID(ble_uuid_t *uuid, JsVar *v) { } return err_code ? "BLE device error adding UUID" : 0; #else - jsiConsolePrintf("FIXME\n"); + uuid->uuid = ((data[13]<<8) | data[12]); + for(int i = 0; i < 16; i++){ + uuid->uuid128[i] = data[i]; + } return 0; #endif } diff --git a/libs/bluetooth/bluetooth_utils.h b/libs/bluetooth/bluetooth_utils.h index 3823f1de9..db6dfe092 100644 --- a/libs/bluetooth/bluetooth_utils.h +++ b/libs/bluetooth/bluetooth_utils.h @@ -30,6 +30,7 @@ #define BLE_NAME_NUS "BLE_UART" #define BLE_NAME_FLAGS "BLE_FLAGS" #define BLE_NAME_GATT_SERVER "BLE_GATTS" +#define BLE_NAME_SECURITY "BLE_SEC" typedef enum { BLE_FLAGS_NONE = 0, diff --git a/libs/bluetooth/jswrap_bluetooth.c b/libs/bluetooth/jswrap_bluetooth.c index 243cfe300..6d2e08d92 100644 --- a/libs/bluetooth/jswrap_bluetooth.c +++ b/libs/bluetooth/jswrap_bluetooth.c @@ -29,6 +29,7 @@ #include "nrf5x_utils.h" #include "nordic_common.h" #include "nrf.h" +#include "ble_gap.h" #include "ble_hci.h" #include "ble_advdata.h" #include "ble_conn_params.h" @@ -44,6 +45,12 @@ #endif #endif +#ifdef ESP32 +#include "BLE/esp32_gap_func.h" +#include "BLE/esp32_gatts_func.h" +#include "BLE/esp32_gattc_func.h" +#define BLE_CONN_HANDLE_INVALID -1 +#endif // ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------ @@ -133,24 +140,22 @@ JsVar *bleGetActiveBluetoothGattServer() { /*JSON{ "type" : "init", - "generate" : "jswrap_nrf_init" + "generate" : "jswrap_ble_init" }*/ -void jswrap_nrf_init() { +void jswrap_ble_init() { // Turn off sleeping if it was on before jsiStatus &= ~BLE_IS_SLEEPING; if (jsiStatus & JSIS_COMPLETELY_RESET) { -#ifdef USE_NFC -#ifdef PUCKJS +#if defined(USE_NFC) && defined(NFC_DEFAULT_URL) // By default Puck.js's NFC will send you to the PuckJS website // address is included so Web Bluetooth can connect to the correct one - JsVar *addr = jswrap_nrf_bluetooth_getAddress(); - JsVar *uri = jsvVarPrintf("https://puck-js.com/go?a=%v", addr); + JsVar *addr = jswrap_ble_getAddress(); + JsVar *uri = jsvVarPrintf(NFC_DEFAULT_URL"?a=%v", addr); jsvUnLock(addr); - jswrap_nrf_nfcURL(uri); + jswrap_nfc_URL(uri); jsvUnLock(uri); -#endif #endif } else { #ifdef USE_NFC @@ -166,15 +171,15 @@ void jswrap_nrf_init() { // Set advertising interval back to default bleAdvertisingInterval = DEFAULT_ADVERTISING_INTERVAL; // Now set up whatever advertising we were doing before - jswrap_nrf_reconfigure_softdevice(); + jswrap_ble_reconfigure_softdevice(); } /** Reconfigure the softdevice (on init or after restart) to have all the services/advertising we need */ -void jswrap_nrf_reconfigure_softdevice() { +void jswrap_ble_reconfigure_softdevice() { // restart various JsVar *v,*o; v = jsvObjectGetChild(execInfo.root, BLE_SCAN_EVENT,0); - if (v) jsble_set_scanning(true); + if (v) jsble_set_scanning(true, false); jsvUnLock(v); v = jsvObjectGetChild(execInfo.root, BLE_RSSI_EVENT,0); if (v) jsble_set_rssi_scan(true); @@ -182,7 +187,7 @@ void jswrap_nrf_reconfigure_softdevice() { // advertising v = jsvObjectGetChild(execInfo.hiddenRoot, BLE_NAME_ADVERTISE_DATA, 0); o = jsvObjectGetChild(execInfo.hiddenRoot, BLE_NAME_ADVERTISE_OPTIONS, 0); - if (v || o) jswrap_nrf_bluetooth_setAdvertising(v, o); + if (v || o) jswrap_ble_setAdvertising(v, o); jsvUnLock2(v,o); // services v = jsvObjectGetChild(execInfo.hiddenRoot, BLE_NAME_SERVICE_DATA, 0); @@ -190,23 +195,23 @@ void jswrap_nrf_reconfigure_softdevice() { jsvUnLock(v); // If we had scan response data set, update it JsVar *scanData = jsvObjectGetChild(execInfo.hiddenRoot, BLE_NAME_SCAN_RESPONSE_DATA, 0); - if (scanData) jswrap_nrf_bluetooth_setScanResponse(scanData); + if (scanData) jswrap_ble_setScanResponse(scanData); jsvUnLock(scanData); } /*JSON{ "type" : "idle", - "generate" : "jswrap_nrf_idle" + "generate" : "jswrap_ble_idle" }*/ -bool jswrap_nrf_idle() { +bool jswrap_ble_idle() { return false; } /*JSON{ "type" : "kill", - "generate" : "jswrap_nrf_kill" + "generate" : "jswrap_ble_kill" }*/ -void jswrap_nrf_kill() { +void jswrap_ble_kill() { #ifdef USE_NFC // stop NFC emulation jsble_nfc_stop(); // not a problem to call this if NFC isn't started @@ -218,7 +223,7 @@ void jswrap_nrf_kill() { if (bleTaskInfo) jsvUnLock(bleTaskInfo); bleTaskInfo = 0; // if we were scanning, make sure we stop - jsble_set_scanning(false); + jsble_set_scanning(false, false); jsble_set_rssi_scan(false); #if CENTRAL_LINK_COUNT>0 @@ -229,7 +234,7 @@ void jswrap_nrf_kill() { #endif } -void jswrap_nrf_dumpBluetoothInitialisation(vcbprintf_callback user_callback, void *user_data) { +void jswrap_ble_dumpBluetoothInitialisation(vcbprintf_callback user_callback, void *user_data) { JsVar *v,*o; @@ -264,6 +269,19 @@ void jswrap_nrf_dumpBluetoothInitialisation(vcbprintf_callback user_callback, vo // ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------ +/*JSON{ + "type": "class", + "class" : "NRF" +} +The NRF class is for controlling functionality of the Nordic nRF51/nRF52 chips. + +Most functionality is related to Bluetooth Low Energy, however there are also some functions related to NFC that apply to NRF52-based devices. + +*/ + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ + /*JSON{ "type" : "event", "class" : "NRF", @@ -284,13 +302,23 @@ Called when a host device connects to Espruino. The first argument contains the } Called when a host device disconnects from Espruino. */ - +/*JSON{ + "type" : "event", + "class" : "NRF", + "name" : "HID", + "#if" : "defined(NRF52)" +} +Called with a single byte value when Espruino is set up as +a HID device and the computer it is connected to sends a +HID report back to Espruino. This is usually used for handling +indications such as the Caps Lock LED. + */ /*JSON{ "type" : "event", "class" : "NRF", "name" : "servicesDiscover", - "ifdef" : "NRF52" + "#if" : "defined(NRF52) || defined(ESP32)" } Called with discovered services when discovery is finished */ @@ -298,7 +326,7 @@ Called with discovered services when discovery is finished "type" : "event", "class" : "NRF", "name" : "characteristicsDiscover", - "ifdef" : "NRF52" + "#if" : "defined(NRF52) || defined(ESP32)" } Called with discovered characteristics when discovery is finished */ @@ -362,21 +390,22 @@ NRF.connect("aa:bb:cc:dd:ee:ff").then(function(gatt) { "ifdef" : "NRF52" } Called when a characteristic's value changes, *after* `BluetoothRemoteGATTCharacteristic.startNotifications` has been called. -See that for an example. -The first argument is of the form `{target : BluetoothRemoteGATTCharacteristic}` +``` + ... + return service.getCharacteristic("characteristic_uuid"); +}).then(function(c) { + c.on('characteristicvaluechanged', function(event) { + console.log("-> "+event.target.value); + }); + return c.startNotifications(); +}).then(... +``` -`BluetoothRemoteGATTCharacteristic.value` will then contain the new value. +The first argument is of the form `{target : BluetoothRemoteGATTCharacteristic}`, and `BluetoothRemoteGATTCharacteristic.value` +will then contain the new value (as a DataView). */ -/*JSON{ - "type": "class", - "class" : "NRF" -} -The NRF class is for controlling functionality of the Nordic nRF51/nRF52 chips. Currently these only used in [Puck.js](http://puck-js.com) and the [BBC micro:bit](/MicroBit). - -The main part of this is control of Bluetooth Low Energy - both searching for devices, and changing advertising data. -*/ /*JSON{ "type" : "object", "name" : "Bluetooth", @@ -390,14 +419,14 @@ The Bluetooth Serial port - used when data is sent or received over Bluetooth Sm "type" : "staticmethod", "class" : "NRF", "name" : "disconnect", - "generate" : "jswrap_nrf_bluetooth_disconnect" + "generate" : "jswrap_ble_disconnect" } If a device is connected to Espruino, disconnect from it. */ -void jswrap_nrf_bluetooth_disconnect() { +void jswrap_ble_disconnect() { uint32_t err_code; - if (jsble_has_simple_connection()) { - err_code = jsble_disconnect(m_conn_handle); + if (jsble_has_peripheral_connection()) { + err_code = jsble_disconnect(m_peripheral_conn_handle); jsble_check_error(err_code); } } @@ -406,7 +435,7 @@ void jswrap_nrf_bluetooth_disconnect() { "type" : "staticmethod", "class" : "NRF", "name" : "sleep", - "generate" : "jswrap_nrf_bluetooth_sleep" + "generate" : "jswrap_ble_sleep" } Disable Bluetooth advertising and disconnect from any device that connected to Puck.js as a peripheral (this won't affect any devices @@ -416,28 +445,28 @@ This makes Puck.js undiscoverable, so it can't be connected to. Use `NRF.wake()` to wake up and make Puck.js connectable again. */ -void jswrap_nrf_bluetooth_sleep() { +void jswrap_ble_sleep() { // set as sleeping bleStatus |= BLE_IS_SLEEPING; // stop advertising jsble_advertising_stop(); // If connected, disconnect. // when we disconnect, we'll see BLE_IS_SLEEPING and won't advertise - jswrap_nrf_bluetooth_disconnect(); + jswrap_ble_disconnect(); } /*JSON{ "type" : "staticmethod", "class" : "NRF", "name" : "wake", - "generate" : "jswrap_nrf_bluetooth_wake" + "generate" : "jswrap_ble_wake" } Enable Bluetooth advertising (this is enabled by default), which allows other devices to discover and connect to Puck.js. Use `NRF.sleep()` to disable advertising. */ -void jswrap_nrf_bluetooth_wake() { +void jswrap_ble_wake() { bleStatus &= ~BLE_IS_SLEEPING; jsble_advertising_start(); } @@ -446,7 +475,7 @@ void jswrap_nrf_bluetooth_wake() { "type" : "staticmethod", "class" : "NRF", "name" : "restart", - "generate" : "jswrap_nrf_bluetooth_restart" + "generate" : "jswrap_ble_restart" } Restart the Bluetooth softdevice (if there is currently a BLE connection, it will queue a restart to be done when the connection closes). @@ -456,7 +485,7 @@ BLE softdevice has some settings that cannot be reset. For example there are only a certain number of unique UUIDs. Once these are all used the only option is to restart the softdevice to clear them all out. */ -void jswrap_nrf_bluetooth_restart() { +void jswrap_ble_restart() { if (jsble_has_connection()) { jsiConsolePrintf("BLE Connected, queueing BLE restart for later\n"); bleStatus |= BLE_NEEDS_SOFTDEVICE_RESTART; @@ -472,15 +501,15 @@ void jswrap_nrf_bluetooth_restart() { "type" : "staticmethod", "class" : "NRF", "name" : "getAddress", - "generate" : "jswrap_nrf_bluetooth_getAddress", + "generate" : "jswrap_ble_getAddress", "return" : ["JsVar", "MAC address - a string of the form 'aa:bb:cc:dd:ee:ff'" ] } -Get this device's Bluetooth MAC address. +Get this device's default Bluetooth MAC address. For Puck.js, the last 5 characters of this (eg. `ee:ff`) are used in the device's advertised Bluetooth name. */ -JsVar *jswrap_nrf_bluetooth_getAddress() { +JsVar *jswrap_ble_getAddress() { #ifdef NRF5X uint32_t addr0 = NRF_FICR->DEVICEADDR[0]; uint32_t addr1 = NRF_FICR->DEVICEADDR[1]; @@ -497,11 +526,50 @@ JsVar *jswrap_nrf_bluetooth_getAddress() { ((addr0 )&0xFF)); } +/*JSON{ + "type" : "staticmethod", + "class" : "NRF", + "name" : "setAddress", + "#if" : "defined(NRF52)", + "generate" : "jswrap_ble_setAddress", + "params" : [ + ["addr","JsVar","The address to use (as a string)"] + ] +} +Set this device's default Bluetooth MAC address: + +``` +NRF.setAddress("ff:ee:dd:cc:bb:aa random"); +``` + +Addresses take the form: + +* `"ff:ee:dd:cc:bb:aa"` or `"ff:ee:dd:cc:bb:aa public"` for a public address +* `"ff:ee:dd:cc:bb:aa random"` for a random static address (the default for Espruino) + +This may throw a `INVALID_BLE_ADDR` error if the upper two bits +of the address don't match the address type. + +*/ +void jswrap_ble_setAddress(JsVar *address) { +#ifdef NRF52 + ble_gap_addr_t p_addr; + if (!bleVarToAddr(address, &p_addr)) { + jsExceptionHere(JSET_ERROR, "Expecting a mac address of the form aa:bb:cc:dd:ee:ff"); + return; + } + uint32_t err_code = sd_ble_gap_addr_set(&p_addr); + jsble_check_error(err_code); +#else + jsExceptionHere(JSET_ERROR, "Not implemented"); +#endif +} + /*JSON{ "type" : "staticmethod", "class" : "NRF", "name" : "getBattery", - "generate" : "jswrap_nrf_bluetooth_getBattery", + "generate" : "jswrap_ble_getBattery", "return" : ["float", "Battery level in volts" ] } Get the battery level in volts (the voltage that the NRF chip is running off of). @@ -509,7 +577,7 @@ Get the battery level in volts (the voltage that the NRF chip is running off of) This is the battery level of the device itself - it has nothing to with any device that might be connected. */ -JsVarFloat jswrap_nrf_bluetooth_getBattery() { +JsVarFloat jswrap_ble_getBattery() { return jshReadVRef(); } @@ -517,7 +585,7 @@ JsVarFloat jswrap_nrf_bluetooth_getBattery() { "type" : "staticmethod", "class" : "NRF", "name" : "setAdvertising", - "generate" : "jswrap_nrf_bluetooth_setAdvertising", + "generate" : "jswrap_ble_setAdvertising", "params" : [ ["data","JsVar","The data to advertise as an object - see below for more info"], ["options","JsVar","An optional object of options"] @@ -573,15 +641,15 @@ you want to advertise more data, consider using an array for `data` (See below), `NRF.setScanResponse`. You can even specify an array of arrays or objects, in which case each advertising packet -will be used in turn - for instance to make your device advertise -both Eddystone and iBeacon: +will be used in turn - for instance to make your device advertise battery level and its name +as well as both Eddystone and iBeacon : ``` NRF.setAdvertising([ {0x180F : [Puck.getBatteryPercentage()]}, // normal advertising, with battery % require("ble_ibeacon").get(...), // iBeacon require("ble_eddystone").get(...), // eddystone -],{interval:500}); +], {interval:300}); ``` `options` is an object, which can contain: @@ -592,7 +660,7 @@ NRF.setAdvertising([ showName: true/false // include full name, or nothing discoverable: true/false // general discoverable, or limited - default is limited connectable: true/false // whether device is connectable - default is true - interval: 600 // Advertising interval in msec, between 20 and 10000 + interval: 600 // Advertising interval in msec, between 20 and 10000 (default is 375ms) manufacturer: 0x0590 // IF sending manufacturer data, this is the manufacturer ID manufacturerData: [...] // IF sending manufacturer data, this is an array of data } @@ -608,9 +676,32 @@ NRF.setAdvertising({},{name:"Hello"}); You can also specify 'manufacturer data', which is another form of advertising data. We've registered the Manufacturer ID 0x0590 (as Pur3 Ltd) for use with *Official Espruino devices* - use it to advertise whatever data you'd like, but we'd recommend -using JSON. +using JSON. + +For example by not advertising a device name you can send up to 24 bytes of JSON on +Espruino's manufacturer ID: + +``` +var data = {a:1,b:2}; +NRF.setAdvertising({},{ + showName:false, + manufacturer:0x0590, + manufacturerData:JSON.stringify(data) +}); +``` + +If you're using [EspruinoHub](https://github.com/espruino/EspruinoHub) then it will +automatically decode this into the folling MQTT topics: + +* `/ble/advertise/ma:c_:_a:dd:re:ss/espruino` -> `{"a":10,"b":15}` +* `/ble/advertise/ma:c_:_a:dd:re:ss/a` -> `1` +* `/ble/advertise/ma:c_:_a:dd:re:ss/b` -> `2` + +Note that **you only have 24 characters available for JSON**, so try to use +the shortest field names possible and avoid floating point values that can +be very long when converted to a String. */ -void jswrap_nrf_bluetooth_setAdvertising(JsVar *data, JsVar *options) { +void jswrap_ble_setAdvertising(JsVar *data, JsVar *options) { uint32_t err_code; bool bleChanged = false; bool isAdvertising = bleStatus & BLE_IS_ADVERTISING; @@ -646,9 +737,12 @@ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data, JsVar *options) { err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)namePtr, nameLen); -#else - err_code = 0xDEAD; - jsiConsolePrintf("FIXME\n"); +//#else +// err_code = 0xDEAD; +// jsiConsolePrintf("FIXME\n"); +#endif +#ifdef ESP32 + bluetooth_setDeviceName(v); #endif jsble_check_error(err_code); bleChanged = true; @@ -665,7 +759,7 @@ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data, JsVar *options) { if (jsvIsObject(data) || jsvIsUndefined(data)) { // if it's an object, work out what the advertising data for it is - advArray = jswrap_nrf_bluetooth_getAdvertisingData(data, options); + advArray = jswrap_ble_getAdvertisingData(data, options); // if undefined, make sure we *save* undefined if (jsvIsUndefined(data)) { initialArray = advArray; @@ -683,7 +777,7 @@ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data, JsVar *options) { while (jsvObjectIteratorHasValue(&it)) { JsVar *v = jsvObjectIteratorGetValue(&it); if (jsvIsObject(v) || jsvIsUndefined(v)) { - JsVar *newv = jswrap_nrf_bluetooth_getAdvertisingData(v, options); + JsVar *newv = jswrap_ble_getAdvertisingData(v, options); jsvObjectIteratorSetValue(&it, newv); jsvUnLock(newv); isNested = true; @@ -729,10 +823,22 @@ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data, JsVar *options) { if (bleChanged && isAdvertising) jsble_advertising_stop(); #ifdef NRF5X + #if NRF_SD_BLE_API_VERSION>5 + ble_gap_adv_data_t d; + memset(&d,0,sizeof(d)); + d.adv_data.p_data = dPtr; + d.adv_data.len = dLen; + // TODO: scan_rsp_data? Does not setting this remove it? +//FIXME err_code = sd_ble_gap_adv_set_configure(mp_adv_handle, &d, NULL); + #else err_code = sd_ble_gap_adv_data_set((uint8_t *)dPtr, dLen, NULL, 0); + #endif #else err_code = 0xDEAD; jsiConsolePrintf("FIXME\n"); +#endif +#ifdef ESP32 + err_code = bluetooth_gap_setAdvertizing(advArray); #endif jsvUnLock(initialArray); jsble_check_error(err_code); @@ -744,7 +850,7 @@ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data, JsVar *options) { "type" : "staticmethod", "class" : "NRF", "name" : "getAdvertisingData", - "generate" : "jswrap_nrf_bluetooth_getAdvertisingData", + "generate" : "jswrap_ble_getAdvertisingData", "params" : [ ["data","JsVar","The data to advertise as an object"], ["options","JsVar","An optional object of options"] @@ -754,8 +860,13 @@ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data, JsVar *options) { This is just like `NRF.setAdvertising`, except instead of advertising the data, it returns the packet that would be advertised as an array. */ -JsVar *jswrap_nrf_bluetooth_getAdvertisingData(JsVar *data, JsVar *options) { +JsVar *jswrap_ble_getAdvertisingData(JsVar *data, JsVar *options) { uint32_t err_code; +#ifdef ESP32 + JsVar *r; + r = bluetooth_gap_getAdvertisingData(data,options); + return r; +#endif #ifdef NRF5X ble_advdata_t advdata; jsble_setup_advdata(&advdata); @@ -781,7 +892,7 @@ JsVar *jswrap_nrf_bluetooth_getAdvertisingData(JsVar *data, JsVar *options) { advdata.p_manuf_specific_data = (ble_advdata_manuf_data_t*)alloca(sizeof(ble_advdata_manuf_data_t)); advdata.p_manuf_specific_data->company_identifier = 0xFFFF; // pre-fill with test manufacturer data advdata.p_manuf_specific_data->data.size = dLen; - advdata.p_manuf_specific_data->data.p_data = dPtr; + advdata.p_manuf_specific_data->data.p_data = (uint8_t*)dPtr; } jsvUnLock(v); } @@ -851,7 +962,7 @@ JsVar *jswrap_nrf_bluetooth_getAdvertisingData(JsVar *data, JsVar *options) { "type" : "staticmethod", "class" : "NRF", "name" : "setScanResponse", - "generate" : "jswrap_nrf_bluetooth_setScanResponse", + "generate" : "jswrap_ble_setScanResponse", "params" : [ ["data","JsVar","The data to for the scan response"] ] @@ -869,8 +980,8 @@ NRF.setScanResponse([0x07, // Length of Data services into the scan response - so you can't use both `advertise` and `NRF.setServices` or one will overwrite the other. */ -void jswrap_nrf_bluetooth_setScanResponse(JsVar *data) { - uint32_t err_code; +void jswrap_ble_setScanResponse(JsVar *data) { + uint32_t err_code = 0; jsvObjectSetOrRemoveChild(execInfo.hiddenRoot, BLE_NAME_SCAN_RESPONSE_DATA, data); @@ -881,7 +992,11 @@ void jswrap_nrf_bluetooth_setScanResponse(JsVar *data) { return; } #ifdef NRF5X +#if NRF_SD_BLE_API_VERSION<5 err_code = sd_ble_gap_adv_data_set(NULL, 0, (uint8_t *)dPtr, dLen); +#else + jsWarn("setScanResponse not working on SDK15\n"); +#endif #else err_code = 0xDEAD; jsiConsolePrintf("FIXME\n"); @@ -896,7 +1011,7 @@ void jswrap_nrf_bluetooth_setScanResponse(JsVar *data) { "type" : "staticmethod", "class" : "NRF", "name" : "setServices", - "generate" : "jswrap_nrf_bluetooth_setServices", + "generate" : "jswrap_ble_setServices", "params" : [ ["data","JsVar","The service (and characteristics) to advertise"], ["options","JsVar","Optional object containing options"] @@ -922,8 +1037,7 @@ NRF.setServices({ ``` Or to allow the 3 LEDs to be controlled by writing numbers 0 to 7 to a -characteristic, you can do the following. `evt.data` is an array of -bytes. +characteristic, you can do the following. `evt.data` is an ArrayBuffer. ``` NRF.setServices({ @@ -953,7 +1067,7 @@ NRF.setServices({ indicate : true, // optional, default is false description: "My Characteristic", // optional, default is null onWrite : function(evt) { // optional - console.log("Got ", evt.data); + console.log("Got ", evt.data); // an ArrayBuffer } } // more characteristics allowed @@ -1005,8 +1119,14 @@ UART, however you then be unable to connect to Puck.js's console via Bluetooth. If you absolutely require two or more 128 bit UUIDs then you will have to specify your own raw advertising data packets with `NRF.setAdvertising` +**Note:** The services on Espruino can only be modified when there is +no device connected to it as it requires a restart of the Bluetooth stack. +**iOS devices will 'cache' the list of services** so apps like +NRF Connect may incorrectly display the old services even after you +have modified them. To fix this, disable and re-enable Bluetooth on your +iOS device, or use an Android device to run NRF Connect. */ -void jswrap_nrf_bluetooth_setServices(JsVar *data, JsVar *options) { +void jswrap_ble_setServices(JsVar *data, JsVar *options) { if (!(jsvIsObject(data) || jsvIsUndefined(data))) { jsExceptionHere(JSET_TYPEERROR, "Expecting object or undefined, got %t", data); return; @@ -1063,7 +1183,7 @@ void jswrap_nrf_bluetooth_setServices(JsVar *data, JsVar *options) { // work out whether to apply changes if (bleStatus & (BLE_SERVICES_WERE_SET|BLE_NEEDS_SOFTDEVICE_RESTART)) { - jswrap_nrf_bluetooth_restart(); + jswrap_ble_restart(); } else { /* otherwise, we can set the services now, since we're only adding * and not changing anything we don't need a restart. */ @@ -1075,7 +1195,7 @@ void jswrap_nrf_bluetooth_setServices(JsVar *data, JsVar *options) { "type" : "staticmethod", "class" : "NRF", "name" : "updateServices", - "generate" : "jswrap_nrf_bluetooth_updateServices", + "generate" : "jswrap_ble_updateServices", "params" : [ ["data","JsVar","The service (and characteristics) to update"] ] @@ -1147,10 +1267,14 @@ otherwise the characteristic will be updated but no notification will be sent. **Note:** See `NRF.setServices` for more information */ -void jswrap_nrf_bluetooth_updateServices(JsVar *data) { +void jswrap_ble_updateServices(JsVar *data) { uint32_t err_code; bool ok = true; +#ifdef NRF5X + jsble_peripheral_activity(); // flag that we've been busy +#endif + if (jsvIsObject(data)) { JsvObjectIterator it; jsvObjectIteratorNew(&it, data); @@ -1198,10 +1322,10 @@ void jswrap_nrf_bluetooth_updateServices(JsVar *data) { gatts_value.len = vLen; gatts_value.offset = 0; gatts_value.p_value = (uint8_t*)vPtr; - err_code = sd_ble_gatts_value_set(m_conn_handle, char_handle, &gatts_value); + err_code = sd_ble_gatts_value_set(m_peripheral_conn_handle, char_handle, &gatts_value); if (jsble_check_error(err_code)) { ok = false; - } if ((notification_requested || indication_requested) && jsble_has_simple_connection()) { + } if ((notification_requested || indication_requested) && jsble_has_peripheral_connection()) { // Notify/indicate connected clients if necessary memset(&hvx_params, 0, sizeof(hvx_params)); uint16_t len = (uint16_t)vLen; @@ -1211,7 +1335,7 @@ void jswrap_nrf_bluetooth_updateServices(JsVar *data) { hvx_params.p_len = &len; hvx_params.p_data = (uint8_t*)vPtr; - err_code = sd_ble_gatts_hvx(m_conn_handle, &hvx_params); + err_code = sd_ble_gatts_hvx(m_peripheral_conn_handle, &hvx_params); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) #if NRF_SD_BLE_API_VERSION<5 @@ -1250,13 +1374,123 @@ void jswrap_nrf_bluetooth_updateServices(JsVar *data) { } } + +/// Filter device based on a list of filters (like .requestDevice. Return true if it matches +bool jswrap_ble_filter_device(JsVar *filters, JsVar *device) { + bool matches = false; + JsvObjectIterator fit; + jsvObjectIteratorNew(&fit, filters); + while (!matches && jsvObjectIteratorHasValue(&fit)) { + JsVar *filter = jsvObjectIteratorGetValue(&fit); + matches = true; + JsVar *v; + if ((v = jsvObjectGetChild(filter, "services", 0))) { + // Find one service in the device's service + JsVar *deviceServices = jsvObjectGetChild(device, "services", 0); + JsvObjectIterator it; + jsvObjectIteratorNew(&it, v); + while (jsvObjectIteratorHasValue(&it)) { + bool foundService = false; + if (deviceServices) { + JsVar *uservice = jsvObjectIteratorGetValue(&it); + ble_uuid_t userviceUuid; + bleVarToUUIDAndUnLock(&userviceUuid, uservice); + JsvObjectIterator dit; + jsvObjectIteratorNew(&dit, deviceServices); + while (jsvObjectIteratorHasValue(&dit)) { + JsVar *deviceService = jsvObjectIteratorGetValue(&dit); + ble_uuid_t deviceServiceUuid; + bleVarToUUIDAndUnLock(&deviceServiceUuid, deviceService); + if (bleUUIDEqual(userviceUuid, deviceServiceUuid)) + foundService = true; + jsvObjectIteratorNext(&dit); + } + jsvObjectIteratorFree(&dit); + } + if (!foundService) matches = false; + jsvObjectIteratorNext(&it); + } + jsvObjectIteratorFree(&it); + jsvUnLock2(v, deviceServices); + } + if ((v = jsvObjectGetChild(filter, "name", 0))) { + // match name exactly + JsVar *deviceName = jsvObjectGetChild(device, "name", 0); + if (!jsvIsEqual(v, deviceName)) + matches = false; + jsvUnLock2(v, deviceName); + } + if ((v = jsvObjectGetChild(filter, "namePrefix", 0))) { + // match start of name + JsVar *deviceName = jsvObjectGetChild(device, "name", 0); + if (!jsvIsString(v) || + !jsvIsString(deviceName) || + jsvGetStringLength(v)>jsvGetStringLength(deviceName) || + jsvCompareString(v, deviceName,0,0,true)!=0) + matches = false; + jsvUnLock2(v, deviceName); + } + // Non-standard 'id' element + if ((v = jsvObjectGetChild(filter, "id", 0))) { + JsVar *w = jsvObjectGetChild(device, "id", 0); + if (!jsvIsBasicVarEqual(v,w)) + matches = false; + jsvUnLock2(v,w); + } + // match service data + if ((v = jsvObjectGetChild(filter, "serviceData", 0))) { + if (jsvIsObject(v)) { + JsvObjectIterator it; + jsvObjectIteratorNew(&it,v); + while (jsvObjectIteratorHasValue(&it)) { + JsVar *childName = jsvObjectIteratorGetKey(&it); + JsVar *serviceData = jsvObjectGetChild(device, "serviceData", 0); + if (!serviceData) matches = false; + else { + JsVar *child = jsvFindChildFromVar(serviceData, childName, false); + if (!child) matches = false; + jsvUnLock(child); + } + jsvUnLock2(childName, serviceData); + jsvObjectIteratorNext(&it); + } + jsvObjectIteratorFree(&it); + } + jsvUnLock(v); + } + // match manufacturer data + if ((v = jsvObjectGetChild(filter, "manufacturerData", 0))) { + if (jsvIsObject(v)) { + JsvObjectIterator it; + jsvObjectIteratorNew(&it,v); + while (jsvObjectIteratorHasValue(&it)) { + JsVar* manfacturera = jsvObjectIteratorGetKey(&it); + JsVar* manfacturerb = jsvObjectGetChild(device, "manufacturer", 0); + if (!jsvIsBasicVarEqual(manfacturera, manfacturerb)) + matches = false; + jsvUnLock2(manfacturera, manfacturerb); + jsvObjectIteratorNext(&it); + } + jsvObjectIteratorFree(&it); + } + jsvUnLock(v); + } + // check if all ok + jsvUnLock(filter); + jsvObjectIteratorNext(&fit); + } + jsvObjectIteratorFree(&fit); + return matches; +} + /*JSON{ "type" : "staticmethod", "class" : "NRF", "name" : "setScan", - "generate" : "jswrap_nrf_bluetooth_setScan", + "generate" : "jswrap_ble_setScan", "params" : [ - ["callback","JsVar","The callback to call with received advertising packets, or undefined to stop"] + ["callback","JsVar","The callback to call with received advertising packets, or undefined to stop"], + ["options","JsVar","An optional object `{filters: ...}` (as would be passed to `NRF.requestDevice`) to filter devices by"] ] } @@ -1291,6 +1525,22 @@ BluetoothDevice { } ``` +You can also supply a set of filters as a second argument, which will +allow you to filter the devices you get a callback for. This really helps +to cut down on the time spent processing JavaScript code in areas with +a lot of Bluetooth advertisements. For example to find only devices +with the manufacturer data 0x590 (Espruino's ID) you could do: + +``` +NRF.setScan(function(d) { + console.log(d.manufacturerData); +}, { filters: [{ manufacturerData:{0x0590:{}} }] }); +``` + +You can also specify `active:true` in the second argument to perform +active scanning (this requests scan response packets) from any +devices it finds. + **Note:** BLE advertising packets can arrive quickly - faster than you'll be able to print them to the console. It's best only to print a few, or to use a function like `NRF.findDevices(..)` which will collate a list @@ -1300,7 +1550,7 @@ of available devices. can draw a *lot* of power (12mA or so), so you should use it sparingly or you can run your battery down quickly. */ -void jswrap_nrf_bluetooth_setScan_cb(JsVar *callback, JsVar *adv) { +void jswrap_ble_setScan_cb(JsVar *callback, JsVar *filters, JsVar *adv) { /* This is called when we get data - do some processing here in the main loop then call the callback with it (it avoids us doing more allocations than needed inside the IRQ) */ @@ -1361,25 +1611,42 @@ void jswrap_nrf_bluetooth_setScan_cb(JsVar *callback, JsVar *adv) { if (jsvGetLength(serviceData)) jsvObjectSetChild(device, "serviceData", serviceData); jsvUnLock3(data, services, serviceData); - jspExecuteFunction(callback, 0, 1, &device); + + if (!filters || jswrap_ble_filter_device(filters, device)) + jspExecuteFunction(callback, 0, 1, &device); jsvUnLock(device); } -void jswrap_nrf_bluetooth_setScan(JsVar *callback) { +void jswrap_ble_setScan(JsVar *callback, JsVar *options) { + JsVar *filters = 0; + bool activeScan = false; + if (jsvIsObject(options)) { + activeScan = jsvGetBoolAndUnLock(jsvObjectGetChild(options, "active", 0)); + filters = jsvObjectGetChild(options, "filters", 0); + if (filters && !jsvIsArray(filters)) { + jsvUnLock(filters); + jsExceptionHere(JSET_TYPEERROR, "requestDevice expecting an array of filters, got %t", filters); + return; + } + } else if (options) + jsExceptionHere(JSET_TYPEERROR, "Expecting Object got %t\n", options); // set the callback event variable if (!jsvIsFunction(callback)) callback=0; if (callback) { - JsVar *fn = jsvNewNativeFunction((void (*)(void))jswrap_nrf_bluetooth_setScan_cb, JSWAT_THIS_ARG|(JSWAT_JSVAR< 5 + // TODO: what about BLE_GAP_TX_POWER_ROLE_ADV and BLE_GAP_TX_POWER_ROLE_CONN + err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_SCAN_INIT, 0/*ignored*/, pwr); +#else err_code = sd_ble_gap_tx_power_set(pwr); +#endif #else err_code = 0xDEAD; jsiConsolePrintf("FIXME\n"); @@ -1585,12 +1864,16 @@ void jswrap_nrf_bluetooth_setTxPower(JsVarInt pwr) { "type" : "staticmethod", "class" : "NRF", "name" : "setLowPowerConnection", - "generate" : "jswrap_nrf_bluetooth_setLowPowerConnection", + "generate" : "jswrap_ble_setLowPowerConnection", "params" : [ ["lowPower","bool","Whether the connection is low power or not"] ] } +**THIS IS DEPRECATED** - please use `NRF.setConnectionInterval` for +peripheral and `NRF.connect(addr, options)`/`BluetoothRemoteGATTServer.connect(options)` +for central connections. + This sets the connection parameters - these affect the transfer speed and power usage when the device is connected. @@ -1604,7 +1887,7 @@ decreased. This will only take effect after the connection is disconnected and re-established. */ -void jswrap_nrf_bluetooth_setLowPowerConnection(bool lowPower) { +void jswrap_ble_setLowPowerConnection(bool lowPower) { BLEFlags oldflags = jsvGetIntegerAndUnLock(jsvObjectGetChild(execInfo.hiddenRoot, BLE_NAME_FLAGS, 0)); BLEFlags flags = oldflags; if (lowPower) @@ -1613,7 +1896,7 @@ void jswrap_nrf_bluetooth_setLowPowerConnection(bool lowPower) { flags &= ~BLE_FLAGS_LOW_POWER; if (flags != oldflags) { jsvObjectSetChildAndUnLock(execInfo.hiddenRoot, BLE_NAME_FLAGS, jsvNewFromInteger(flags)); - jswrap_nrf_bluetooth_restart(); + jswrap_ble_restart(); } } @@ -1623,7 +1906,7 @@ void jswrap_nrf_bluetooth_setLowPowerConnection(bool lowPower) { "class" : "NRF", "name" : "nfcURL", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_nfcURL", + "generate" : "jswrap_nfc_URL", "params" : [ ["url","JsVar","The URL string to expose on NFC, or `undefined` to disable NFC"] ] @@ -1633,15 +1916,13 @@ Enables NFC and starts advertising the given URL. For example: ``` NRF.nfcURL("http://espruino.com"); ``` - -**Note:** This is only available on nRF52-based devices */ -void jswrap_nrf_nfcURL(JsVar *url) { +void jswrap_nfc_URL(JsVar *url) { #ifdef USE_NFC // Check for disabling NFC if (jsvIsUndefined(url)) { jsvObjectRemoveChild(execInfo.hiddenRoot, "NfcData"); - jswrap_nrf_nfcStop(); + jswrap_nfc_stop(); return; } @@ -1688,7 +1969,7 @@ void jswrap_nrf_nfcURL(JsVar *url) { flatStrPtr[NDEF_FULL_URL_HEADER_LEN + urlLen] = NDEF_TERM_TLV; /* start nfc peripheral */ - JsVar* uid = jswrap_nrf_nfcStart(NULL); + JsVar* uid = jswrap_nfc_start(NULL); /* inject UID/BCC */ size_t len; @@ -1703,7 +1984,7 @@ void jswrap_nrf_nfcURL(JsVar *url) { "class" : "NRF", "name" : "nfcRaw", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_nfcRaw", + "generate" : "jswrap_nfc_raw", "params" : [ ["payload","JsVar","The NFC NDEF message to deliver to the reader"] ] @@ -1714,15 +1995,13 @@ Enables NFC and starts advertising with Raw data. For example: NRF.nfcRaw(new Uint8Array([193, 1, 0, 0, 0, 13, 85, 3, 101, 115, 112, 114, 117, 105, 110, 111, 46, 99, 111, 109])); // same as NRF.nfcURL("http://espruino.com"); ``` - -**Note:** This is only available on nRF52-based devices */ -void jswrap_nrf_nfcRaw(JsVar *payload) { +void jswrap_nfc_raw(JsVar *payload) { #ifdef USE_NFC // Check for disabling NFC if (jsvIsUndefined(payload)) { jsvObjectRemoveChild(execInfo.hiddenRoot, "NfcData"); - jswrap_nrf_nfcStop(); + jswrap_nfc_stop(); return; } @@ -1751,7 +2030,7 @@ void jswrap_nrf_nfcRaw(JsVar *payload) { flatStrPtr[NDEF_FULL_RAW_HEADER_LEN + dataLen] = NDEF_TERM_TLV; /* start nfc peripheral */ - JsVar* uid = jswrap_nrf_nfcStart(NULL); + JsVar* uid = jswrap_nfc_start(NULL); /* inject UID/BCC */ size_t len; @@ -1767,7 +2046,7 @@ void jswrap_nrf_nfcRaw(JsVar *payload) { "class" : "NRF", "name" : "nfcStart", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_nfcStart", + "generate" : "jswrap_nfc_start", "params" : [ ["payload","JsVar","Optional 7 byte UID"] ], @@ -1781,10 +2060,8 @@ fired when data is received. ``` NRF.nfcStart(); ``` - -**Note:** This is only available on nRF52-based devices */ -JsVar *jswrap_nrf_nfcStart(JsVar *payload) { +JsVar *jswrap_nfc_start(JsVar *payload) { #ifdef USE_NFC /* Turn off NFC */ jsble_nfc_stop(); @@ -1828,6 +2105,8 @@ JsVar *jswrap_nrf_nfcStart(JsVar *payload) { JsVar *arr = jsvNewArrayBufferWithPtr(size, &ptr); if (ptr) jsble_nfc_get_internal((uint8_t *)ptr, &size); return arr; +#else + return 0; #endif } @@ -1836,7 +2115,7 @@ JsVar *jswrap_nrf_nfcStart(JsVar *payload) { "class" : "NRF", "name" : "nfcStop", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_nfcStop", + "generate" : "jswrap_nfc_stop", "params" : [ ] } **Advanced NFC Functionality.** If you just want to advertise a URL, use `NRF.nfcURL` instead. @@ -1846,10 +2125,8 @@ Disables NFC. ``` NRF.nfcStop(); ``` - -**Note:** This is only available on nRF52-based devices */ -void jswrap_nrf_nfcStop() { +void jswrap_nfc_stop() { #ifdef USE_NFC jsvObjectRemoveChild(execInfo.hiddenRoot, "NfcEnabled"); jsble_nfc_stop(); @@ -1862,7 +2139,7 @@ void jswrap_nrf_nfcStop() { "class" : "NRF", "name" : "nfcSend", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_nfcSend", + "generate" : "jswrap_nfc_send", "params" : [ ["payload","JsVar","Optional tx data"] ] @@ -1881,10 +2158,8 @@ NRF.nfcSend(0x0A); // or NRF.nfcSend(); ``` - -**Note:** This is only available on nRF52-based devices */ -void jswrap_nrf_nfcSend(JsVar *payload) { +void jswrap_nfc_send(JsVar *payload) { #ifdef USE_NFC /* Switch to RX */ if (jsvIsUndefined(payload)) @@ -1908,7 +2183,7 @@ void jswrap_nrf_nfcSend(JsVar *payload) { "class" : "NRF", "name" : "sendHIDReport", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_sendHIDReport", + "generate" : "jswrap_ble_sendHIDReport", "params" : [ ["data","JsVar","Input report data as an array"], ["callback","JsVar","A callback function to be called when the data is sent"] @@ -1916,7 +2191,7 @@ void jswrap_nrf_nfcSend(JsVar *payload) { } Send a USB HID report. HID must first be enabled with `NRF.setServices({}, {hid: hid_report})` */ -void jswrap_nrf_sendHIDReport(JsVar *data, JsVar *callback) { +void jswrap_ble_sendHIDReport(JsVar *data, JsVar *callback) { #if BLE_HIDS_ENABLED JSV_GET_AS_CHAR_ARRAY(vPtr, vLen, data) if (vPtr && vLen) { @@ -1934,32 +2209,42 @@ void jswrap_nrf_sendHIDReport(JsVar *data, JsVar *callback) { "type" : "staticmethod", "class" : "NRF", "name" : "requestDevice", - "ifdef" : "NRF52", - "generate" : "jswrap_nrf_bluetooth_requestDevice", + "#if" : "defined(NRF52) || defined(ESP32)", + "generate" : "jswrap_ble_requestDevice", "params" : [ ["options","JsVar","Options used to filter the device to use"] ], - "return" : ["JsVar", "A Promise that is resolved (or rejected) when the connection is complete" ] + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) when the connection is complete" ], + "return_object" : "Promise" } Search for available devices matching the given filters. Since we have no UI here, Espruino will pick the FIRST device it finds, or it'll call `catch`. +`options` can have the following fields: + +* `filters` - a list of filters that a device must match before it is returned (see below) +* `timeout` - the maximum time to scan for in milliseconds (scanning stops when a match +is found. eg. `NRF.requestDevice({ timeout:2000, filters: [ ... ] })` +* `active` - whether to perform active scanning (requesting 'scan response' packets from any +devices that are found). eg. `NRF.requestDevice({ active:true, filters: [ ... ] })` + +**NOTE:** `timeout` and `active` are not part of the Web Bluetooth standard. + The following filter types are implemented: * `services` - list of services as strings (all of which must match). 128 bit services must be in the form '01230123-0123-0123-0123-012301230123' * `name` - exact device name * `namePrefix` - starting characters of device name +* `id` - exact device address (`id:"e9:53:86:09:89:99 random"`) (this is Espruino-specific, and is not part of the Web Bluetooth spec) +* `serviceData` - an object containing service characteristics which must all match (`serviceData:{"1809":{}}`). Matching of actual service data is not supported yet. +* `manufacturerData` - an object containing manufacturer UUIDs which must all match (`manufacturerData:{0x0590:{}}`). Matching of actual manufacturer data is not supported yet. ``` NRF.requestDevice({ filters: [{ namePrefix: 'Puck.js' }] }).then(function(device) { ... }); // or NRF.requestDevice({ filters: [{ services: ['1823'] }] }).then(function(device) { ... }); -``` - -You can also specify a timeout to wait for devices in milliseconds. The default is 2 seconds (2000): - -``` -NRF.requestDevice({ timeout:2000, filters: [ ... ] }) +// or +NRF.requestDevice({ filters: [{ manufacturerData:{0x0590:{}} }] }).then(function(device) { ... }); ``` As a full example, to send data to another Puck.js to turn an LED on: @@ -1993,125 +2278,66 @@ NRF.requestDevice({ filters: [{ namePrefix: 'Puck.js' }]}).then( () => { gatt.disconnect(); console.log("Done!"); } ); ``` -Note that you'll have to keep track of the `gatt` variable so that you can +Note that you have to keep track of the `gatt` variable so that you can disconnect the Bluetooth connection when you're done. - -**Note:** This is only available on some devices */ #if CENTRAL_LINK_COUNT>0 - -JsVar *jswrap_nrf_bluetooth_requestDevice_filter_device(JsVar *filter, JsVar *device) { - bool matches = true; - JsVar *v; - if ((v = jsvObjectGetChild(filter, "services", 0))) { - // Find one service in the device's service - JsVar *deviceServices = jsvObjectGetChild(device, "services", 0); - JsvObjectIterator it; - jsvObjectIteratorNew(&it, v); - while (jsvObjectIteratorHasValue(&it)) { - bool foundService = false; - JsVar *uservice = jsvObjectIteratorGetValue(&it); - ble_uuid_t userviceUuid; - bleVarToUUIDAndUnLock(&userviceUuid, uservice); - JsvObjectIterator dit; - jsvObjectIteratorNew(&dit, deviceServices); - while (jsvObjectIteratorHasValue(&dit)) { - JsVar *deviceService = jsvObjectIteratorGetValue(&dit); - ble_uuid_t deviceServiceUuid; - bleVarToUUIDAndUnLock(&deviceServiceUuid, deviceService); - if (bleUUIDEqual(userviceUuid, deviceServiceUuid)) - foundService = true; - jsvObjectIteratorNext(&dit); - } - jsvObjectIteratorFree(&dit); - if (!foundService) matches = false; - jsvObjectIteratorNext(&it); - } - jsvObjectIteratorFree(&it); - jsvUnLock2(v, deviceServices); - } - if ((v = jsvObjectGetChild(filter, "name", 0))) { - // match name exactly - JsVar *deviceName = jsvObjectGetChild(device, "name", 0); - if (!jsvIsEqual(v, deviceName)) - matches = false; - jsvUnLock2(v, deviceName); - } - if ((v = jsvObjectGetChild(filter, "namePrefix", 0))) { - // match start of name - JsVar *deviceName = jsvObjectGetChild(device, "name", 0); - if (!jsvIsString(v) || - !jsvIsString(deviceName) || - jsvGetStringLength(v)>jsvGetStringLength(deviceName) || - jsvCompareString(v, deviceName,0,0,true)!=0) - matches = false; - jsvUnLock2(v, deviceName); - } - return matches ? jsvLockAgain(device) : 0; -} - -JsVar *jswrap_nrf_bluetooth_requestDevice_filter_devices(JsVar *filter, JsVar *devices) { - JsVar *foundDevice = 0; - JsvObjectIterator dit; - jsvObjectIteratorNew(&dit, devices); - while (!foundDevice && jsvObjectIteratorHasValue(&dit)) { - JsVar *device = jsvObjectIteratorGetValue(&dit); - foundDevice = jswrap_nrf_bluetooth_requestDevice_filter_device(filter, device); - jsvUnLock(device); - jsvObjectIteratorNext(&dit); - } - jsvObjectIteratorFree(&dit); - return foundDevice; -} - -void jswrap_nrf_bluetooth_requestDevice_finish(JsVar *options, JsVar *devices) { +/// Called when we timeout waiting for a device +void jswrap_ble_requestDevice_finish() { if (!bleInTask(BLETASK_REQUEST_DEVICE)) return; - JsVar *foundDevice = 0; - JsVar *filters = jsvObjectGetChild(options, "filters", 0); - if (jsvIsArray(filters)) { - JsvObjectIterator fit; - jsvObjectIteratorNew(&fit, filters); - while (!foundDevice && jsvObjectIteratorHasValue(&fit)) { - JsVar *filter = jsvObjectIteratorGetValue(&fit); - foundDevice = jswrap_nrf_bluetooth_requestDevice_filter_devices(filter, devices); - jsvUnLock(filter); - jsvObjectIteratorNext(&fit); - } - jsvObjectIteratorFree(&fit); - } else { - jsExceptionHere(JSET_TYPEERROR, "requestDevice expecting an array of filters, got %t", filters); - bleCompleteTaskFail(BLETASK_REQUEST_DEVICE, 0); - jsvUnLock(filters); + jswrap_ble_setScan(0,0); // stop scanning + bleCompleteTaskFailAndUnLock(BLETASK_REQUEST_DEVICE, jsvNewFromString("No device found matching filters")); +} + +/// Called when a device is found +void jswrap_ble_requestDevice_scan(JsVar *device) { + if (!bleInTask(BLETASK_REQUEST_DEVICE)) return; - } - jsvUnLock(filters); - if (foundDevice) - bleCompleteTaskSuccessAndUnLock(BLETASK_REQUEST_DEVICE, foundDevice); - else - bleCompleteTaskFailAndUnLock(BLETASK_REQUEST_DEVICE, jsvNewFromString("No device found matching filters")); + // We know the device matches because setScan would have checked for us + jswrap_ble_setScan(0,0); // stop scanning + jswrap_interface_clearTimeout(bleTaskInfo /*the timeout*/); // cancel the timeout + bleCompleteTaskSuccess(BLETASK_REQUEST_DEVICE, device); } #endif -JsVar *jswrap_nrf_bluetooth_requestDevice(JsVar *options) { +JsVar *jswrap_ble_requestDevice(JsVar *options) { #if CENTRAL_LINK_COUNT>0 if (!(jsvIsUndefined(options) || jsvIsObject(options))) { jsExceptionHere(JSET_TYPEERROR, "Expecting an object, for %t", options); return 0; } - JsVar *timeout = jsvObjectGetChild(options, "timeout", 0); + JsVar *filters = jsvObjectGetChild(options, "filters", 0); + if (!jsvIsArray(filters)) { + jsvUnLock(filters); + jsExceptionHere(JSET_TYPEERROR, "requestDevice expecting an array of filters, got %t", filters); + return 0; + } + jsvUnLock(filters); + + JsVarFloat timeout = jsvGetFloatAndUnLock(jsvObjectGetChild(options, "timeout", 0)); + if (isnan(timeout) || timeout<=0) timeout = 2000; + JsVar *promise = 0; - if (bleNewTask(BLETASK_REQUEST_DEVICE, 0)) { - JsVar *fn = jsvNewNativeFunction((void (*)(void))jswrap_nrf_bluetooth_requestDevice_finish, JSWAT_THIS_ARG|(JSWAT_JSVAR<0 JsVar *device = jspNewObject(0, "BluetoothDevice"); if (!device) return 0; jsvObjectSetChild(device, "id", mac); JsVar *gatt = jswrap_BluetoothDevice_gatt(device); + jsvUnLock(device); if (!gatt) return 0; - return jswrap_nrf_BluetoothRemoteGATTServer_connect(gatt); + JsVar *promise = jswrap_ble_BluetoothRemoteGATTServer_connect(gatt, options); + jsvUnLock(gatt); + return promise; #else jsExceptionHere(JSET_ERROR, "Unimplemented"); return 0; @@ -2178,7 +2416,7 @@ JsVar *jswrap_nrf_bluetooth_connect(JsVar *mac) { "class" : "NRF", "name" : "setWhitelist", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_setWhitelist", + "generate" : "jswrap_ble_setWhitelist", "params" : [ ["whitelisting","bool","Are we using a whitelist? (default false)"] ] @@ -2192,30 +2430,142 @@ devices will not be added to the whitelist. **Note:** This is remembered between `reset()`s but isn't remembered after power-on (you'll have to add it to `onInit()`. */ -void jswrap_nrf_setWhitelist(bool whitelist) { +void jswrap_ble_setWhitelist(bool whitelist) { #if PEER_MANAGER_ENABLED jsble_central_setWhitelist(whitelist); #endif } +/*JSON{ + "type" : "staticmethod", + "class" : "NRF", + "name" : "setConnectionInterval", + "ifdef" : "NRF52", + "generate" : "jswrap_ble_setConnectionInterval", + "params" : [ + ["interval","JsVar","The connection interval to use (see below)"] + ] +} +When connected, Bluetooth LE devices communicate at a set interval. +Lowering the interval (eg. more packets/second) means a lower delay when +sending data, higher bandwidth, but also more power consumption. + +By default, when connected as a peripheral Espruino automatically adjusts the +connection interval. When connected it's as fast as possible (7.5ms) but when idle +for over a minute it drops to 200ms. On continued activity (>1 BLE operation) the +interval is raised to 7.5ms again. + +The options for `interval` are: + +* `undefined` / `"auto"` : (default) automatically adjust connection interval +* `100` : set min and max connection interval to the same number (between 7.5ms and 4000ms) +* `{minInterval:20, maxInterval:100}` : set min and max connection interval as a range + +This configuration is not remembered during a `save()` - you will have to +re-set it via `onInit`. + +**Note:** If connecting to another device (as Central), you can use +an extra argument to `NRF.connect` or `BluetoothRemoteGATTServer.connect` +to specify a connection interval. + +**Note:** This overwrites any changes imposed by the deprecated `NRF.setLowPowerConnection` +*/ +void jswrap_ble_setConnectionInterval(JsVar *interval) { +#if NRF52 + if (jsvIsUndefined(interval) || jsvIsStringEqual(interval,"auto")) { + // allow automatic interval setting + bleStatus &= ~BLE_DISABLE_DYNAMIC_INTERVAL; + } else if (jsvIsNumeric(interval)) { + // disable auto interval + bleStatus |= BLE_DISABLE_DYNAMIC_INTERVAL; + JsVarFloat f = jsvGetFloat(interval); + jsble_check_error(jsble_set_periph_connection_interval(f,f)); + } else if (jsvIsObject(interval)) { + // disable auto interval + bleStatus |= BLE_DISABLE_DYNAMIC_INTERVAL; + JsVarFloat min = jsvGetFloatAndUnLock(jsvObjectGetChild(interval,"minInterval",0)); + JsVarFloat max = jsvGetFloatAndUnLock(jsvObjectGetChild(interval,"maxInterval",0)); + jsble_check_error(jsble_set_periph_connection_interval(min, max)); + } +#endif +} + +/*JSON{ + "type" : "staticmethod", + "class" : "NRF", + "name" : "setSecurity", + "ifdef" : "NRF52", + "generate" : "jswrap_ble_setSecurity", + "params" : [ + ["options","JsVar","An object containing security-related options (see below)"] + ] +} +Sets the security options used when connecting/pairing. This applies to both central +*and* peripheral mode. + +``` +NRF.setSecurity({ + display : bool // default false, can this device display a passkey + // - sent via the `BluetoothDevice.passkey` event + keyboard : bool // default false, can this device enter a passkey + // - request sent via the `BluetoothDevice.passkeyRequest` event + bond : bool // default true, Perform bonding + mitm : bool // default false, Man In The Middle protection + lesc : bool // default false, LE Secure Connections + passkey : // default "", or a 6 digit passkey to use +}); +``` + +For instance, to require pairing and to specify a passkey: + +``` +NRF.setSecurity({passkey:"123456", mitm:1, display:1}); +``` +*/ +void jswrap_ble_setSecurity(JsVar *options) { + if (!jsvIsObject(options) && !jsvIsUndefined(options)) + jsExceptionHere(JSET_TYPEERROR, "Expecting an object or undefined, got %t", options); + else { + jsvObjectSetOrRemoveChild(execInfo.hiddenRoot, BLE_NAME_SECURITY, options); + jsble_update_security(); + } +} + /*JSON{ "type" : "class", "class" : "BluetoothDevice", "ifdef" : "NRF52" } -Web Bluetooth-style device - get this using `NRF.requestDevice(address)` +A Web Bluetooth-style device - you can request one using `NRF.requestDevice(address)` + +For example: + +``` +var gatt; +NRF.requestDevice({ filters: [{ name: 'Puck.js abcd' }] }).then(function(device) { + console.log("found device"); + return device.gatt.connect(); +}).then(function(g) { + gatt = g; + console.log("connected"); + return gatt.startBonding(); +}).then(function() { + console.log("bonded", gatt.getSecurityStatus()); + gatt.disconnect(); +}).catch(function(e) { + console.log("ERROR",e); +}); +``` */ /*JSON{ "type" : "property", "class" : "BluetoothDevice", "name" : "gatt", - "ifdef" : "NRF52", + "#if" : "defined(NRF52) || defined(ESP32)", "generate" : "jswrap_BluetoothDevice_gatt", "return" : ["JsVar", "A `BluetoothRemoteGATTServer` for this device" ] } - -**Note:** This is only available on some devices */ JsVar *jswrap_BluetoothDevice_gatt(JsVar *parent) { #if CENTRAL_LINK_COUNT>0 @@ -2232,34 +2582,124 @@ JsVar *jswrap_BluetoothDevice_gatt(JsVar *parent) { return 0; #endif } +/*JSON{ + "type" : "property", + "class" : "BluetoothDevice", + "name" : "rssi", + "ifdef" : "NRF52", + "generate" : false, + "return" : ["bool", "The last received RSSI (signal strength) for this device" ] +} +*//*Documentation only*/ +/*JSON{ + "type" : "event", + "class" : "BluetoothDevice", + "name" : "passkey", + "ifdef" : "NRF52", + "params" : [ + ["passkey","JsVar","A 6 character numeric String to be displayed"] + ] +} +Called when the device pairs and sends a passkey that Espruino should display. + +For this to be used, you'll have to specify that there's a display using `NRF.setSecurity` + +**This is not part of the Web Bluetooth Specification.** It has been added +specifically for Espruino. +*/ +/*JSON{ + "type" : "event", + "class" : "BluetoothDevice", + "name" : "passkeyRequest", + "ifdef" : "NRF52" +} +Called when the device pairs, displays a passkey, and wants Espruino to tell it what the passkey was. + +Respond with `BluetoothDevice.sendPasskey()` with a 6 character string containing only `0..9`. + +For this to be used, you'll have to specify that there's a keyboard using `NRF.setSecurity` + +**This is not part of the Web Bluetooth Specification.** It has been added +specifically for Espruino. +*/ +/*JSON{ + "type" : "method", + "class" : "BluetoothDevice", + "name" : "sendPasskey", + "ifdef" : "NRF52", + "generate" : "jswrap_ble_BluetoothDevice_sendPasskey", + "params" : [ + ["passkey","JsVar","A 6 character numeric String to be returned to the device"] + ] +} +To be used as a response when the event `BluetoothDevice.sendPasskey` has been received. + +**This is not part of the Web Bluetooth Specification.** It has been added +specifically for Espruino. +*/ +#if NRF52 +void jswrap_ble_BluetoothDevice_sendPasskey(JsVar *parent, JsVar *passkeyVar) { + char passkey[BLE_GAP_PASSKEY_LEN+1]; + memset(passkey, 0, sizeof(passkey)); + jsvGetString(passkeyVar, passkey, sizeof(passkey)); + uint32_t err_code = jsble_central_send_passkey(passkey); + jsble_check_error(err_code); +} +#endif /*JSON{ "type" : "method", "class" : "BluetoothRemoteGATTServer", "name" : "connect", - "ifdef" : "NRF52", - "generate" : "jswrap_nrf_BluetoothRemoteGATTServer_connect", - "return" : ["JsVar", "A Promise that is resolved (or rejected) when the connection is complete" ] + "#if" : "defined(NRF52) || defined(ESP32)", + "generate" : "jswrap_ble_BluetoothRemoteGATTServer_connect", + "params" : [ + ["options","JsVar","(Espruino-specific) An object of connection options (see below)"] + ], + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) when the connection is complete" ], + "return_object" : "Promise" } Connect to a BLE device - returns a promise, the argument of which is the `BluetoothRemoteGATTServer` connection. See [`NRF.requestDevice`](/Reference#l_NRF_requestDevice) for usage examples. -**Note:** This is only available on some devices +`options` is an optional object containing: + +``` +{ + minInterval // min connection interval in milliseconds, 7.5 ms to 4 s + maxInterval // max connection interval in milliseconds, 7.5 ms to 4 s +} +``` + +By default the interval is 20-200ms (or 500-1000ms if `NRF.setLowPowerConnection(true)` was called. +During connection Espruino negotiates with the other device to find a common interval that can be +used. + +For instance calling: + +``` +NRF.requestDevice({ filters: [{ namePrefix: 'Pixl.js' }] }).then(function(device) { + return device.gatt.connect({minInterval:7.5, maxInterval:7.5}); +}).then(function(g) { +``` + +will force the connection to use the fastest connection interval possible (as long as the device +at the other end supports it). */ #if CENTRAL_LINK_COUNT>0 -static void _jswrap_nrf_bluetooth_central_connect(JsVar *addr) { +static void _jswrap_ble_central_connect(JsVar *addr, JsVar *options) { // this function gets called on idle - just to make it less // likely we get connected while in the middle of executing stuff ble_gap_addr_t peer_addr; - // this should be ok since we checked in jswrap_nrf_BluetoothRemoteGATTServer_connect + // this should be ok since we checked in jswrap_ble_BluetoothRemoteGATTServer_connect if (!bleVarToAddr(addr, &peer_addr)) return; - jsble_central_connect(peer_addr); + jsble_central_connect(peer_addr, options); } #endif -JsVar *jswrap_nrf_BluetoothRemoteGATTServer_connect(JsVar *parent) { +JsVar *jswrap_ble_BluetoothRemoteGATTServer_connect(JsVar *parent, JsVar *options) { #if CENTRAL_LINK_COUNT>0 JsVar *device = jsvObjectGetChild(parent, "device", 0); @@ -2275,9 +2715,10 @@ JsVar *jswrap_nrf_BluetoothRemoteGATTServer_connect(JsVar *parent) { JsVar *promise = 0; if (bleNewTask(BLETASK_CONNECT, parent/*BluetoothRemoteGATTServer*/)) { - JsVar *fn = jsvNewNativeFunction((void (*)(void))_jswrap_nrf_bluetooth_central_connect, JSWAT_VOID|(JSWAT_JSVAR<0 uint32_t err_code; if (m_central_conn_handle != BLE_CONN_HANDLE_INVALID) { // we have a connection, disconnect + JsVar *promise = 0; + if (bleNewTask(BLETASK_DISCONNECT, parent/*BluetoothRemoteGATTServer*/)) + promise = jsvLockAgainSafe(blePromise); err_code = jsble_disconnect(m_central_conn_handle); jsble_check_error(err_code); + return promise; } else { // no connection - try and cancel the connect attempt (assume we have one) +#ifdef NRF52 err_code = sd_ble_gap_connect_cancel(); +#endif +#ifdef ESP32 + jsWarn("connect cancel not implemented yet\n"); +#endif // maybe we don't, in which case we don't care about the error code + return jswrap_promise_resolve(parent); } #else jsExceptionHere(JSET_ERROR, "Unimplemented"); #endif + return 0; } /*JSON{ @@ -2336,11 +2801,12 @@ void jswrap_BluetoothRemoteGATTServer_disconnect(JsVar *parent) { "class" : "BluetoothRemoteGATTServer", "name" : "startBonding", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_BluetoothRemoteGATTServer_startBonding", + "generate" : "jswrap_ble_BluetoothRemoteGATTServer_startBonding", "params" : [ ["forceRePair","bool","If the device is already bonded, re-pair it"] ], - "return" : ["JsVar", "A Promise that is resolved (or rejected) when the bonding is complete" ] + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) when the bonding is complete" ], + "return_object" : "Promise" } Start negotiating bonding (secure communications) with the connected device, and return a Promise that is completed on success or failure. @@ -2363,11 +2829,9 @@ NRF.requestDevice({ filters: [{ name: 'Puck.js abcd' }] }).then(function(device) ``` **This is not part of the Web Bluetooth Specification.** It has been added -specifically for Puck.js. - -**Note:** This is only available on some devices +specifically for Espruino. */ -JsVar *jswrap_nrf_BluetoothRemoteGATTServer_startBonding(JsVar *parent, bool forceRePair) { +JsVar *jswrap_ble_BluetoothRemoteGATTServer_startBonding(JsVar *parent, bool forceRePair) { #if CENTRAL_LINK_COUNT>0 if (bleNewTask(BLETASK_BONDING, parent/*BluetoothRemoteGATTServer*/)) { JsVar *promise = jsvLockAgainSafe(blePromise); @@ -2387,7 +2851,7 @@ JsVar *jswrap_nrf_BluetoothRemoteGATTServer_startBonding(JsVar *parent, bool for "class" : "BluetoothRemoteGATTServer", "name" : "getSecurityStatus", "ifdef" : "NRF52", - "generate" : "jswrap_nrf_BluetoothRemoteGATTServer_getSecurityStatus", + "generate" : "jswrap_ble_BluetoothRemoteGATTServer_getSecurityStatus", "return" : ["JsVar", "An object" ] } Return an object with information about the security @@ -2408,10 +2872,8 @@ negotiating a secure connection. **This is not part of the Web Bluetooth Specification.** It has been added specifically for Puck.js. - -**Note:** This is only available on some devices */ -JsVar *jswrap_nrf_BluetoothRemoteGATTServer_getSecurityStatus(JsVar *parent) { +JsVar *jswrap_ble_BluetoothRemoteGATTServer_getSecurityStatus(JsVar *parent) { #if CENTRAL_LINK_COUNT>0 return jsble_central_getSecurityStatus(); #else @@ -2426,10 +2888,11 @@ JsVar *jswrap_nrf_BluetoothRemoteGATTServer_getSecurityStatus(JsVar *parent) { "name" : "getPrimaryService", "generate" : "jswrap_BluetoothRemoteGATTServer_getPrimaryService", "params" : [ ["service","JsVar","The service UUID"] ], - "return" : ["JsVar", "A Promise that is resolved (or rejected) when the primary service is found" ], - "ifdef" : "NRF52" + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) when the primary service is found (the argument contains a `BluetoothRemoteGATTService`)" ], + "return_object" : "Promise", + "#if" : "defined(NRF52) || defined(ESP32)" } -**Note:** This is only available on some devices +See `NRF.connect` for usage examples. */ JsVar *jswrap_BluetoothRemoteGATTServer_getPrimaryService(JsVar *parent, JsVar *service) { #if CENTRAL_LINK_COUNT>0 @@ -2458,10 +2921,10 @@ JsVar *jswrap_BluetoothRemoteGATTServer_getPrimaryService(JsVar *parent, JsVar * "class" : "BluetoothRemoteGATTServer", "name" : "getPrimaryServices", "generate" : "jswrap_BluetoothRemoteGATTServer_getPrimaryServices", - "return" : ["JsVar", "A Promise that is resolved (or rejected) when the primary services are found" ], - "ifdef" : "NRF52" + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) when the primary services are found (the argument contains an array of `BluetoothRemoteGATTService`)" ], + "return_object" : "Promise", + "#if" : "defined(NRF52) || defined(ESP32)" } -**Note:** This is only available on some devices */ JsVar *jswrap_BluetoothRemoteGATTServer_getPrimaryServices(JsVar *parent) { #if CENTRAL_LINK_COUNT>0 @@ -2487,7 +2950,7 @@ JsVar *jswrap_BluetoothRemoteGATTServer_getPrimaryServices(JsVar *parent) { "params" : [ ["callback","JsVar","The callback to call with the RSSI value, or undefined to stop"] ], - "ifdef" : "NRF52" + "#if" : "defined(NRF52) || defined(ESP32)" } Start/stop listening for RSSI values on the active GATT connection @@ -2503,7 +2966,6 @@ gattServer.setRSSIHandler(); RSSI is the 'Received Signal Strength Indication' in dBm -**Note:** This is only available on some devices */ void jswrap_BluetoothRemoteGATTServer_setRSSIHandler(JsVar *parent, JsVar *callback) { #if CENTRAL_LINK_COUNT>0 @@ -2522,7 +2984,7 @@ void jswrap_BluetoothRemoteGATTServer_setRSSIHandler(JsVar *parent, JsVar *callb /*JSON{ "type" : "class", "class" : "BluetoothRemoteGATTService", - "ifdef" : "NRF52" + "#if" : "defined(NRF52) || defined(ESP32)" } Web Bluetooth-style GATT service - get this using `BluetoothRemoteGATTServer.getPrimaryService(s)` @@ -2534,10 +2996,11 @@ https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice "name" : "getCharacteristic", "generate" : "jswrap_BluetoothRemoteGATTService_getCharacteristic", "params" : [ ["characteristic","JsVar","The characteristic UUID"] ], - "return" : ["JsVar", "A Promise that is resolved (or rejected) when the characteristic is found" ], - "ifdef" : "NRF52" + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) when the characteristic is found (the argument contains a `BluetoothRemoteGATTCharacteristic`)" ], + "return_object" : "Promise", + "#if" : "defined(NRF52) || defined(ESP32)" } -**Note:** This is only available on some devices +See `NRF.connect` for usage examples. */ JsVar *jswrap_BluetoothRemoteGATTService_getCharacteristic(JsVar *parent, JsVar *characteristic) { #if CENTRAL_LINK_COUNT>0 @@ -2566,10 +3029,10 @@ JsVar *jswrap_BluetoothRemoteGATTService_getCharacteristic(JsVar *parent, JsVar "class" : "BluetoothRemoteGATTService", "name" : "getCharacteristics", "generate" : "jswrap_BluetoothRemoteGATTService_getCharacteristics", - "return" : ["JsVar", "A Promise that is resolved (or rejected) when the characteristic is found" ], - "ifdef" : "NRF52" + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) when the characteristic is found (the argument contains an array of `BluetoothRemoteGATTCharacteristic`)" ], + "return_object" : "Promise", + "#if" : "defined(NRF52) || defined(ESP32)" } -**Note:** This is only available on some devices */ JsVar *jswrap_BluetoothRemoteGATTService_getCharacteristics(JsVar *parent) { #if CENTRAL_LINK_COUNT>0 @@ -2591,7 +3054,7 @@ JsVar *jswrap_BluetoothRemoteGATTService_getCharacteristics(JsVar *parent) { /*JSON{ "type" : "class", "class" : "BluetoothRemoteGATTCharacteristic", - "ifdef" : "NRF52" + "#if" : "defined(NRF52) || defined(ESP32)" } Web Bluetooth-style GATT characteristic - get this using `BluetoothRemoteGATTService.getCharacteristic(s)` @@ -2601,11 +3064,13 @@ https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattcharacteristi "type" : "method", "class" : "BluetoothRemoteGATTCharacteristic", "name" : "writeValue", - "generate" : "jswrap_nrf_BluetoothRemoteGATTCharacteristic_writeValue", + "generate" : "jswrap_ble_BluetoothRemoteGATTCharacteristic_writeValue", "params" : [ ["data","JsVar","The data to write"] ], - "return" : ["JsVar", "A Promise that is resolved (or rejected) when the characteristic is written" ] + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) when the characteristic is written" ], + "return_object" : "Promise", + "#if" : "defined(NRF52) || defined(ESP32)" } Write a characteristic's value @@ -2626,10 +3091,8 @@ NRF.connect(device_address).then(function(d) { console.log("Something's broken."); }); ``` - -**Note:** This is only available on some devices */ -JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_writeValue(JsVar *characteristic, JsVar *data) { +JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_writeValue(JsVar *characteristic, JsVar *data) { #if CENTRAL_LINK_COUNT>0 JSV_GET_AS_CHAR_ARRAY(dataPtr, dataLen, data); if (!dataPtr) return 0; @@ -2649,11 +3112,13 @@ JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_writeValue(JsVar *characteri "type" : "method", "class" : "BluetoothRemoteGATTCharacteristic", "name" : "readValue", - "generate" : "jswrap_nrf_BluetoothRemoteGATTCharacteristic_readValue", - "return" : ["JsVar", "A Promise that is resolved (or rejected) with a DataView when the characteristic is read" ] + "generate" : "jswrap_ble_BluetoothRemoteGATTCharacteristic_readValue", + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) with a `DataView` when the characteristic is read" ], + "return_object" : "Promise", + "#if" : "defined(NRF52) || defined(ESP32)" } -Read a characteristic's value, return a promise containing a DataView +Read a characteristic's value, return a promise containing a `DataView` ``` var device; @@ -2672,10 +3137,8 @@ NRF.connect(device_address).then(function(d) { console.log("Something's broken."); }); ``` - -**Note:** This is only available on some devices */ -JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_readValue(JsVar *characteristic) { +JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_readValue(JsVar *characteristic) { #if CENTRAL_LINK_COUNT>0 if (!bleNewTask(BLETASK_CHARACTERISTIC_READ, characteristic)) return 0; @@ -2693,10 +3156,13 @@ JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_readValue(JsVar *characteris "type" : "method", "class" : "BluetoothRemoteGATTCharacteristic", "name" : "startNotifications", - "generate" : "jswrap_nrf_BluetoothRemoteGATTCharacteristic_startNotifications", - "return" : ["JsVar", "A Promise that is resolved (or rejected) with data when notifications have been added" ] + "generate" : "jswrap_ble_BluetoothRemoteGATTCharacteristic_startNotifications", + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) with data when notifications have been added" ], + "return_object" : "Promise", + "ifdef" : "NRF52" } -Starts notifications - whenever this characteristic's value changes, a `characteristicvaluechanged` event is fired. +Starts notifications - whenever this characteristic's value changes, a `characteristicvaluechanged` event is fired +and `characteristic.value` will then contain the new value as a `DataView`. ``` var device; @@ -2712,7 +3178,7 @@ NRF.connect(device_address).then(function(d) { }); return c.startNotifications(); }).then(function(d) { - console.log("Waiting for notifications")); + console.log("Waiting for notifications"); }).catch(function() { console.log("Something's broken."); }); @@ -2737,10 +3203,8 @@ NRF.connect("pu:ck:js:ad:dr:es random").then(function(g) { console.log("Done!"); }); ``` - -**Note:** This is only available on some devices */ -JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_startNotifications(JsVar *characteristic) { +JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_startNotifications(JsVar *characteristic) { #if CENTRAL_LINK_COUNT>0 // Set our characteristic's handle up in the list of handles to notify for @@ -2779,12 +3243,14 @@ JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_startNotifications(JsVar *ch "type" : "method", "class" : "BluetoothRemoteGATTCharacteristic", "name" : "stopNotifications", - "generate" : "jswrap_nrf_BluetoothRemoteGATTCharacteristic_stopNotifications", - "return" : ["JsVar", "A Promise that is resolved (or rejected) with data when notifications have been removed" ] + "generate" : "jswrap_ble_BluetoothRemoteGATTCharacteristic_stopNotifications", + "return" : ["JsVar", "A `Promise` that is resolved (or rejected) with data when notifications have been removed" ], + "return_object" : "Promise", + "ifdef" : "NRF52" } -**Note:** This is only available on some devices +Stop notifications (that were requested with `BluetoothRemoteGATTCharacteristic.startNotifications`) */ -JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_stopNotifications(JsVar *characteristic) { +JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_stopNotifications(JsVar *characteristic) { #if CENTRAL_LINK_COUNT>0 // Remove our characteristic handle from the list of handles to notify for uint16_t handle = (uint16_t)jsvGetIntegerAndUnLock(jsvObjectGetChild(characteristic, "handle_value", 0)); diff --git a/libs/bluetooth/jswrap_bluetooth.h b/libs/bluetooth/jswrap_bluetooth.h index de1e98132..65c7b6e3d 100644 --- a/libs/bluetooth/jswrap_bluetooth.h +++ b/libs/bluetooth/jswrap_bluetooth.h @@ -19,6 +19,7 @@ typedef enum { BLETASK_REQUEST_DEVICE, ///< Waiting for requestDevice to finish BLETASK_CENTRAL_START, // =========================================== Start of central tasks BLETASK_CONNECT = BLETASK_CENTRAL_START, ///< Connect in central mode + BLETASK_DISCONNECT, ///< Disconnect from Central BLETASK_PRIMARYSERVICE, ///< Find primary service BLETASK_CHARACTERISTIC, ///< Find characteristics BLETASK_CHARACTERISTIC_WRITE, ///< Write to a characteristic @@ -33,6 +34,7 @@ typedef enum { #define BLETASK_IS_CENTRAL(x) ((x)>=BLETASK_CENTRAL_START && ((x)<=BLETASK_CENTRAL_END)) extern JsVar *bleTaskInfo; // info related to the current task + bool bleInTask(BleTask task); BleTask bleGetCurrentTask(); bool bleNewTask(BleTask task, JsVar *taskInfo); @@ -50,56 +52,60 @@ JsVar *bleGetActiveBluetoothGattServer(); #endif // ------------------------------------------------------------------------------ -void jswrap_nrf_init(); -bool jswrap_nrf_idle(); -void jswrap_nrf_kill(); +void jswrap_ble_init(); +bool jswrap_ble_idle(); +void jswrap_ble_kill(); // Used to dump bluetooth initialisation info for 'dump' -void jswrap_nrf_dumpBluetoothInitialisation(vcbprintf_callback user_callback, void *user_data); +void jswrap_ble_dumpBluetoothInitialisation(vcbprintf_callback user_callback, void *user_data); /** Reconfigure the softdevice (on init or after restart) to have all the services/advertising we need */ -void jswrap_nrf_reconfigure_softdevice(); +void jswrap_ble_reconfigure_softdevice(); // ------------------------------------------------------------------------------ -void jswrap_nrf_bluetooth_disconnect(); -void jswrap_nrf_bluetooth_sleep(); -void jswrap_nrf_bluetooth_wake(); -void jswrap_nrf_bluetooth_restart(); -JsVar *jswrap_nrf_bluetooth_getAddress(); +void jswrap_ble_disconnect(); +void jswrap_ble_sleep(); +void jswrap_ble_wake(); +void jswrap_ble_restart(); +JsVar *jswrap_ble_getAddress(); +void jswrap_ble_setAddress(JsVar *address); -JsVarFloat jswrap_nrf_bluetooth_getBattery(); -void jswrap_nrf_bluetooth_setAdvertising(JsVar *data, JsVar *options); -JsVar *jswrap_nrf_bluetooth_getAdvertisingData(JsVar *data, JsVar *options); -void jswrap_nrf_bluetooth_setScanResponse(JsVar *data); -void jswrap_nrf_bluetooth_setServices(JsVar *data, JsVar *options); -void jswrap_nrf_bluetooth_updateServices(JsVar *data); -void jswrap_nrf_bluetooth_setScan(JsVar *callback); -void jswrap_nrf_bluetooth_findDevices(JsVar *callback, JsVar *timeout); -void jswrap_nrf_bluetooth_setRSSIHandler(JsVar *callback); -void jswrap_nrf_bluetooth_setTxPower(JsVarInt pwr); -void jswrap_nrf_bluetooth_setLowPowerConnection(bool lowPower); +JsVarFloat jswrap_ble_getBattery(); +void jswrap_ble_setAdvertising(JsVar *data, JsVar *options); +JsVar *jswrap_ble_getAdvertisingData(JsVar *data, JsVar *options); +void jswrap_ble_setScanResponse(JsVar *data); +void jswrap_ble_setServices(JsVar *data, JsVar *options); +void jswrap_ble_updateServices(JsVar *data); +void jswrap_ble_setScan(JsVar *callback, JsVar *options); +void jswrap_ble_findDevices(JsVar *callback, JsVar *options); +void jswrap_ble_setRSSIHandler(JsVar *callback); +void jswrap_ble_setTxPower(JsVarInt pwr); +void jswrap_ble_setLowPowerConnection(bool lowPower); -void jswrap_nrf_nfcURL(JsVar *url); -void jswrap_nrf_nfcRaw(JsVar *payload); -JsVar *jswrap_nrf_nfcStart(JsVar *payload); -void jswrap_nrf_nfcStop(); -void jswrap_nrf_nfcSend(JsVar *payload); -void jswrap_nrf_sendHIDReport(JsVar *data, JsVar *callback); +void jswrap_nfc_URL(JsVar *url); +void jswrap_nfc_raw(JsVar *payload); +JsVar *jswrap_nfc_start(JsVar *payload); +void jswrap_nfc_stop(); +void jswrap_nfc_send(JsVar *payload); +void jswrap_ble_sendHIDReport(JsVar *data, JsVar *callback); -JsVar *jswrap_nrf_bluetooth_requestDevice(JsVar *options); -JsVar *jswrap_nrf_bluetooth_connect(JsVar *mac); -void jswrap_nrf_setWhitelist(bool whitelist); +JsVar *jswrap_ble_requestDevice(JsVar *options); +JsVar *jswrap_ble_connect(JsVar *mac, JsVar *options); +void jswrap_ble_setWhitelist(bool whitelist); +void jswrap_ble_setConnectionInterval(JsVar *interval); +void jswrap_ble_setSecurity(JsVar *options); JsVar *jswrap_BluetoothDevice_gatt(JsVar *parent); -JsVar *jswrap_nrf_BluetoothRemoteGATTServer_connect(JsVar *parent); -void jswrap_BluetoothRemoteGATTServer_disconnect(JsVar *parent); -JsVar *jswrap_nrf_BluetoothRemoteGATTServer_startBonding(JsVar *parent, bool forceRePair); -JsVar *jswrap_nrf_BluetoothRemoteGATTServer_getSecurityStatus(JsVar *parent); +void jswrap_ble_BluetoothDevice_sendPasskey(JsVar *parent, JsVar *passkeyVar); +JsVar *jswrap_ble_BluetoothRemoteGATTServer_connect(JsVar *parent, JsVar *options); +JsVar *jswrap_BluetoothRemoteGATTServer_disconnect(JsVar *parent); +JsVar *jswrap_ble_BluetoothRemoteGATTServer_startBonding(JsVar *parent, bool forceRePair); +JsVar *jswrap_ble_BluetoothRemoteGATTServer_getSecurityStatus(JsVar *parent); JsVar *jswrap_BluetoothRemoteGATTServer_getPrimaryService(JsVar *parent, JsVar *service); JsVar *jswrap_BluetoothRemoteGATTServer_getPrimaryServices(JsVar *parent); void jswrap_BluetoothRemoteGATTServer_setRSSIHandler(JsVar *parent, JsVar *callback); JsVar *jswrap_BluetoothRemoteGATTService_getCharacteristic(JsVar *parent, JsVar *characteristic); JsVar *jswrap_BluetoothRemoteGATTService_getCharacteristics(JsVar *parent); -JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_writeValue(JsVar *characteristic, JsVar *data); -JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_readValue(JsVar *characteristic); -JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_startNotifications(JsVar *characteristic); -JsVar *jswrap_nrf_BluetoothRemoteGATTCharacteristic_stopNotifications(JsVar *characteristic); +JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_writeValue(JsVar *characteristic, JsVar *data); +JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_readValue(JsVar *characteristic); +JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_startNotifications(JsVar *characteristic); +JsVar *jswrap_ble_BluetoothRemoteGATTCharacteristic_stopNotifications(JsVar *characteristic); diff --git a/libs/compression/compress_heatshrink.c b/libs/compression/compress_heatshrink.c index d1aa55438..d5957a770 100644 --- a/libs/compression/compress_heatshrink.c +++ b/libs/compression/compress_heatshrink.c @@ -21,8 +21,8 @@ #define BUFFERSIZE 128 -/** gets data from array, writes to callback */ -void heatshrink_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned char ch, uint32_t *cbdata), uint32_t *cbdata) { +/** gets data from array, writes to callback if nonzero. Returns total length. */ +uint32_t heatshrink_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned char ch, uint32_t *cbdata), uint32_t *cbdata) { heatshrink_encoder hse; uint8_t outBuf[BUFFERSIZE]; heatshrink_encoder_reset(&hse); @@ -43,8 +43,9 @@ void heatshrink_encode(unsigned char *data, size_t dataLen, void (*callback)(uns do { pres = heatshrink_encoder_poll(&hse, outBuf, sizeof(outBuf), &count); assert(pres >= 0); - for (i=0;i= 0); + if (data) memcpy(&data[polled], outBuf, count); polled += count; } while (pres == HSER_POLL_MORE); assert(pres == HSER_POLL_EMPTY); @@ -98,4 +102,6 @@ void heatshrink_decode(int (*callback)(uint32_t *cbdata), uint32_t *cbdata, unsi heatshrink_decoder_finish(&hsd); } } + return (uint32_t)polled; } + diff --git a/libs/compression/compress_heatshrink.h b/libs/compression/compress_heatshrink.h index aaadb4804..537dc38b2 100644 --- a/libs/compression/compress_heatshrink.h +++ b/libs/compression/compress_heatshrink.h @@ -12,8 +12,8 @@ * ---------------------------------------------------------------------------- */ -/** gets data from array, writes to callback */ -void heatshrink_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned char ch, uint32_t *cbdata), uint32_t *cbdata); +/** gets data from array, writes to callback if nonzero. Returns total length. */ +uint32_t heatshrink_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned char ch, uint32_t *cbdata), uint32_t *cbdata); -/** gets data from callback, writes it into array */ -void heatshrink_decode(int (*callback)(uint32_t *cbdata), uint32_t *cbdata, unsigned char *data); +/** gets data from callback, writes it into array if nonzero. Returns total length */ +uint32_t heatshrink_decode(int (*callback)(uint32_t *cbdata), uint32_t *cbdata, unsigned char *data); diff --git a/libs/compression/compress_rle.c b/libs/compression/compress_rle.c index 2bc64e9f8..085e8ef42 100644 --- a/libs/compression/compress_rle.c +++ b/libs/compression/compress_rle.c @@ -14,13 +14,15 @@ #include "compress_rle.h" -// gets data from array, writes to callback -void rle_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned char ch, uint32_t *cbdata), uint32_t *cbdata) { +/** gets data from array, writes to callback if nonzero. Returns total length. */ +uint32_t rle_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned char ch, uint32_t *cbdata), uint32_t *cbdata) { + uint32_t outputLen = 0; int lastCh = -1; // not a valid char while (dataLen) { unsigned char ch = *(data++); dataLen--; - callback(ch, cbdata); + outputLen++; + if (callback) callback(ch, cbdata); if (ch==lastCh) { int cnt = 0; while (dataLen && lastCh==*data && cnt<255) { @@ -28,25 +30,31 @@ void rle_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned c dataLen--; cnt++; } - callback((unsigned char)cnt, cbdata); + outputLen++; + if (callback) callback((unsigned char)cnt, cbdata); } lastCh = ch; } + return outputLen; } -// gets data from callback, writes it into array -void rle_decode(int (*callback)(uint32_t *cbdata), uint32_t *cbdata, unsigned char *data) { +/** gets data from callback, writes it into array if nonzero. Returns total length */ +uint32_t rle_decode(int (*callback)(uint32_t *cbdata), uint32_t *cbdata, unsigned char *data) { + uint32_t outputLen = 0; int lastCh = -256; // not a valid char while (true) { int ch = callback(cbdata); - if (ch<0) return; - *(data++) = (unsigned char)ch; + if (ch<0) return outputLen; + if (data) data[outputLen] = (unsigned char)ch; + outputLen++; if (ch==lastCh) { int cnt = callback(cbdata); while (cnt-->0) { - *(data++) = (unsigned char)ch; + if (data) data[outputLen] = (unsigned char)ch; + outputLen++; } } lastCh = ch; } + return outputLen; } diff --git a/libs/compression/compress_rle.h b/libs/compression/compress_rle.h index 65838a2d6..0cf092627 100644 --- a/libs/compression/compress_rle.h +++ b/libs/compression/compress_rle.h @@ -14,8 +14,8 @@ #include "jsutils.h" -/** gets data from array, writes to callback */ -void rle_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned char ch, uint32_t *cbdata), uint32_t *cbdata); +/** gets data from array, writes to callback if nonzero. Returns total length. */ +uint32_t rle_encode(unsigned char *data, size_t dataLen, void (*callback)(unsigned char ch, uint32_t *cbdata), uint32_t *cbdata); -/** gets data from callback, writes it into array */ -void rle_decode(int (*callback)(uint32_t *cbdata), uint32_t *cbdata, unsigned char *data); +/** gets data from callback, writes it into array if nonzero. Returns total length */ +uint32_t rle_decode(int (*callback)(uint32_t *cbdata), uint32_t *cbdata, unsigned char *data); diff --git a/libs/compression/jswrap_heatshrink.c b/libs/compression/jswrap_heatshrink.c new file mode 100644 index 000000000..b333806b6 --- /dev/null +++ b/libs/compression/jswrap_heatshrink.c @@ -0,0 +1,109 @@ +/* + * This file is part of Espruino, a JavaScript interpreter for Microcontrollers + * + * Copyright (C) 2018 Gordon Williams + * + * 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/. + * + * ---------------------------------------------------------------------------- + * This file is designed to be parsed during the build process + * + * Simple compression/decompression using the heatshrink library + * ---------------------------------------------------------------------------- + */ +#include "jsvar.h" +#include "jsvariterator.h" +#include "compress_heatshrink.h" +#include "jswrap_heatshrink.h" +#include "jsparse.h" + + +/*JSON{ + "type" : "library", + "class" : "heatshrink", + "ifndef" : "SAVE_ON_FLASH" +} +Simple library for compression/decompression using [heatshrink](https://github.com/atomicobject/heatshrink), an [LZSS](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Storer%E2%80%93Szymanski) compression tool. + +Espruino uses heatshrink internally to compress RAM down to fit in Flash memory when `save()` is used. This just exposes that functionality. + +Functions here take and return buffers of data. There is no support for streaming, so both the compressed and decompressed data must be able to fit in memory at the same time. +*/ +typedef struct { + char *ptr; + size_t len; +} DecompressInfo; + +static void _jswrap_heatshrink_compress_output(unsigned char ch, uint32_t *cbdata) { + unsigned char **outPtr = (unsigned char**)cbdata; + *((*outPtr)++) = ch; +} +static int _jswrap_heatshrink_decompress_input(uint32_t *cbdata) { + DecompressInfo *decompressInfo = (DecompressInfo *)cbdata; + if (!decompressInfo->len) return -1; + decompressInfo->len--; + return (unsigned char)*(decompressInfo->ptr++); +} + +/*JSON{ + "type" : "staticmethod", + "class" : "heatshrink", + "name" : "compress", + "generate" : "jswrap_heatshrink_compress", + "params" : [ + ["data","JsVar","The data to compress"] + ], + "return" : ["JsVar","Returns the result as an ArrayBuffer"], + "return_object" : "ArrayBuffer", + "ifndef" : "SAVE_ON_FLASH" +} +*/ +JsVar *jswrap_heatshrink_compress(JsVar *data) { + JSV_GET_AS_CHAR_ARRAY(dataPtr, dataLen, data); + if (!dataPtr) return 0; + uint32_t compressedSize = heatshrink_encode((unsigned char*)dataPtr, dataLen, NULL, NULL); + + char *outPtr = 0; + JsVar *outArr = jsvNewArrayBufferWithPtr((unsigned int)compressedSize, &outPtr); + if (!outPtr) { + jsError("Not enough memory for result"); + return 0; + } + heatshrink_encode((unsigned char*)dataPtr, dataLen, _jswrap_heatshrink_compress_output, (uint32_t*)&outPtr); + return outArr; +} + +/*JSON{ + "type" : "staticmethod", + "class" : "heatshrink", + "name" : "decompress", + "generate" : "jswrap_heatshrink_decompress", + "params" : [ + ["data","JsVar","The data to decompress"] + ], + "return" : ["JsVar","Returns the result as an ArrayBuffer"], + "return_object" : "ArrayBuffer", + "ifndef" : "SAVE_ON_FLASH" +} +*/ +JsVar *jswrap_heatshrink_decompress(JsVar *data) { + JSV_GET_AS_CHAR_ARRAY(dataPtr, dataLen, data); + if (!dataPtr) return 0; + DecompressInfo decompressInfo; + decompressInfo.ptr = dataPtr; + decompressInfo.len = dataLen; + uint32_t decompressedSize = heatshrink_decode(_jswrap_heatshrink_decompress_input, (uint32_t*)&decompressInfo, NULL); + + char *outPtr = 0; + JsVar *outArr = jsvNewArrayBufferWithPtr((unsigned int)decompressedSize, &outPtr); + if (!outPtr) { + jsError("Not enough memory for result"); + return 0; + } + decompressInfo.ptr = dataPtr; + decompressInfo.len = dataLen; + heatshrink_decode(_jswrap_heatshrink_decompress_input, (uint32_t*)&decompressInfo, (unsigned char*)outPtr); + return outArr; +} diff --git a/targets/esp8266/emulator/user_config.h b/libs/compression/jswrap_heatshrink.h similarity index 59% rename from targets/esp8266/emulator/user_config.h rename to libs/compression/jswrap_heatshrink.h index d85c29eb9..7a5db8ea8 100644 --- a/targets/esp8266/emulator/user_config.h +++ b/libs/compression/jswrap_heatshrink.h @@ -1,22 +1,17 @@ /* * This file is part of Espruino, a JavaScript interpreter for Microcontrollers * - * Copyright (C) 2015 Gordon Williams + * Copyright (C) 2018 Gordon Williams * * 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/. * * ---------------------------------------------------------------------------- - * This file is designed to be parsed during the build process - * - * Contains ESP8266 board specific functions. + * Simple compression/decompression using the heatshrink library * ---------------------------------------------------------------------------- */ +#include "jsvar.h" -#ifndef TARGETS_ESP8266_EMULATOR_USER_CONFIG_H_ -#define TARGETS_ESP8266_EMULATOR_USER_CONFIG_H_ - - - -#endif /* TARGETS_ESP8266_EMULATOR_USER_CONFIG_H_ */ +JsVar *jswrap_heatshrink_compress(JsVar *data); +JsVar *jswrap_heatshrink_decompress(JsVar *data); diff --git a/libs/crypto/jswrap_crypto.c b/libs/crypto/jswrap_crypto.c index 4800e0dc6..5a6b94412 100644 --- a/libs/crypto/jswrap_crypto.c +++ b/libs/crypto/jswrap_crypto.c @@ -16,13 +16,20 @@ #include "jsvar.h" #include "jsvariterator.h" #include "jswrap_crypto.h" +#include "jsparse.h" #ifdef USE_AES #include "mbedtls/include/mbedtls/aes.h" #endif +#ifndef USE_SHA1_JS #include "mbedtls/include/mbedtls/sha1.h" +#endif +#ifdef USE_SHA256 #include "mbedtls/include/mbedtls/sha256.h" +#endif +#ifdef USE_SHA512 #include "mbedtls/include/mbedtls/sha512.h" +#endif #include "mbedtls/include/mbedtls/pkcs5.h" #ifdef USE_TLS #include "mbedtls/include/mbedtls/pk.h" @@ -38,7 +45,7 @@ } Cryptographic functions -**Note:** This library is currently only included in builds for the Espruino Pico and Espruino WiFi. For other boards you will have to make build your own firmware, and you may need to remove other features in order to make room. +**Note:** This library is currently only included in builds for boards where there is space. For other boards there is `crypto.js` which implements SHA1 in JS. */ @@ -46,11 +53,11 @@ Cryptographic functions "type" : "class", "library" : "crypto", "class" : "AES", - "ifdef" : "USE_TLS" + "ifdef" : "USE_AES" } Class containing AES encryption/decryption -**Note:** This library is currently only included in builds for the Espruino Pico and Espruino WiFi. For other boards you will have to make build your own firmware, and you may need to remove other features in order to make room. +**Note:** This library is currently only included in builds for boards where there is space. For other boards there is `crypto.js` which implements SHA1 in JS. */ /*JSON{ "type" : "staticproperty", @@ -118,16 +125,29 @@ CryptoMode jswrap_crypto_getMode(JsVar *mode) { } mbedtls_md_type_t jswrap_crypto_getHasher(JsVar *hasher) { +#ifndef USE_SHA1_JS if (jsvIsStringEqual(hasher, "SHA1")) return MBEDTLS_MD_SHA1; +#endif +#ifdef USE_SHA256 if (jsvIsStringEqual(hasher, "SHA224")) return MBEDTLS_MD_SHA224; if (jsvIsStringEqual(hasher, "SHA256")) return MBEDTLS_MD_SHA256; +#endif +#ifdef USE_SHA512 if (jsvIsStringEqual(hasher, "SHA384")) return MBEDTLS_MD_SHA384; if (jsvIsStringEqual(hasher, "SHA512")) return MBEDTLS_MD_SHA512; +#endif jsExceptionHere(JSET_ERROR, "Unknown Hasher %q", hasher); return MBEDTLS_MD_NONE; } JsVar *jswrap_crypto_SHAx(JsVar *message, int shaNum) { +#ifdef USE_SHA1_JS + if (shaNum==1) { + // (c) 2016 Rhys Williams, @jumjum. https://github.com/espruino/EspruinoDocs/blob/master/modules/crypto.js + return jspExecuteJSFunction("(function(b){function n(a){for(d=3;0<=d;d--)g.push(a>>8*d&255)}var d,a;b=E.toString(b)+'\\x80';var v=new Int32Array([1518500249,1859775393,2400959708,3395469782]);var k=Math.ceil((b.length/4+2)/16);var g=Array(k);b=E.toUint8Array(b);for(d=0;da;a++){var c=f+(a<<2);e[a]=b[c]<<24|b[c+1]<<16|b[c+2]<<8|b[c+3]}g[d]=e}g[k-1][14]=8*(b.length-1)/Math.pow(2,32);g[k-1][14]=Math.floor(g[k-1][14]);g[k-1][15]=8*(b.length-1)&4294967295;b=1732584193;var p=4023233417;var q=2562383102;var r=271733878;var t=3285377520;var l=new Int32Array(80);for(d=0;da;a++)l[a]=g[d][a];for(a=16;80>a;a++)f=l[a-3]^l[a-8]^l[a-14]^l[a-16],l[a]=f<<1|f>>>31;f=b;c=p;e=q;var h=r;var u=t;for(a=0;80>a;a++){var m=Math.floor(a/20);var w=f<<5|f>>>27;var x=0===m?c&e^~c&h:1===m?c^e^h:2===m?c&e^c&h^e&h:c^e^h;m=w+x+u+v[m]+l[a]&4294967295;u=h;h=e;e=c<<30|c>>>2;c=f;f=m}b=b+f&4294967295;p=p+c&4294967295;q=q+e&4294967295;r=r+h&4294967295;t=t+u&4294967295}g=[];n(b);n(p);n(q);n(r);n(t);return E.toUint8Array(g).buffer})",0,1,&message); + } +#endif + JSV_GET_AS_CHAR_ARRAY(msgPtr, msgLen, message); if (!msgPtr) return 0; @@ -141,11 +161,17 @@ JsVar *jswrap_crypto_SHAx(JsVar *message, int shaNum) { return 0; } +#ifndef USE_SHA1_JS if (shaNum==1) mbedtls_sha1((unsigned char *)msgPtr, msgLen, (unsigned char *)outPtr); +#endif +#ifdef USE_SHA256 else if (shaNum==224) mbedtls_sha256((unsigned char *)msgPtr, msgLen, (unsigned char *)outPtr, true/*224*/); else if (shaNum==256) mbedtls_sha256((unsigned char *)msgPtr, msgLen, (unsigned char *)outPtr, false/*256*/); +#endif +#ifdef USE_SHA512 else if (shaNum==384) mbedtls_sha512((unsigned char *)msgPtr, msgLen, (unsigned char *)outPtr, true/*384*/); else if (shaNum==512) mbedtls_sha512((unsigned char *)msgPtr, msgLen, (unsigned char *)outPtr, false/*512*/); +#endif return outArr; } @@ -162,7 +188,11 @@ JsVar *jswrap_crypto_SHAx(JsVar *message, int shaNum) { "ifdef" : "USE_CRYPTO" } -Performs a SHA1 hash and returns the result as a 20 byte ArrayBuffer +Performs a SHA1 hash and returns the result as a 20 byte ArrayBuffer. + +**Note:** On some boards (currently only Espruino Original) there +isn't space for a fully unrolled SHA1 implementation so a slower +all-JS implementation is used instead. */ /*JSON{ "type" : "staticmethod", @@ -174,7 +204,7 @@ Performs a SHA1 hash and returns the result as a 20 byte ArrayBuffer ], "return" : ["JsVar","Returns a 20 byte ArrayBuffer"], "return_object" : "ArrayBuffer", - "ifdef" : "USE_CRYPTO" + "ifdef" : "USE_SHA256" } Performs a SHA224 hash and returns the result as a 28 byte ArrayBuffer @@ -189,7 +219,7 @@ Performs a SHA224 hash and returns the result as a 28 byte ArrayBuffer ], "return" : ["JsVar","Returns a 20 byte ArrayBuffer"], "return_object" : "ArrayBuffer", - "ifdef" : "USE_CRYPTO" + "ifdef" : "USE_SHA256" } Performs a SHA256 hash and returns the result as a 32 byte ArrayBuffer @@ -204,7 +234,7 @@ Performs a SHA256 hash and returns the result as a 32 byte ArrayBuffer ], "return" : ["JsVar","Returns a 20 byte ArrayBuffer"], "return_object" : "ArrayBuffer", - "ifdef" : "USE_CRYPTO" + "ifdef" : "USE_SHA512" } Performs a SHA384 hash and returns the result as a 48 byte ArrayBuffer @@ -219,7 +249,7 @@ Performs a SHA384 hash and returns the result as a 48 byte ArrayBuffer ], "return" : ["JsVar","Returns a 32 byte ArrayBuffer"], "return_object" : "ArrayBuffer", - "ifdef" : "USE_CRYPTO" + "ifdef" : "USE_SHA512" } Performs a SHA512 hash and returns the result as a 64 byte ArrayBuffer @@ -299,8 +329,9 @@ JsVar *jswrap_crypto_PBKDF2(JsVar *passphrase, JsVar *salt, JsVar *options) { return 0; } } +#endif - +#ifdef USE_AES static NO_INLINE JsVar *jswrap_crypto_AEScrypt(JsVar *message, JsVar *key, JsVar *options, bool encrypt) { int err; @@ -411,8 +442,6 @@ static NO_INLINE JsVar *jswrap_crypto_AEScrypt(JsVar *message, JsVar *key, JsVar return 0; } } -#endif -#ifdef USE_AES /*JSON{ "type" : "staticmethod", diff --git a/libs/crypto/mbedtls/config.h b/libs/crypto/mbedtls/config.h index d3e5b5e89..d5a5f7198 100644 --- a/libs/crypto/mbedtls/config.h +++ b/libs/crypto/mbedtls/config.h @@ -34,37 +34,28 @@ // See aes.c. Do we want 10kB of data full of constants? no. #define MBEDTLS_AES_ROM_TABLES +// Use non-unrolled SHA256 +#define MBEDTLS_SHA256_SMALLER #ifdef USE_TLS /* mbed TLS feature support */ -#define MBEDTLS_CIPHER_MODE_CBC -#define MBEDTLS_CIPHER_MODE_CFB -#define MBEDTLS_CIPHER_MODE_CTR - #define MBEDTLS_PKCS1_V15 #define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED #define MBEDTLS_SSL_PROTO_TLS1_2 /* mbed TLS modules */ -#define MBEDTLS_AES_C -#define MBEDTLS_ASN1_PARSE_C #define MBEDTLS_BIGNUM_C -#define MBEDTLS_CIPHER_C #define MBEDTLS_CTR_DRBG_C #define MBEDTLS_ECP_C #define MBEDTLS_ENTROPY_C #define MBEDTLS_MD_C #define MBEDTLS_MD5_C -#define MBEDTLS_OID_C -#define MBEDTLS_PKCS5_C #define MBEDTLS_PK_C #define MBEDTLS_PK_PARSE_C #define MBEDTLS_RSA_C -#define MBEDTLS_SHA1_C -#define MBEDTLS_SHA256_C -#define MBEDTLS_SHA512_C + #define MBEDTLS_SSL_CLI_C #define MBEDTLS_SSL_SRV_C #define MBEDTLS_SSL_TLS_C @@ -88,26 +79,25 @@ #define MBEDTLS_ECP_DP_BP512R1_ENABLED #define MBEDTLS_ECP_DP_CURVE25519_ENABLED -#else // !USE_TLS +#endif -/* mbed TLS feature support */ +/* common mbed TLS feature support */ #define MBEDTLS_CIPHER_MODE_CBC #define MBEDTLS_CIPHER_MODE_CFB #define MBEDTLS_CIPHER_MODE_CTR - -/* mbed TLS modules */ +/* common mbed TLS modules */ #define MBEDTLS_AES_C #define MBEDTLS_ASN1_PARSE_C #define MBEDTLS_CIPHER_C #define MBEDTLS_MD_C #define MBEDTLS_OID_C #define MBEDTLS_PKCS5_C +#ifndef USE_SHA1_JS #define MBEDTLS_SHA1_C +#endif #define MBEDTLS_SHA256_C #define MBEDTLS_SHA512_C -#endif - #include "jsvar.h" #define MBEDTLS_PLATFORM_C diff --git a/libs/filesystem/fat_sd/fattime.c b/libs/filesystem/fat_sd/fattime.c index d1ef75953..bb377a9f5 100644 --- a/libs/filesystem/fat_sd/fattime.c +++ b/libs/filesystem/fat_sd/fattime.c @@ -1,23 +1,23 @@ -/* Martin Thomas 4/2009 */ +/* Martin Thomas 4/2009 + * Gordon Williams 2019 */ #include "integer.h" #include "fattime.h" -//#include "rtc.h" +#include "jswrap_date.h" DWORD get_fattime (void) { + JsVarFloat time = jswrap_date_now(); + TimeInDay tid = getTimeFromMilliSeconds(time, false); + CalendarDate date = getCalendarDate(tid.daysSinceEpoch); + DWORD res; - /*RTC_t rtc; - - rtc_gettime( &rtc );*/ - - res = (((DWORD)2012/*rtc.year*/ - 1980) << 25) - | ((DWORD)1/*rtc.month*/ << 21) - | ((DWORD)1/*rtc.mday*/ << 16) - | (WORD)(0/*rtc.hour*/ << 11) - | (WORD)(0/*rtc.min*/ << 5) - | (WORD)(0/*rtc.sec*/ >> 1); - + res = (((DWORD)date.year - 1980) << 25) + | ((DWORD)(date.month+1) << 21) + | ((DWORD)date.day << 16) + | (WORD)(tid.hour << 11) + | (WORD)(tid.min << 5) + | (WORD)(tid.sec >> 1); return res; } diff --git a/libs/filesystem/fat_sd/ffconf.h b/libs/filesystem/fat_sd/ffconf.h index 2df4b2e72..27f78f0db 100755 --- a/libs/filesystem/fat_sd/ffconf.h +++ b/libs/filesystem/fat_sd/ffconf.h @@ -208,6 +208,7 @@ / System Configurations /---------------------------------------------------------------------------*/ +#ifdef SAVE_ON_FLASH #define _FS_NORTC 1 #define _NORTC_MON 1 #define _NORTC_MDAY 1 @@ -220,6 +221,10 @@ / to be added to the project to read current time from RTC. _NORTC_MON, / _NORTC_MDAY and _NORTC_YEAR have no effect. / These options have no effect at read-only configuration (_FS_READONLY == 1). */ +#else +#define _FS_NORTC 0 +// we have time! +#endif #define _FS_LOCK 0 diff --git a/libs/filesystem/jswrap_file.c b/libs/filesystem/jswrap_file.c index b6a5c0343..c1a577167 100755 --- a/libs/filesystem/jswrap_file.c +++ b/libs/filesystem/jswrap_file.c @@ -18,7 +18,7 @@ #include "jsflags.h" #define JS_FS_DATA_NAME JS_HIDDEN_CHAR_STR"FSd" // the data in each file -#define JS_FS_OPEN_FILES_NAME JS_HIDDEN_CHAR_STR"FSo" // the list of open files +#define JS_FS_OPEN_FILES_NAME "FSopen" // the list of open files #if !defined(LINUX) && !defined(USE_FILESYSTEM_SDIO) && !defined(USE_FLASHFS) #define SD_CARD_ANYWHERE #endif @@ -239,6 +239,7 @@ static bool allocateJsFile(JsFile* file,FileMode mode, FileType type) { JsVar *data = jsvNewFlatStringOfLength(sizeof(JsFileData)); if (!data) { // out of memory for flat string + jsErrorFlags |= JSERR_LOW_MEMORY; // flag this up as an issue jsvUnLock(parent); return false; } @@ -603,9 +604,11 @@ Before first use the media needs to be formatted. ``` fs=require("fs"); -if ( typeof(fs.readdirSync())==="undefined" ) { - console.log("Formatting FS"); - E.flashFatFS({format:true}); +try { + fs.readdirSync(); + } catch (e) { //'Uncaught Error: Unable to mount media : NO_FILESYSTEM' + console.log('Formatting FS - only need to do once'); + E.flashFatFS({ format: true }); } fs.writeFileSync("bang.txt", "This is the way the world ends\nnot with a bang but a whimper.\n"); fs.readdirSync(); diff --git a/libs/graphics/graphics.c b/libs/graphics/graphics.c index 10c947852..f0e625809 100644 --- a/libs/graphics/graphics.c +++ b/libs/graphics/graphics.c @@ -73,21 +73,48 @@ void graphicsFallbackScroll(JsGraphics *gfx, int xdir, int ydir) { if (xdir==0 && ydir==0) return; int y; if (ydir<=0) { - int h = gfx->data.height+xdir; + int h = gfx->data.height+ydir; for (y=0;y0 for (y=gfx->data.height-ydir-1;y>=0;y--) graphicsFallbackScrollX(gfx, xdir, y, y+ydir); } +#ifndef SAVE_ON_FLASH gfx->data.modMinX=0; gfx->data.modMinY=0; gfx->data.modMaxX=gfx->data.width-1; gfx->data.modMaxY=gfx->data.height-1; +#endif } // ---------------------------------------------------------------------------------------------- +void graphicsStructResetState(JsGraphics *gfx) { + gfx->data.fgColor = 0xFFFFFFFF; + gfx->data.bgColor = 0; + gfx->data.fontSize = JSGRAPHICS_FONTSIZE_4X6; +#ifndef SAVE_ON_FLASH + gfx->data.fontAlignX = 3; + gfx->data.fontAlignY = 3; + gfx->data.fontRotate = 0; +#endif + gfx->data.cursorX = 0; + gfx->data.cursorY = 0; +} + +void graphicsStructInit(JsGraphics *gfx) { + // type/width/height/bpp should be set elsewhere... + gfx->data.flags = JSGRAPHICSFLAGS_NONE; + graphicsStructResetState(gfx); +#ifndef SAVE_ON_FLASH + gfx->data.modMaxX = -32768; + gfx->data.modMaxY = -32768; + gfx->data.modMinX = 32767; + gfx->data.modMinY = 32767; +#endif +} + bool graphicsGetFromVar(JsGraphics *gfx, JsVar *parent) { gfx->graphicsVar = parent; JsVar *data = jsvObjectGetChild(parent, JS_HIDDEN_CHAR_STR"gfx", 0); @@ -160,10 +187,12 @@ void graphicsToDeviceCoordinates(const JsGraphics *gfx, short *x, short *y) { static void graphicsSetPixelDevice(JsGraphics *gfx, int x, int y, unsigned int col) { if (x<0 || y<0 || x>=gfx->data.width || y>=gfx->data.height) return; +#ifndef SAVE_ON_FLASH if (x < gfx->data.modMinX) gfx->data.modMinX=(short)x; if (x > gfx->data.modMaxX) gfx->data.modMaxX=(short)x; if (y < gfx->data.modMinY) gfx->data.modMinY=(short)y; if (y > gfx->data.modMaxY) gfx->data.modMaxY=(short)y; +#endif gfx->setPixel(gfx,(short)x,(short)y,col & (unsigned int)((1L<data.bpp)-1)); } @@ -188,12 +217,12 @@ static void graphicsFillRectDevice(JsGraphics *gfx, int x1, int y1, int x2, int if (x2>=gfx->data.width) x2 = gfx->data.width - 1; if (y2>=gfx->data.height) y2 = gfx->data.height - 1; if (x2data.modMinX) gfx->data.modMinX=(short)x1; if (x2 > gfx->data.modMaxX) gfx->data.modMaxX=(short)x2; if (y1 < gfx->data.modMinY) gfx->data.modMinY=(short)y1; if (y2 > gfx->data.modMaxY) gfx->data.modMaxY=(short)y2; - +#endif if (x1==x2 && y1==y2) { gfx->setPixel(gfx,(short)x1,(short)y1,gfx->data.fgColor); return; @@ -240,57 +269,68 @@ void graphicsDrawRect(JsGraphics *gfx, short x1, short y1, short x2, short y2) { graphicsFillRectDevice(gfx,x1,y2,x1,y1); } -void graphicsDrawCircle(JsGraphics *gfx, short posX, short posY, short rad) { - graphicsToDeviceCoordinates(gfx, &posX, &posY); - - int radY = 0, - radX = rad; - // Decision criterion divided by 2 evaluated at radX=radX, radY=0 - int decisionOver2 = 1 - radX; - - while (radX >= radY) { - graphicsSetPixelDevice(gfx, radX + posX, radY + posY, gfx->data.fgColor); - graphicsSetPixelDevice(gfx, radY + posX, radX + posY, gfx->data.fgColor); - graphicsSetPixelDevice(gfx, -radX + posX, radY + posY, gfx->data.fgColor); - graphicsSetPixelDevice(gfx, -radY + posX, radX + posY, gfx->data.fgColor); - graphicsSetPixelDevice(gfx, -radX + posX, -radY + posY, gfx->data.fgColor); - graphicsSetPixelDevice(gfx, -radY + posX, -radX + posY, gfx->data.fgColor); - graphicsSetPixelDevice(gfx, radX + posX, -radY + posY, gfx->data.fgColor); - graphicsSetPixelDevice(gfx, radY + posX, -radX + posY, gfx->data.fgColor); - radY++; - - if (decisionOver2 <= 0) { - // Change in decision criterion for radY -> radY+1 - decisionOver2 += 2 * radY + 1; - } - else { - radX--; - // Change for radY -> radY+1, radX -> radX-1 - decisionOver2 += 2 * (radY - radX) + 1; +void graphicsDrawEllipse(JsGraphics *gfx, short posX1, short posY1, short posX2, short posY2){ + graphicsToDeviceCoordinates(gfx, &posX1, &posY1); + graphicsToDeviceCoordinates(gfx, &posX2, &posY2); + int posX = (posX1+posX2)/2; + int posY = (posY1+posY2)/2; + int width = (posX2-posX1)/2; + int height = (posY2-posY1)/2; + if (width<0) width=-width; + if (height<0) height=-height; + int hh = height * height; + int ww = width * width; + int hhww = hh * ww; + int x0 = width; + int dx = 0; + int y; + graphicsSetPixelDevice(gfx,posX - width,posY,gfx->data.fgColor); + graphicsSetPixelDevice(gfx,posX + width,posY,gfx->data.fgColor); + for (y = 1; y <= height; y++) { + int x1 = x0 - (dx - 1); + for(; x1> 0; x1--) + if (x1 * x1 * hh + y * y * ww <= hhww) + break; + dx = x0 - x1; + x0 = x1; + if(dx<2){ + graphicsSetPixelDevice(gfx,posX - x0, posY - y,gfx->data.fgColor); + graphicsSetPixelDevice(gfx,posX + x0, posY - y,gfx->data.fgColor); + graphicsSetPixelDevice(gfx,posX - x0, posY + y,gfx->data.fgColor); + graphicsSetPixelDevice(gfx,posX + x0, posY + y,gfx->data.fgColor); + } else { + graphicsFillRectDevice(gfx,posX - x0, posY - y, posX - x0 - dx + 1, posY - y); + graphicsFillRectDevice(gfx,posX + x0, posY - y, posX + x0 + dx - 1, posY - y); + graphicsFillRectDevice(gfx,posX - x0, posY + y, posX - x0 - dx + 1, posY + y); + graphicsFillRectDevice(gfx,posX + x0, posY + y, posX + x0 + dx - 1, posY + y); } } } -void graphicsFillCircle(JsGraphics *gfx, short x, short y, short rad) { - graphicsToDeviceCoordinates(gfx, &x, &y); - - int radY = 0; - int decisionOver2 = 1 - rad; - - while (rad >= radY) { - graphicsFillRectDevice(gfx, rad + x, radY + y, -rad + x, -radY + y); - graphicsFillRectDevice(gfx, radY + x, rad + y, -radY + x, -rad + y); - graphicsFillRectDevice(gfx, -rad + x, radY + y, rad + x, -radY + y); - graphicsFillRectDevice(gfx, -radY + x, rad + y, radY + x, -rad + y); - radY++; - if (decisionOver2 <= 0){ - // Change in decision criterion for radY -> radY+1 - decisionOver2 += 2 * radY + 1; - }else{ - rad--; - // Change for radY -> radY+1, rad -> rad-1 - decisionOver2 += 2 * (radY - rad) + 1; - } +void graphicsFillEllipse(JsGraphics *gfx, short posX1, short posY1, short posX2, short posY2){ + graphicsToDeviceCoordinates(gfx, &posX1, &posY1); + graphicsToDeviceCoordinates(gfx, &posX2, &posY2); + int posX = (posX1+posX2)/2; + int posY = (posY1+posY2)/2; + int width = (posX2-posX1)/2; + int height = (posY2-posY1)/2; + if (width<0) width=-width; + if (height<0) height=-height; + int hh = height * height; + int ww = width * width; + int hhww = hh * ww; + int x0 = width; + int dx = 0; + graphicsFillRectDevice(gfx, posX - width, posY, posX + width, posY); + for (int y = 1; y <= height; y++) { + int x1 = x0 - (dx - 1); + for ( ; x1 > 0; x1--) + if (x1*x1*hh + y*y*ww <= hhww) + break; + dx = x0 - x1; + x0 = x1; + graphicsFillRectDevice(gfx, posX - x0, posY - y, posX + x0, posY - y); + graphicsFillRectDevice(gfx, posX - x0, posY + y, posX + x0, posY + y); } } @@ -340,30 +380,31 @@ void graphicsDrawLine(JsGraphics *gfx, short x1, short y1, short x2, short y2) { } static inline void graphicsFillPolyCreateScanLines(JsGraphics *gfx, short *minx, short *maxx, short x1, short y1,short x2, short y2) { - if (y2 < y1) { - short t; - t=x1;x1=x2;x2=t; - t=y1;y1=y2;y2=t; - } - int xh = x1*256; - int yl = y2-y1; - if (yl==0) yl=1; - int stepx = (x2-x1)*256 / yl; - short y; - for (y=y1;y<=y2;y++) { - int x = xh>>8; - if (x<-32768) x=-32768; - if (x>32767) x=32767; - if (y>=0 && ydata.height) { - if (xmaxx[y]) { - maxx[y] = (short)x; - } - } - xh += stepx; + if (y2 < y1) { + short t; + t=x1;x1=x2;x2=t; + t=y1;y1=y2;y2=t; + } + int xh = x1*256 + 128/*do rounding here rather than when we >>8*/; + int yl = (1+y2)-y1; + int stepx = ((x2-x1)*256 + (yl/2)/*rounding*/) / yl; + short y; + int x = xh>>8; + if (x<-32768) x=-32768; + if (x>32767) x=32767; + for (y=y1;y<=y2;y++) { + int oldx = x; + xh += stepx; + x = xh>>8; + if (x<-32768) x=-32768; + if (x>32767) x=32767; + if (y>=0 && ydata.height) { + if (oldxmaxx[y]) maxx[y] = (short)oldx; + if (xmaxx[y]) maxx[y] = (short)x; } + } } void graphicsFillPoly(JsGraphics *gfx, int points, short *vertices) { diff --git a/libs/graphics/graphics.h b/libs/graphics/graphics.h index 3b1555933..153347046 100644 --- a/libs/graphics/graphics.h +++ b/libs/graphics/graphics.h @@ -30,17 +30,19 @@ typedef enum { JSGRAPHICSFLAGS_ARRAYBUFFER_ZIGZAG = 1, ///< ArrayBuffer: zig-zag (even rows reversed) JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE = 2, ///< ArrayBuffer: if 1 bpp, treat bytes as stacked vertically JSGRAPHICSFLAGS_ARRAYBUFFER_MSB = 4, ///< ArrayBuffer: store pixels MSB first - JSGRAPHICSFLAGS_SWAP_XY = 8, //< All devices: swap X and Y over - JSGRAPHICSFLAGS_INVERT_X = 16, //< All devices: x = getWidth() - (x+1) - where x is DEVICE X - JSGRAPHICSFLAGS_INVERT_Y = 32, //< All devices: y = getHeight() - (y+1) - where y is DEVICE Y + JSGRAPHICSFLAGS_ARRAYBUFFER_INTERLEAVEX = 8, //< ArrayBuffer: Pixels 0,2,4,etc are from the top half of the image, 1,3,5,etc from the bottom half. Used for P3 LED panels + JSGRAPHICSFLAGS_SWAP_XY = 16, //< All devices: swap X and Y over + JSGRAPHICSFLAGS_INVERT_X = 32, //< All devices: x = getWidth() - (x+1) - where x is DEVICE X + JSGRAPHICSFLAGS_INVERT_Y = 64, //< All devices: y = getHeight() - (y+1) - where y is DEVICE Y + JSGRAPHICSFLAGS_COLOR_BASE = 128, JSGRAPHICSFLAGS_COLOR_RGB = 0, - JSGRAPHICSFLAGS_COLOR_BRG = 64, //< All devices: color order is BRG - JSGRAPHICSFLAGS_COLOR_BGR = 128, //< All devices: color order is BGR - JSGRAPHICSFLAGS_COLOR_GBR = 64+128, //< All devices: color order is GBR - JSGRAPHICSFLAGS_COLOR_GRB = 256, //< All devices: color order is GRB - JSGRAPHICSFLAGS_COLOR_RBG = 256+64, //< All devices: color order is RBG - JSGRAPHICSFLAGS_COLOR_MASK = 64+128+256, //< All devices: color order is BRG + JSGRAPHICSFLAGS_COLOR_BRG = JSGRAPHICSFLAGS_COLOR_BASE, //< All devices: color order is BRG + JSGRAPHICSFLAGS_COLOR_BGR = JSGRAPHICSFLAGS_COLOR_BASE*2, //< All devices: color order is BGR + JSGRAPHICSFLAGS_COLOR_GBR = JSGRAPHICSFLAGS_COLOR_BASE*3, //< All devices: color order is GBR + JSGRAPHICSFLAGS_COLOR_GRB = JSGRAPHICSFLAGS_COLOR_BASE*4, //< All devices: color order is GRB + JSGRAPHICSFLAGS_COLOR_RBG = JSGRAPHICSFLAGS_COLOR_BASE*5, //< All devices: color order is RBG + JSGRAPHICSFLAGS_COLOR_MASK = JSGRAPHICSFLAGS_COLOR_BASE*7, //< All devices: color order is BRG } JsGraphicsFlags; #define JSGRAPHICS_FONTSIZE_4X6 (-1) // a bitmap font @@ -60,7 +62,14 @@ typedef struct { unsigned int fgColor, bgColor; ///< current foreground and background colors short fontSize; ///< See JSGRAPHICS_FONTSIZE_ constants short cursorX, cursorY; ///< current cursor positions +#ifndef SAVE_ON_FLASH + unsigned char fontAlignX : 2; + unsigned char fontAlignY : 2; + unsigned char fontRotate : 2; +#endif +#ifndef SAVE_ON_FLASH short modMinX, modMinY, modMaxX, modMaxY; ///< area that has been modified +#endif } PACKED_FLAGS JsGraphicsData; typedef struct JsGraphics { @@ -75,23 +84,14 @@ typedef struct JsGraphics { void (*scroll)(struct JsGraphics *gfx, int xdir, int ydir); // scroll - leave unscrolled area undefined } PACKED_FLAGS JsGraphics; -static inline void graphicsStructInit(JsGraphics *gfx) { - // type/width/height/bpp should be set elsewhere... - gfx->data.flags = JSGRAPHICSFLAGS_NONE; - gfx->data.fgColor = 0xFFFFFFFF; - gfx->data.bgColor = 0; - gfx->data.fontSize = JSGRAPHICS_FONTSIZE_4X6; - gfx->data.cursorX = 0; - gfx->data.cursorY = 0; - gfx->data.modMaxX = -32768; - gfx->data.modMaxY = -32768; - gfx->data.modMinX = 32767; - gfx->data.modMinY = 32767; -} - // ---------------------------------- these are in graphics.c -// Access a JsVar and get/set the relevant info in JsGraphics +/// Reset graphics structure state (eg font size, color, etc) +void graphicsStructResetState(JsGraphics *gfx); +/// Completely reset graphics structure including flags +void graphicsStructInit(JsGraphics *gfx); +/// Access the Graphics Instance JsVar and get the relevant info in a JsGraphics structure bool graphicsGetFromVar(JsGraphics *gfx, JsVar *parent); +/// Access the Graphics Instance JsVar and set the relevant info from JsGraphics structure void graphicsSetVar(JsGraphics *gfx); // ---------------------------------------------------------------------------------------------- /// Get the memory requires for this graphics's pixels if everything was packed as densely as possible @@ -105,8 +105,8 @@ void graphicsClear(JsGraphics *gfx); void graphicsFillRect(JsGraphics *gfx, short x1, short y1, short x2, short y2); void graphicsFallbackFillRect(JsGraphics *gfx, short x1, short y1, short x2, short y2); // Simple fillrect - doesn't call device-specific FR void graphicsDrawRect(JsGraphics *gfx, short x1, short y1, short x2, short y2); -void graphicsDrawCircle(JsGraphics *gfx, short posX, short posY, short rad); -void graphicsFillCircle(JsGraphics *gfx, short x, short y, short rad); +void graphicsDrawEllipse(JsGraphics *gfx, short x, short y, short x2, short y2); +void graphicsFillEllipse(JsGraphics *gfx, short x, short y, short x2, short y2); void graphicsDrawString(JsGraphics *gfx, short x1, short y1, const char *str); void graphicsDrawLine(JsGraphics *gfx, short x1, short y1, short x2, short y2); void graphicsFillPoly(JsGraphics *gfx, int points, short *vertices); // may overwrite vertices... diff --git a/libs/graphics/jswrap_graphics.c b/libs/graphics/jswrap_graphics.c index 17c3c03b9..9561038b6 100644 --- a/libs/graphics/jswrap_graphics.c +++ b/libs/graphics/jswrap_graphics.c @@ -26,6 +26,8 @@ #include "lcd_fsmc.h" #endif +#include "jswrap_functions.h" // for asURL + /*JSON{ "type" : "class", @@ -56,6 +58,7 @@ void jswrap_graphics_init() { JsVar *parent = jspNewObject("LCD", "Graphics"); if (parent) { JsVar *parentObj = jsvSkipName(parent); + jsvObjectSetChild(execInfo.hiddenRoot, JS_GRAPHICS_VAR, parentObj); JsGraphics gfx; graphicsStructInit(&gfx); gfx.data.type = JSGRAPHICSTYPE_FSMC; @@ -72,6 +75,22 @@ void jswrap_graphics_init() { #endif } +/*JSON{ + "type" : "staticmethod", + "class" : "Graphics", + "name" : "getInstance", + "generate" : "jswrap_graphics_getInstance", + "return" : ["JsVar","An instance of `Graphics` or undefined"] +} +On devices like Pixl.js or HYSTM boards that contain a built-in display +this will return an instance of the graphics class that can be used to +access that display. + +Internally, this is stored as a member called `gfx` inside the 'hiddenRoot'. +*/ +JsVar *jswrap_graphics_getInstance() { + return jsvObjectGetChild(execInfo.hiddenRoot, JS_GRAPHICS_VAR, 0); +} static bool isValidBPP(int bpp) { return bpp==1 || bpp==2 || bpp==4 || bpp==8 || bpp==16 || bpp==24 || bpp==32; // currently one colour can't ever be spread across multiple bytes @@ -91,6 +110,7 @@ static bool isValidBPP(int bpp) { "zigzag = whether to alternate the direction of scanlines for rows", "vertical_byte = whether to align bits in a byte vertically or not", "msb = when bits<8, store pixels msb first", + "interleavex = Pixels 0,2,4,etc are from the top half of the image, 1,3,5,etc from the bottom half. Used for P3 LED panels.", "color_order = re-orders the colour values that are supplied via setColor" ]] ], @@ -126,11 +146,19 @@ JsVar *jswrap_graphics_createArrayBuffer(int width, int height, int bpp, JsVar * gfx.data.flags = (JsGraphicsFlags)(gfx.data.flags | JSGRAPHICSFLAGS_ARRAYBUFFER_ZIGZAG); if (jsvGetBoolAndUnLock(jsvObjectGetChild(options, "msb", 0))) gfx.data.flags = (JsGraphicsFlags)(gfx.data.flags | JSGRAPHICSFLAGS_ARRAYBUFFER_MSB); + if (jsvGetBoolAndUnLock(jsvObjectGetChild(options, "interleavex", 0))) + gfx.data.flags = (JsGraphicsFlags)(gfx.data.flags | JSGRAPHICSFLAGS_ARRAYBUFFER_INTERLEAVEX); if (jsvGetBoolAndUnLock(jsvObjectGetChild(options, "vertical_byte", 0))) { if (gfx.data.bpp==1) gfx.data.flags = (JsGraphicsFlags)(gfx.data.flags | JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE); - else - jsWarn("vertical_byte only works for 1bpp ArrayBuffers\n"); + else { + jsExceptionHere(JSET_ERROR, "vertical_byte only works for 1bpp ArrayBuffers\n"); + return 0; + } + if (gfx.data.height&7) { + jsExceptionHere(JSET_ERROR, "height must be a multiple of 8 when using vertical_byte\n"); + return 0; + } } JsVar *colorv = jsvObjectGetChild(options, "color_order", 0); if (colorv) { @@ -254,6 +282,100 @@ JsVar *jswrap_graphics_createSDL(int width, int height) { } #endif + +/*JSON{ + "type" : "staticmethod", + "class" : "Graphics", + "name" : "createImage", + "#if" : "!defined(SAVE_ON_FLASH) && !defined(ESPRUINOBOARD)", + "generate" : "jswrap_graphics_createImage", + "params" : [ + ["str","JsVar","A String containing a newline-separated image - space is 0, anything else is 1"] + ], + "return" : ["JsVar","An Image object that can be used with `Graphics.drawImage`"] +} +Create a simple Black and White image for use with `Graphics.drawImage`. + +Use as follows: + +``` +var img = Graphics.createImage(` +XXXXXXXXX +X X +X X X +X X X +X X +XXXXXXXXX +`); +g.drawImage(img, x,y); +``` + +If the characters at the beginning and end of the string are newlines, they +will be ignored. Spaces are treated as `0`, and any other character is a `1` +*/ +JsVar *jswrap_graphics_createImage(JsVar *data) { + if (!jsvIsString(data)) { + jsExceptionHere(JSET_TYPEERROR, "Expecting a String"); + return 0; + } + int x=0,y=0; + int width=0, height=0; + size_t startCharacter = 0; + JsvStringIterator it; + // First iterate and work out width and height + jsvStringIteratorNew(&it,data,0); + while (jsvStringIteratorHasChar(&it)) { + char ch = jsvStringIteratorGetChar(&it); + if (ch=='\n') { + if (x==0 && y==0) startCharacter = 1; // ignore first character + x=0; + y++; + } else { + if (y>=height) height=y+1; + x++; + if (x>width) width=x; + } + jsvStringIteratorNext(&it); + } + jsvStringIteratorFree(&it); + // Sorted - now create the object, set it up and create the buffer + JsVar *img = jsvNewObject(); + if (!img) return 0; + jsvObjectSetChildAndUnLock(img,"width",jsvNewFromInteger(width)); + jsvObjectSetChildAndUnLock(img,"height",jsvNewFromInteger(height)); + // bpp is 1, no need to set it + int len = (width*height+7)>>3; + JsVar *buffer = jsvNewStringOfLength((unsigned)len, NULL); + if (!buffer) { // not enough memory + jsvUnLock(img); + return 0; + } + // Now set the characters! + x=0; + y=0; + jsvStringIteratorNew(&it,data,startCharacter); + while (jsvStringIteratorHasChar(&it)) { + char ch = jsvStringIteratorGetChar(&it); + if (ch=='\n') { + x=0; + y++; + } else { + if (ch!=' ') { + /* a pixel to set. This'll be slowish for non-flat strings, + * but this is here for relatively small bitmaps + * anyway so it's not a big deal */ + size_t idx = (size_t)(y*width + x); + jsvSetCharInString(buffer, idx>>3, (char)(128>>(idx&7)), true/*OR*/); + } + x++; + } + jsvStringIteratorNext(&it); + } + jsvStringIteratorFree(&it); + jsvObjectSetChildAndUnLock(img, "buffer", buffer); + return img; +} + /*JSON{ "type" : "method", "class" : "Graphics", @@ -283,14 +405,21 @@ int jswrap_graphics_getWidthOrHeight(JsVar *parent, bool height) { "type" : "method", "class" : "Graphics", "name" : "clear", - "generate" : "jswrap_graphics_clear" + "generate" : "jswrap_graphics_clear", + "params" : [ + ["reset","bool","If `true`, resets the state of Graphics to the default (eg. Color, Font, etc)"] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Clear the LCD with the Background Color */ -void jswrap_graphics_clear(JsVar *parent) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_clear(JsVar *parent, bool resetState) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; + if (resetState) graphicsStructResetState(&gfx); graphicsClear(&gfx); graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); } /*JSON{ @@ -299,18 +428,21 @@ void jswrap_graphics_clear(JsVar *parent) { "name" : "fillRect", "generate" : "jswrap_graphics_fillRect", "params" : [ - ["x1","int32","The left"], - ["y1","int32","The top"], - ["x2","int32","The right"], - ["y2","int32","The bottom"] - ] + ["x1","int32","The left X coordinate"], + ["y1","int32","The top Y coordinate"], + ["x2","int32","The right X coordinate"], + ["y2","int32","The bottom Y coordinate"] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Fill a rectangular area in the Foreground Color */ -void jswrap_graphics_fillRect(JsVar *parent, int x1, int y1, int x2, int y2) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_fillRect(JsVar *parent, int x1, int y1, int x2, int y2) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; graphicsFillRect(&gfx, (short)x1,(short)y1,(short)x2,(short)y2); graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); } /*JSON{ @@ -319,59 +451,114 @@ void jswrap_graphics_fillRect(JsVar *parent, int x1, int y1, int x2, int y2) { "name" : "drawRect", "generate" : "jswrap_graphics_drawRect", "params" : [ - ["x1","int32","The left"], - ["y1","int32","The top"], - ["x2","int32","The right"], - ["y2","int32","The bottom"] - ] + ["x1","int32","The left X coordinate"], + ["y1","int32","The top Y coordinate"], + ["x2","int32","The right X coordinate"], + ["y2","int32","The bottom Y coordinate"] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Draw an unfilled rectangle 1px wide in the Foreground Color */ -void jswrap_graphics_drawRect(JsVar *parent, int x1, int y1, int x2, int y2) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_drawRect(JsVar *parent, int x1, int y1, int x2, int y2) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; graphicsDrawRect(&gfx, (short)x1,(short)y1,(short)x2,(short)y2); graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); } /*JSON{ "type" : "method", "class" : "Graphics", "name" : "fillCircle", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_graphics_fillCircle", "params" : [ ["x","int32","The X axis"], ["y","int32","The Y axis"], ["rad","int32","The circle radius"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Draw a filled circle in the Foreground Color */ - void jswrap_graphics_fillCircle(JsVar *parent, int x, int y, int rad) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; - graphicsFillCircle(&gfx, (short)x,(short)y,(short)rad); - graphicsSetVar(&gfx); // gfx data changed because modified area + JsVar *jswrap_graphics_fillCircle(JsVar *parent, int x, int y, int rad) { + jswrap_graphics_fillEllipse(parent, x-rad, y-rad, x+rad, y+rad); + return jsvLockAgain(parent); } /*JSON{ "type" : "method", "class" : "Graphics", "name" : "drawCircle", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_graphics_drawCircle", "params" : [ ["x","int32","The X axis"], ["y","int32","The Y axis"], ["rad","int32","The circle radius"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Draw an unfilled circle 1px wide in the Foreground Color */ -void jswrap_graphics_drawCircle(JsVar *parent, int x, int y, int rad) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; - graphicsDrawCircle(&gfx, (short)x,(short)y,(short)rad); - graphicsSetVar(&gfx); // gfx data changed because modified area +JsVar *jswrap_graphics_drawCircle(JsVar *parent, int x, int y, int rad) { + jswrap_graphics_drawEllipse(parent, x-rad, y-rad, x+rad, y+rad); + return jsvLockAgain(parent); } /*JSON{ + "type" : "method", + "class" : "Graphics", + "name" : "fillEllipse", + "ifndef" : "SAVE_ON_FLASH", + "generate" : "jswrap_graphics_fillEllipse", + "params" : [ + ["x1","int32","The left X coordinate"], + ["y1","int32","The top Y coordinate"], + ["x2","int32","The right X coordinate"], + ["y2","int32","The bottom Y coordinate"] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" +} +Draw a filled ellipse in the Foreground Color +*/ +JsVar *jswrap_graphics_fillEllipse(JsVar *parent, int x, int y, int x2, int y2) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; + graphicsFillEllipse(&gfx, (short)x,(short)y,(short)x2,(short)y2); + graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); + } + +/*JSON{ + "type" : "method", + "class" : "Graphics", + "name" : "drawEllipse", + "ifndef" : "SAVE_ON_FLASH", + "generate" : "jswrap_graphics_drawEllipse", + "params" : [ + ["x1","int32","The left X coordinate"], + ["y1","int32","The top Y coordinate"], + ["x2","int32","The right X coordinate"], + ["y2","int32","The bottom Y coordinate"] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" +} +Draw an ellipse in the Foreground Color +*/ +JsVar *jswrap_graphics_drawEllipse(JsVar *parent, int x, int y, int x2, int y2) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; + graphicsDrawEllipse(&gfx, (short)x,(short)y,(short)x2,(short)y2); + graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); + } + + /*JSON{ "type" : "method", "class" : "Graphics", "name" : "getPixel", @@ -398,12 +585,14 @@ int jswrap_graphics_getPixel(JsVar *parent, int x, int y) { ["x","int32","The left"], ["y","int32","The top"], ["col","JsVar","The color"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Set a pixel's color */ -void jswrap_graphics_setPixel(JsVar *parent, int x, int y, JsVar *color) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_setPixel(JsVar *parent, int x, int y, JsVar *color) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; unsigned int col = gfx.data.fgColor; if (!jsvIsUndefined(color)) col = (unsigned int)jsvGetInteger(color); @@ -411,6 +600,7 @@ void jswrap_graphics_setPixel(JsVar *parent, int x, int y, JsVar *color) { gfx.data.cursorX = (short)x; gfx.data.cursorY = (short)y; graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); } /*JSON{ @@ -422,9 +612,25 @@ void jswrap_graphics_setPixel(JsVar *parent, int x, int y, JsVar *color) { ["r","JsVar","Red (between 0 and 1) OR an integer representing the color in the current bit depth and color order"], ["g","JsVar","Green (between 0 and 1)"], ["b","JsVar","Blue (between 0 and 1)"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } -Set the color to use for subsequent drawing operations +Set the color to use for subsequent drawing operations. + +If just `r` is specified as an integer, the numeric value will be written directly into a pixel. eg. On a 24 bit `Graphics` instance you set bright blue with either `g.setColor(0,0,1)` or `g.setColor(0x0000FF)`. + +The mapping is as follows: + +* 32 bit: `r,g,b` => `0xFFrrggbb` +* 24 bit: `r,g,b` => `0xrrggbb` +* 16 bit: `r,g,b` => `0brrrrrggggggbbbbb` (RGB565) +* Other bpp: `r,g,b` => white if `r+g+b > 50%`, otherwise black (use `r` on its own as an integer) + +If you specified `color_order` when creating the `Graphics` instance, `r`,`g` and `b` will be swapped as you specified. + +**Note:** On devices with low flash memory, `r` **must** be an integer representing the color in the current bit depth. It cannot +be a floating point value, and `g` and `b` are ignored. */ /*JSON{ "type" : "method", @@ -435,13 +641,23 @@ Set the color to use for subsequent drawing operations ["r","JsVar","Red (between 0 and 1) OR an integer representing the color in the current bit depth and color order"], ["g","JsVar","Green (between 0 and 1)"], ["b","JsVar","Blue (between 0 and 1)"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } -Set the background color to use for subsequent drawing operations +Set the background color to use for subsequent drawing operations. + +See `Graphics.setColor` for more information on the mapping of `r`, `g`, and `b` to pixel values. + +**Note:** On devices with low flash memory, `r` **must** be an integer representing the color in the current bit depth. It cannot +be a floating point value, and `g` and `b` are ignored. */ -void jswrap_graphics_setColorX(JsVar *parent, JsVar *r, JsVar *g, JsVar *b, bool isForeground) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_setColorX(JsVar *parent, JsVar *r, JsVar *g, JsVar *b, bool isForeground) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; unsigned int color = 0; +#ifdef SAVE_ON_FLASH + if (false) { +#else JsVarFloat rf, gf, bf; rf = jsvGetFloat(r); gf = jsvGetFloat(g); @@ -497,6 +713,7 @@ void jswrap_graphics_setColorX(JsVar *parent, JsVar *r, JsVar *g, JsVar *b, bool color = (unsigned int)(bi | (gi<<8) | (ri<<16)); } else color = (unsigned int)(((ri+gi+bi)>=384) ? 0xFFFFFFFF : 0); +#endif } else { // just rgb color = (unsigned int)jsvGetInteger(r); @@ -506,6 +723,7 @@ void jswrap_graphics_setColorX(JsVar *parent, JsVar *r, JsVar *g, JsVar *b, bool else gfx.data.bgColor = color; graphicsSetVar(&gfx); + return jsvLockAgain(parent); } /*JSON{ @@ -535,7 +753,9 @@ JsVarInt jswrap_graphics_getColorX(JsVar *parent, bool isForeground) { "type" : "method", "class" : "Graphics", "name" : "setFontBitmap", - "generate_full" : "jswrap_graphics_setFontSizeX(parent, JSGRAPHICS_FONTSIZE_4X6, false)" + "generate_full" : "jswrap_graphics_setFontSizeX(parent, JSGRAPHICS_FONTSIZE_4X6, false)", + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Make subsequent calls to `drawString` use the built-in 4x6 pixel bitmapped Font */ @@ -547,12 +767,14 @@ Make subsequent calls to `drawString` use the built-in 4x6 pixel bitmapped Font "generate_full" : "jswrap_graphics_setFontSizeX(parent, size, true)", "params" : [ ["size","int32","The height of the font, as an integer"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Make subsequent calls to `drawString` use a Vector Font of the given height */ -void jswrap_graphics_setFontSizeX(JsVar *parent, int size, bool checkValid) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_setFontSizeX(JsVar *parent, int size, bool checkValid) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; if (checkValid) { if (size<1) size=1; @@ -566,6 +788,7 @@ void jswrap_graphics_setFontSizeX(JsVar *parent, int size, bool checkValid) { } gfx.data.fontSize = (short)size; graphicsSetVar(&gfx); + return jsvLockAgain(parent); } /*JSON{ "type" : "method", @@ -577,29 +800,31 @@ void jswrap_graphics_setFontSizeX(JsVar *parent, int size, bool checkValid) { ["firstChar","int32","The first character in the font - usually 32 (space)"], ["width","JsVar","The width of each character in the font. Either an integer, or a string where each character represents the width"], ["height","int32","The height as an integer"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Make subsequent calls to `drawString` use a Custom Font of the given height. See the [Fonts page](http://www.espruino.com/Fonts) for more information about custom fonts and how to create them. */ -void jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar, JsVar *width, int height) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar, JsVar *width, int height) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; if (!jsvIsString(bitmap)) { jsExceptionHere(JSET_ERROR, "Font bitmap must be a String"); - return; + return 0; } if (firstChar<0 || firstChar>255) { jsExceptionHere(JSET_ERROR, "First character out of range"); - return; + return 0; } if (!jsvIsString(width) && !jsvIsInt(width)) { jsExceptionHere(JSET_ERROR, "Font width must be a String or an integer"); - return; + return 0; } if (height<=0 || height>255) { jsExceptionHere(JSET_ERROR, "Invalid height"); - return; + return 0; } jsvObjectSetChild(parent, JSGRAPHICS_CUSTOMFONT_BMP, bitmap); jsvObjectSetChild(parent, JSGRAPHICS_CUSTOMFONT_WIDTH, width); @@ -607,8 +832,42 @@ void jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar, jsvObjectSetChildAndUnLock(parent, JSGRAPHICS_CUSTOMFONT_FIRSTCHAR, jsvNewFromInteger(firstChar)); gfx.data.fontSize = JSGRAPHICS_FONTSIZE_CUSTOM; graphicsSetVar(&gfx); + return jsvLockAgain(parent); +} +/*JSON{ + "type" : "method", + "class" : "Graphics", + "name" : "setFontAlign", + "ifndef" : "SAVE_ON_FLASH", + "generate" : "jswrap_graphics_setFontAlign", + "params" : [ + ["x","int32","X alignment. -1=left (default), 0=center, 1=right"], + ["y","int32","Y alignment. -1=top (default), 0=center, 1=bottom"], + ["rotation","int32","Rotation of the text. 0=normal, 1=90 degrees clockwise, 2=180, 3=270"] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" +} +Set the alignment for subsequent calls to `drawString` +*/ +JsVar *jswrap_graphics_setFontAlign(JsVar *parent, int x, int y, int r) { +#ifndef SAVE_ON_FLASH + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; + if (x<-1) x=-1; + if (x>1) x=1; + if (y<-1) y=-1; + if (y>1) y=1; + if (r<0) r=0; + if (r>3) r=3; + gfx.data.fontAlignX = x; + gfx.data.fontAlignY = y; + gfx.data.fontRotate = r; + graphicsSetVar(&gfx); + return jsvLockAgain(parent); +#else + return 0; +#endif } - /*JSON{ "type" : "method", @@ -619,14 +878,15 @@ void jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar, ["str","JsVar","The string"], ["x","int32","The X position of the leftmost pixel"], ["y","int32","The Y position of the topmost pixel"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Draw a string of text in the current font */ -void jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; - int startx = x; JsVar *customBitmap = 0, *customWidth = 0; int customHeight = 0, customFirstChar = 0; if (gfx.data.fontSize>0) { @@ -639,10 +899,35 @@ void jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y) { customHeight = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(parent, JSGRAPHICS_CUSTOMFONT_HEIGHT, 0)); customFirstChar = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(parent, JSGRAPHICS_CUSTOMFONT_FIRSTCHAR, 0)); } +#ifndef SAVE_ON_FLASH + // Handle text rotation + JsGraphicsFlags oldFlags = gfx.data.flags; + if (gfx.data.fontRotate==1) { + gfx.data.flags ^= JSGRAPHICSFLAGS_SWAP_XY | JSGRAPHICSFLAGS_INVERT_X; + int t = gfx.data.width - (x+1); + x = y; + y = t; + } else if (gfx.data.fontRotate==2) { + gfx.data.flags ^= JSGRAPHICSFLAGS_INVERT_X | JSGRAPHICSFLAGS_INVERT_Y; + x = gfx.data.width - (x+1); + y = gfx.data.height - (y+1); + } else if (gfx.data.fontRotate==3) { + gfx.data.flags ^= JSGRAPHICSFLAGS_SWAP_XY | JSGRAPHICSFLAGS_INVERT_Y; + int t = gfx.data.height - (y+1); + y = x; + x = t; + } + // Handle font alignment + if (gfx.data.fontAlignX<2) // 0=center, 1=right, 2=undefined, 3=left + x -= jswrap_graphics_stringWidth(parent, var) * (gfx.data.fontAlignX+1)/2; + if (gfx.data.fontAlignY<2) // 0=center, 1=bottom, 2=undefined, 3=top + y -= customHeight * (gfx.data.fontAlignX+1)/2; +#endif int maxX = (gfx.data.flags & JSGRAPHICSFLAGS_SWAP_XY) ? gfx.data.height : gfx.data.width; int maxY = (gfx.data.flags & JSGRAPHICSFLAGS_SWAP_XY) ? gfx.data.width : gfx.data.height; - JsVar *str = jsvAsString(var, false); + int startx = x; + JsVar *str = jsvAsString(var); JsvStringIterator it; jsvStringIteratorNew(&it, str, 0); while (jsvStringIteratorHasChar(&it)) { @@ -709,7 +994,11 @@ void jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y) { } jsvStringIteratorFree(&it); jsvUnLock3(str, customBitmap, customWidth); +#ifndef SAVE_ON_FLASH + gfx.data.flags = oldFlags; // restore flags because of text rotation graphicsSetVar(&gfx); // gfx data changed because modified area +#endif + return jsvLockAgain(parent); } /*JSON{ @@ -734,12 +1023,17 @@ JsVarInt jswrap_graphics_stringWidth(JsVar *parent, JsVar *var) { customFirstChar = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(parent, JSGRAPHICS_CUSTOMFONT_FIRSTCHAR, 0)); } - JsVar *str = jsvAsString(var, false); + JsVar *str = jsvAsString(var); JsvStringIterator it; jsvStringIteratorNew(&it, str, 0); int width = 0; + int maxWidth = 0; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); + if (ch=='\n') { + if (width>maxWidth) maxWidth=width; + width = 0; + } if (gfx.data.fontSize>0) { #ifndef SAVE_ON_FLASH width += (int)graphicsVectorCharWidth(&gfx, gfx.data.fontSize, ch); @@ -757,7 +1051,7 @@ JsVarInt jswrap_graphics_stringWidth(JsVar *parent, JsVar *var) { } jsvStringIteratorFree(&it); jsvUnLock2(str, customWidth); - return width; + return width>maxWidth ? width : maxWidth; } /*JSON{ @@ -770,14 +1064,17 @@ JsVarInt jswrap_graphics_stringWidth(JsVar *parent, JsVar *var) { ["y1","int32","The top"], ["x2","int32","The right"], ["y2","int32","The bottom"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Draw a line between x1,y1 and x2,y2 in the current foreground color */ -void jswrap_graphics_drawLine(JsVar *parent, int x1, int y1, int x2, int y2) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_drawLine(JsVar *parent, int x1, int y1, int x2, int y2) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; graphicsDrawLine(&gfx, (short)x1,(short)y1,(short)x2,(short)y2); graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); } /*JSON{ @@ -788,16 +1085,19 @@ void jswrap_graphics_drawLine(JsVar *parent, int x1, int y1, int x2, int y2) { "params" : [ ["x","int32","X value"], ["y","int32","Y value"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Draw a line from the last position of lineTo or moveTo to this position */ -void jswrap_graphics_lineTo(JsVar *parent, int x, int y) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_lineTo(JsVar *parent, int x, int y) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; graphicsDrawLine(&gfx, gfx.data.cursorX, gfx.data.cursorY, (short)x, (short)y); gfx.data.cursorX = (short)x; gfx.data.cursorY = (short)y; graphicsSetVar(&gfx); + return jsvLockAgain(parent); } /*JSON{ @@ -808,31 +1108,86 @@ void jswrap_graphics_lineTo(JsVar *parent, int x, int y) { "params" : [ ["x","int32","X value"], ["y","int32","Y value"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Move the cursor to a position - see lineTo */ -void jswrap_graphics_moveTo(JsVar *parent, int x, int y) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_moveTo(JsVar *parent, int x, int y) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; gfx.data.cursorX = (short)x; gfx.data.cursorY = (short)y; graphicsSetVar(&gfx); + return jsvLockAgain(parent); +} + +/*JSON{ + "type" : "method", + "class" : "Graphics", + "name" : "drawPoly", + "ifndef" : "SAVE_ON_FLASH", + "generate" : "jswrap_graphics_drawPoly", + "params" : [ + ["poly","JsVar","An array of vertices, of the form ```[x1,y1,x2,y2,x3,y3,etc]```"], + ["closed","bool","Draw another line between the last element of the array and the first"] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" +} +Draw a polyline (lines between each of the points in `poly`) in the current foreground color +*/ +JsVar *jswrap_graphics_drawPoly(JsVar *parent, JsVar *poly, bool closed) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; + if (!jsvIsIterable(poly)) return 0; + int x,y; + int startx, starty; + int idx = 0; + JsvIterator it; + jsvIteratorNew(&it, poly, JSIF_EVERY_ARRAY_ELEMENT); + while (jsvIteratorHasElement(&it)) { + int el = jsvIteratorGetIntegerValue(&it); + if (idx&1) { + y = el; + if (idx==1) { // save xy positions of first point + startx = x; + starty = y; + } else { + // only start drawing between the first 2 points + graphicsDrawLine(&gfx, gfx.data.cursorX, gfx.data.cursorY, (short)x, (short)y); + } + gfx.data.cursorX = (short)x; + gfx.data.cursorY = (short)y; + } else x = el; + idx++; + jsvIteratorNext(&it); + } + jsvIteratorFree(&it); + // if closed, draw between first and last points + if (closed) + graphicsDrawLine(&gfx, gfx.data.cursorX, gfx.data.cursorY, (short)startx, (short)starty); + + graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); } /*JSON{ "type" : "method", "class" : "Graphics", "name" : "fillPoly", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_graphics_fillPoly", "params" : [ ["poly","JsVar","An array of vertices, of the form ```[x1,y1,x2,y2,x3,y3,etc]```"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Draw a filled polygon in the current foreground color */ -void jswrap_graphics_fillPoly(JsVar *parent, JsVar *poly) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; - if (!jsvIsIterable(poly)) return; +JsVar *jswrap_graphics_fillPoly(JsVar *parent, JsVar *poly) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; + if (!jsvIsIterable(poly)) return 0; const int maxVerts = 128; short verts[maxVerts]; int idx = 0; @@ -847,7 +1202,9 @@ void jswrap_graphics_fillPoly(JsVar *parent, JsVar *poly) { jsWarn("Maximum number of points (%d) exceeded for fillPoly", maxVerts/2); } graphicsFillPoly(&gfx, idx/2, verts); + graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); } /*JSON{ @@ -858,12 +1215,14 @@ void jswrap_graphics_fillPoly(JsVar *parent, JsVar *poly) { "params" : [ ["rotation","int32","The clockwise rotation. 0 for no rotation, 1 for 90 degrees, 2 for 180, 3 for 270"], ["reflect","bool","Whether to reflect the image"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Set the current rotation of the graphics device. */ -void jswrap_graphics_setRotation(JsVar *parent, int rotation, bool reflect) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_setRotation(JsVar *parent, int rotation, bool reflect) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; // clear flags gfx.data.flags &= (JsGraphicsFlags)~(JSGRAPHICSFLAGS_SWAP_XY | JSGRAPHICSFLAGS_INVERT_X | JSGRAPHICSFLAGS_INVERT_Y); @@ -890,6 +1249,7 @@ void jswrap_graphics_setRotation(JsVar *parent, int rotation, bool reflect) { } graphicsSetVar(&gfx); + return jsvLockAgain(parent); } /*JSON{ @@ -898,37 +1258,43 @@ void jswrap_graphics_setRotation(JsVar *parent, int rotation, bool reflect) { "name" : "drawImage", "generate" : "jswrap_graphics_drawImage", "params" : [ - ["image","JsVar","An object with the following fields `{ width : int, height : int, bpp : int, buffer : ArrayBuffer, transparent: optional int }`. bpp = bits per pixel, transparent (if defined) is the colour that will be treated as transparent"], + ["image","JsVar","An object with the following fields `{ width : int, height : int, bpp : optional int, buffer : ArrayBuffer/String, transparent: optional int }`. bpp = bits per pixel (default is 1), transparent (if defined) is the colour that will be treated as transparent"], ["x","int32","The X offset to draw the image"], ["y","int32","The Y offset to draw the image"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Draw an image at the specified position. If the image is 1 bit, the graphics foreground/background colours will be used. Otherwise color data will be copied as-is. Bitmaps are rendered MSB-first */ -void jswrap_graphics_drawImage(JsVar *parent, JsVar *image, int xPos, int yPos) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_drawImage(JsVar *parent, JsVar *image, int xPos, int yPos) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; if (!jsvIsObject(image)) { jsExceptionHere(JSET_ERROR, "Expecting first argument to be an object"); - return; + return 0; } int imageWidth = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(image, "width", 0)); int imageHeight = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(image, "height", 0)); int imageBpp = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(image, "bpp", 0)); + if (imageBpp<=0) imageBpp=1; unsigned int imageBitMask = (unsigned int)((1L<0 && imageHeight>0 && imageBpp>0 && imageBpp<=32)) { + if (!(jsvIsArrayBuffer(imageBuffer) || jsvIsString(imageBuffer)) || + imageWidth<=0 || + imageHeight<=0 || + imageBpp>32) { jsExceptionHere(JSET_ERROR, "Expecting first argument to a valid Image"); jsvUnLock(imageBuffer); - return; + return 0; } + // jsvGetArrayBufferBackingString is fine to be passed a string JsVar *imageBufferString = jsvGetArrayBufferBackingString(imageBuffer); jsvUnLock(imageBuffer); - int x=0, y=0; int bits=0; unsigned int colData = 0; @@ -962,12 +1328,66 @@ void jswrap_graphics_drawImage(JsVar *parent, JsVar *image, int xPos, int yPos) jsvStringIteratorFree(&it); jsvUnLock(imageBufferString); graphicsSetVar(&gfx); // gfx data changed because modified area + return jsvLockAgain(parent); +} + +/*JSON{ + "type" : "method", + "class" : "Graphics", + "name" : "asImage", + "ifndef" : "SAVE_ON_FLASH", + "generate" : "jswrap_graphics_asImage", + "return" : ["JsVar","An Image that can be used with `Graphics.drawImage`"] +} +Return this Graphics object as an Image that can be used with `Graphics.drawImage`. +Will return undefined if data can't be allocated for it. +*/ +JsVar *jswrap_graphics_asImage(JsVar *parent) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; + JsVar *img = jsvNewObject(); + if (!img) return 0; + int w = jswrap_graphics_getWidthOrHeight(parent,false); + int h = jswrap_graphics_getWidthOrHeight(parent,true); + int bpp = gfx.data.bpp; + jsvObjectSetChildAndUnLock(img,"width",jsvNewFromInteger(w)); + jsvObjectSetChildAndUnLock(img,"height",jsvNewFromInteger(h)); + if (bpp!=1) jsvObjectSetChildAndUnLock(img,"bpp",jsvNewFromInteger(bpp)); + int len = (w*h*bpp+7)>>3; + JsVar *buffer = jsvNewStringOfLength((unsigned)len, NULL); + if (!buffer) { // not enough memory + jsvUnLock(img); + return 0; + } + + int x=0, y=0; + unsigned int pixelBits = 0; + unsigned int pixelBitCnt = 0; + JsvStringIterator it; + jsvStringIteratorNew(&it, buffer, 0); + while (jsvStringIteratorHasChar(&it)) { + pixelBits = (pixelBits<=w) { + x=0; + y++; + } + while (pixelBitCnt>=8) { + jsvStringIteratorSetCharAndNext(&it, (char)(pixelBits>>(pixelBitCnt-8))); + pixelBits = pixelBits>>8; + pixelBitCnt -= 8; + } + } + jsvStringIteratorFree(&it); + jsvObjectSetChildAndUnLock(img,"buffer",buffer); + return img; } /*JSON{ "type" : "method", "class" : "Graphics", "name" : "getModified", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_graphics_getModified", "params" : [ ["reset","bool","Whether to reset the modified area or not"] @@ -980,6 +1400,7 @@ the modified area to 0. For instance if `g.setPixel(10,20)` was called, this would return `{x1:10, y1:20, x2:10, y2:20}` */ JsVar *jswrap_graphics_getModified(JsVar *parent, bool reset) { +#ifndef SAVE_ON_FLASH JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; JsVar *obj = 0; if (gfx.data.modMinX <= gfx.data.modMaxX) { // do we have a rect? @@ -999,17 +1420,23 @@ JsVar *jswrap_graphics_getModified(JsVar *parent, bool reset) { graphicsSetVar(&gfx); } return obj; +#else + return 0; +#endif } /*JSON{ "type" : "method", "class" : "Graphics", "name" : "scroll", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_graphics_scroll", "params" : [ ["x","int32","X direction. >0 = to right"], ["y","int32","Y direction. >0 = down"] - ] + ], + "return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"], + "return_object" : "Graphics" } Scroll the contents of this graphics in a certain direction. The remaining area is filled with the background color. @@ -1017,9 +1444,115 @@ is filled with the background color. Note: This uses repeated pixel reads and writes, so will not work on platforms that don't support pixel reads. */ -void jswrap_graphics_scroll(JsVar *parent, int xdir, int ydir) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +JsVar *jswrap_graphics_scroll(JsVar *parent, int xdir, int ydir) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; graphicsScroll(&gfx, xdir, ydir); // update modified area graphicsSetVar(&gfx); + return jsvLockAgain(parent); +} + +/*JSON{ + "type" : "method", + "class" : "Graphics", + "name" : "asBMP", + "#if" : "!defined(SAVE_ON_FLASH) && !defined(ESPRUINOBOARD)", + "generate" : "jswrap_graphics_asBMP", + "return" : ["JsVar","A String representing the Graphics as a Windows BMP file (or 'undefined' if not possible)"] +} +Create a Windows BMP file from this Graphics instance, and return it as a String. +*/ +JsVar *jswrap_graphics_asBMP(JsVar *parent) { + JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0; + if (gfx.data.bpp!=1 && gfx.data.bpp!=24) { + jsExceptionHere(JSET_ERROR, "asBMP/asURL only works on 1bpp/24bpp Graphics"); + return 0; + } + int rowstride = (((gfx.data.width*gfx.data.bpp)+31) >> 5) << 2; // padded to 32 bits + bool hasPalette = gfx.data.bpp==1; + int headerLen = 14+ 12+ (hasPalette?6:0); + int l = headerLen + gfx.data.height*rowstride; + JsVar *imgData = jsvNewFlatStringOfLength((unsigned)l); + if (!imgData) return 0; // not enough memory + unsigned char *imgPtr = (unsigned char *)jsvGetFlatStringPointer(imgData); + imgPtr[0]=66; + imgPtr[1]=77; + imgPtr[2]=(unsigned char)l; + imgPtr[3]=(unsigned char)(l>>8); // plus 2 more bytes for size + imgPtr[10]=(unsigned char)headerLen; + // BITMAPCOREHEADER + imgPtr[14]=12; // sizeof(BITMAPCOREHEADER) + imgPtr[18]=(unsigned char)gfx.data.width; + imgPtr[19]=(unsigned char)(gfx.data.width>>8); + imgPtr[20]=(unsigned char)gfx.data.height; + imgPtr[21]=(unsigned char)(gfx.data.height>>8); + imgPtr[22]=1; + imgPtr[24]=(unsigned char)gfx.data.bpp; // bpp + if (hasPalette) { + imgPtr[26]=255; + imgPtr[27]=255; + imgPtr[28]=255; + } + for (int y=0;y>3) - 1] = (unsigned char)b; + } + } else { + for (int x=0;x>3)); + imgPtr[i++] = (unsigned char)(c); + imgPtr[i++] = (unsigned char)(c>>8); + imgPtr[i++] = (unsigned char)(c>>16); + } + } + } + return imgData; +} + +/*JSON{ + "type" : "method", + "class" : "Graphics", + "name" : "asURL", + "#if" : "!defined(SAVE_ON_FLASH) && !defined(ESPRUINOBOARD)", + "generate" : "jswrap_graphics_asURL", + "return" : ["JsVar","A String representing the Graphics as a URL (or 'undefined' if not possible)"] +} +Create a URL of the form `data:image/bmp;base64,...` that can be pasted into the browser. + +The Espruino Web IDE can detect this data on the console and render the image inline automatically. +*/ +JsVar *jswrap_graphics_asURL(JsVar *parent) { + JsVar *imgData = jswrap_graphics_asBMP(parent); + if (!imgData) return 0; // not enough memory + JsVar *b64 = jswrap_btoa(imgData); + jsvUnLock(imgData); + if (!b64) return 0; // not enough memory + JsVar *r = jsvVarPrintf("data:image/bmp;base64,%v",b64); + jsvUnLock(b64); + return r; +} + +/*JSON{ + "type" : "method", + "class" : "Graphics", + "name" : "dump", + "#if" : "!defined(SAVE_ON_FLASH) && !defined(ESPRUINOBOARD)", + "generate" : "jswrap_graphics_dump" +} +Output this image as a bitmap URL. The Espruino Web IDE can detect the data on the console and render the image inline automatically. + +This is identical to `console.log(g.asURL())` - it is just a convenient function for easy debugging. +*/ +void jswrap_graphics_dump(JsVar *parent) { + JsVar *url = jswrap_graphics_asURL(parent); + if (url) jsiConsolePrintStringVar(url); + jsvUnLock(url); + jsiConsolePrint("\n"); } diff --git a/libs/graphics/jswrap_graphics.h b/libs/graphics/jswrap_graphics.h index 218b0aeea..b85370fed 100644 --- a/libs/graphics/jswrap_graphics.h +++ b/libs/graphics/jswrap_graphics.h @@ -21,33 +21,43 @@ bool jswrap_graphics_idle(); void jswrap_graphics_init(); +JsVar *jswrap_graphics_getInstance(); // For creating graphics classes JsVar *jswrap_graphics_createArrayBuffer(int width, int height, int bpp, JsVar *options); JsVar *jswrap_graphics_createCallback(int width, int height, int bpp, JsVar *callback); #ifdef USE_LCD_SDL JsVar *jswrap_graphics_createSDL(int width, int height); #endif +JsVar *jswrap_graphics_createImage(JsVar *data); int jswrap_graphics_getWidthOrHeight(JsVar *parent, bool height); -void jswrap_graphics_clear(JsVar *parent); -void jswrap_graphics_fillRect(JsVar *parent, int x1, int y1, int x2, int y2); -void jswrap_graphics_drawRect(JsVar *parent, int x1, int y1, int x2, int y2); -void jswrap_graphics_drawCircle(JsVar *parent, int x, int y, int rad); -void jswrap_graphics_fillCircle(JsVar *parent, int x, int y, int rad); +JsVar *jswrap_graphics_clear(JsVar *parent, bool resetState); +JsVar *jswrap_graphics_fillRect(JsVar *parent, int x1, int y1, int x2, int y2); +JsVar *jswrap_graphics_drawRect(JsVar *parent, int x1, int y1, int x2, int y2); +JsVar *jswrap_graphics_drawCircle(JsVar *parent, int x, int y, int rad); +JsVar *jswrap_graphics_fillCircle(JsVar *parent, int x, int y, int rad); +JsVar *jswrap_graphics_drawEllipse(JsVar *parent, int x, int y, int x2, int y2); +JsVar *jswrap_graphics_fillEllipse(JsVar *parent, int x, int y, int x2, int y2); int jswrap_graphics_getPixel(JsVar *parent, int x, int y); -void jswrap_graphics_setPixel(JsVar *parent, int x, int y, JsVar *color); -void jswrap_graphics_setColorX(JsVar *parent, JsVar *r, JsVar *g, JsVar *b, bool isForeground); +JsVar *jswrap_graphics_setPixel(JsVar *parent, int x, int y, JsVar *color); +JsVar *jswrap_graphics_setColorX(JsVar *parent, JsVar *r, JsVar *g, JsVar *b, bool isForeground); JsVarInt jswrap_graphics_getColorX(JsVar *parent, bool isForeground); -void jswrap_graphics_setFontSizeX(JsVar *parent, int size, bool checkValid); -void jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar, JsVar *width, int height); -void jswrap_graphics_drawString(JsVar *parent, JsVar *str, int x, int y); +JsVar *jswrap_graphics_setFontSizeX(JsVar *parent, int size, bool checkValid); +JsVar *jswrap_graphics_setFontCustom(JsVar *parent, JsVar *bitmap, int firstChar, JsVar *width, int height); +JsVar *jswrap_graphics_setFontAlign(JsVar *parent, int x, int y, int r); +JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *str, int x, int y); JsVarInt jswrap_graphics_stringWidth(JsVar *parent, JsVar *var); -void jswrap_graphics_drawLine(JsVar *parent, int x1, int y1, int x2, int y2); -void jswrap_graphics_lineTo(JsVar *parent, int x, int y); -void jswrap_graphics_moveTo(JsVar *parent, int x, int y); -void jswrap_graphics_fillPoly(JsVar *parent, JsVar *poly); -void jswrap_graphics_setRotation(JsVar *parent, int rotation, bool reflect); -void jswrap_graphics_drawImage(JsVar *parent, JsVar *image, int xPos, int yPos); +JsVar *jswrap_graphics_drawLine(JsVar *parent, int x1, int y1, int x2, int y2); +JsVar *jswrap_graphics_lineTo(JsVar *parent, int x, int y); +JsVar *jswrap_graphics_moveTo(JsVar *parent, int x, int y); +JsVar *jswrap_graphics_drawPoly(JsVar *parent, JsVar *poly, bool closed); +JsVar *jswrap_graphics_fillPoly(JsVar *parent, JsVar *poly); +JsVar *jswrap_graphics_setRotation(JsVar *parent, int rotation, bool reflect); +JsVar *jswrap_graphics_drawImage(JsVar *parent, JsVar *image, int xPos, int yPos); +JsVar *jswrap_graphics_asImage(JsVar *parent); JsVar *jswrap_graphics_getModified(JsVar *parent, bool reset); -void jswrap_graphics_scroll(JsVar *parent, int x, int y); +JsVar *jswrap_graphics_scroll(JsVar *parent, int x, int y); +JsVar *jswrap_graphics_asBMP(JsVar *parent); +JsVar *jswrap_graphics_asURL(JsVar *parent); +void jswrap_graphics_dump(JsVar *parent); diff --git a/libs/graphics/jswrap_terminal.c b/libs/graphics/jswrap_terminal.c index d59b048d2..364b1e5fa 100644 --- a/libs/graphics/jswrap_terminal.c +++ b/libs/graphics/jswrap_terminal.c @@ -28,37 +28,11 @@ } A simple VT100 terminal emulator. -When data is sent to this, it searches for a Graphics variable called either -`g` or `LCD` and starts writing characters to it. +When data is sent to the `Terminal` object, `Graphics.getInstance()` +is called and if an instance of `Graphics` is found then characters +are written to it. */ - -/* We can't add properties to Terminal at the moment because of build_jswrapper - * and the hacks used to add devices, but this would be really handy. */ -/*FIXME{ - "type" : "method", - "class" : "Terminal", - "name" : "in", - "generate" : "jswrap_telnet_in", - "params": [ - [ "args", "JsVarArray", "One or more items to feed into the Terminal" ] - ] -} -Send characters to the given Telnet device *as if they were received*. - -For instance if you had set the console to terminal with `Terminal.setConsole()` -then you could feed keypresses in via `Terminal.in`. -*/ -/*void jswrap_telnet_in_cb(int item, void *callbackData) { - NOT_USED(callbackData); - jshPushIOCharEvent(EV_TERMINAL, (char)item); -} -void jswrap_telnet_in(JsVar *parent, JsVar *args) { - NOT_USED(parent); - jsvIterateCallback(args, jswrap_telnet_in_cb, 0); -}*/ - - #define terminalHeight (10) #define terminalCharW (4) #define terminalCharH (6) @@ -78,8 +52,7 @@ static void terminalControlCharsReset() { // Try and find something to use for Graphics - MUST call terminalSetGFX after if this returns true bool terminalGetGFX(JsGraphics *gfx) { - JsVar *v = jsvObjectGetChild(execInfo.root, "g", 0); - if (!v) v = jsvObjectGetChild(execInfo.root, "LCD", 0); + JsVar *v = jswrap_graphics_getInstance(); if (!v) return false; if (graphicsGetFromVar(gfx, v)) return true; @@ -93,18 +66,20 @@ void terminalFlip(JsGraphics *gfx) { if (flip) jsvUnLock2(jspExecuteFunction(flip,gfx->graphicsVar,0,0),flip); } +/// Setup the graphics var state and flip the screen void terminalSetGFX(JsGraphics *gfx) { - terminalFlip(gfx); graphicsSetVar(gfx); + terminalFlip(gfx); // this will read from/save to graphicsVar jsvUnLock(gfx->graphicsVar); } +/// Scroll up to leave one more line free at the bottom void terminalScroll() { terminalY--; JsGraphics gfx; if (terminalGetGFX(&gfx)) { graphicsScroll(&gfx, 0, -terminalCharH); - terminalSetGFX(&gfx); + terminalSetGFX(&gfx); // save and flip } } @@ -117,7 +92,6 @@ void terminalSendChar(char chn) { terminalX = 0; terminalY++; while (terminalY >= terminalHeight) terminalScroll(); - // TODO: scroll! } else if (chn==13) { // carriage return terminalX = 0; } else if (chn==27) { @@ -162,6 +136,22 @@ void terminalSendChar(char chn) { case 66: terminalY++; while (terminalY >= terminalHeight) terminalScroll(); break; // down case 67: if (terminalX<255) terminalX++; break; // right case 68: if (terminalX > 0) terminalX--; break; // left + case 74: { // delete all to right and down + JsGraphics gfx; + if (terminalGetGFX(&gfx)) { + short cx = (short)(terminalOffsetX + terminalX*terminalCharW); + short cy = (short)(terminalOffsetY + terminalY*terminalCharH); + short w = (gfx.data.flags & JSGRAPHICSFLAGS_SWAP_XY) ? gfx.data.height : gfx.data.width; + short h = (gfx.data.flags & JSGRAPHICSFLAGS_SWAP_XY) ? gfx.data.width : gfx.data.height; + // Clear to right and down + unsigned int c = gfx.data.fgColor; + gfx.data.fgColor = gfx.data.bgColor; + graphicsFillRect(&gfx, cx, cy, w-1, cy+terminalCharH-1); // current line + graphicsFillRect(&gfx, terminalOffsetX, cy+terminalCharH, w-1, h-1); // everything under + gfx.data.fgColor = c; + terminalSetGFX(&gfx); + } + } break; } } } diff --git a/libs/graphics/lcd_arraybuffer.c b/libs/graphics/lcd_arraybuffer.c index ec933b6ce..1175eec8b 100644 --- a/libs/graphics/lcd_arraybuffer.c +++ b/libs/graphics/lcd_arraybuffer.c @@ -21,6 +21,15 @@ unsigned int lcdGetPixelIndex_ArrayBuffer(JsGraphics *gfx, int x, int y, int pix if (gfx->data.flags & JSGRAPHICSFLAGS_ARRAYBUFFER_ZIGZAG) { if (y&1) x = gfx->data.width - (x+pixelCount); } + if (gfx->data.flags & JSGRAPHICSFLAGS_ARRAYBUFFER_INTERLEAVEX) { + int h = gfx->data.height>>1; + unsigned int idx = 0; + if (y >= h) { + y-=h; + idx=gfx->data.bpp; + } + return idx + (unsigned int)((x + y*gfx->data.width)*(gfx->data.bpp<<1)); + } if (gfx->data.flags & JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE) return (unsigned int)(((x + (y>>3)*gfx->data.width)<<3) | (y&7)); else @@ -59,6 +68,11 @@ void lcdSetPixels_ArrayBuffer(JsGraphics *gfx, short x, short y, short pixelCoun unsigned int whiteMask = (1U<data.bpp)-1; bool shortCut = (col==0 || (col&whiteMask)==whiteMask) && (!(gfx->data.flags&JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE)); // simple black or white fill + int bppStride = gfx->data.bpp; + if (gfx->data.flags&JSGRAPHICSFLAGS_ARRAYBUFFER_INTERLEAVEX) { + bppStride <<= 1; + shortCut = false; + } while (pixelCount--) { // writing individual bits if (gfx->data.bpp&7/*not a multiple of one byte*/) { @@ -84,7 +98,7 @@ void lcdSetPixels_ArrayBuffer(JsGraphics *gfx, short x, short y, short pixelCoun if (gfx->data.flags & JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE) { jsvArrayBufferIteratorNext(&it); } else { - idx += gfx->data.bpp; + idx += bppStride; if (idx>=8) jsvArrayBufferIteratorNext(&it); } } else { // we're writing whole bytes @@ -140,6 +154,11 @@ void lcdSetPixels_ArrayBuffer_flat(JsGraphics *gfx, short x, short y, short pixe unsigned int whiteMask = (1U<data.bpp)-1; bool shortCut = (col==0 || (col&whiteMask)==whiteMask) && (!(gfx->data.flags&JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE)); // simple black or white fill + int bppStride = gfx->data.bpp; + if (gfx->data.flags&JSGRAPHICSFLAGS_ARRAYBUFFER_INTERLEAVEX) { + bppStride <<= 1; + shortCut = false; + } while (pixelCount--) { // writing individual bits if (gfx->data.bpp&7/*not a multiple of one byte*/) { @@ -161,11 +180,12 @@ void lcdSetPixels_ArrayBuffer_flat(JsGraphics *gfx, short x, short y, short pixe unsigned int mask = (unsigned int)(1<data.bpp)-1; unsigned int existing = (unsigned int)*ptr; unsigned int bitIdx = (gfx->data.flags & JSGRAPHICSFLAGS_ARRAYBUFFER_MSB) ? 8-(idx+gfx->data.bpp) : idx; + assert(ptr>=(unsigned char*)gfx->backendData && ptr<((unsigned char*)gfx->backendData + graphicsGetMemoryRequired(gfx))); *ptr = (char)((existing&~(mask<data.flags & JSGRAPHICSFLAGS_ARRAYBUFFER_VERTICAL_BYTE) { ptr++; } else { - idx += gfx->data.bpp; + idx += bppStride; if (idx>=8) ptr++; } } else { // we're writing whole bytes diff --git a/libs/hashlib/jswrap_hashlib.c b/libs/hashlib/jswrap_hashlib.c deleted file mode 100644 index 4aed8be8d..000000000 --- a/libs/hashlib/jswrap_hashlib.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * This file is part of Espruino, a JavaScript interpreter for Microcontrollers - * - * Copyright (C) 2013 Gordon Williams - * - * 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/. - * - * ---------------------------------------------------------------------------- - * This file is designed to be parsed during the build process - * - * Contains built-in functions for SHA hashes - * ---------------------------------------------------------------------------- - */ -#include -#include "jswrap_hashlib.h" - -const JsHash256 ctx256; -// const JsHash512 ctx512; - -JsHashLib hashFunctions[4] = { - { .name="sha224", .data=(char*)&ctx256.context, .init=sha224_init, .update=sha224_update, - .final=sha224_final, .digest_size=SHA224_DIGEST_SIZE, .block_size=SHA224_BLOCK_SIZE, .ctx_size=sizeof(ctx256.context) }, - { .name="sha256", .data=(char*)&ctx256.context, .init=sha256_init, .update=sha256_update, - .final=sha256_final, .digest_size=SHA256_DIGEST_SIZE, .block_size=SHA256_BLOCK_SIZE, .ctx_size=sizeof(ctx256.context) }/*, - { .name="sha384", .data=(char*)&ctx512.context, .init=sha384_init, .update=sha384_update, - .final=sha384_final, .digest_size=SHA384_DIGEST_SIZE, .block_size=SHA384_BLOCK_SIZE, .ctx_size=sizeof(ctx512.context) }, - { .name="sha512", .data=(char*)&ctx512.context, .init=sha512_init, .update=sha512_update, - .final=sha512_final, .digest_size=SHA512_DIGEST_SIZE, .block_size=SHA512_BLOCK_SIZE, .ctx_size=sizeof(ctx512.context) }*/ -}; - -/*JSON{ - "type" : "library", - "class" : "hashlib" -} -**Note:** This library is currently only included in builds for the original Espruino boards. -For other boards you will have to make build your own firmware. -*/ - -// --------------------------------------------------------------------------------- -// --------------------------------------------------------------------------------- -// --------------------------------------------------------------------------------- -// --------------------------------------------------------------------------------- - -/*JSON{ - "type" : "class", - "library" : "hashlib", - "class" : "HASH" -} -**Note:** This class is currently only included in builds for the original Espruino boards. -For other boards you will have to make build your own firmware. -*/ - -/*JSON{ - "type" : "staticmethod", - "class" : "hashlib", - "name" : "sha224", - "generate" : "jswrap_hashlib_sha224", - "params" : [ - ["message","JsVar","message to hash"] - ], - "return" : ["JsVar","Returns a new HASH SHA224 Object"], - "return_object" : "HASH" -} -*/ -JsVar *jswrap_hashlib_sha224(JsVar *message) { - JsVar *hashobj = jswrap_hashlib_sha2(HASH_SHA224); - - if (jsvIsString(message)) { - jswrap_hashlib_hash_update(hashobj, message); - } - return hashobj; -} - -/*JSON{ - "type" : "staticmethod", - "class" : "hashlib", - "name" : "sha256", - "generate" : "jswrap_hashlib_sha256", - "params" : [ - ["message","JsVar","message to hash"] - ], - "return" : ["JsVar","Returns a new HASH SHA256 Object"], - "return_object" : "HASH" -} -*/ -JsVar *jswrap_hashlib_sha256(JsVar *message) { - JsVar *hashobj = jswrap_hashlib_sha2(HASH_SHA256); - - if (jsvIsString(message)) { - jswrap_hashlib_hash_update(hashobj, message); - } - return hashobj; -} - - -JsVar *jswrap_hashlib_sha2(JsHashType hash_type) { - JsVar *hashobj = jspNewObject(0, "HASH"); - - if (!hashobj) { - return 0; // out of memory - } - - hashFunctions[hash_type].init(hashFunctions[hash_type].data); - - JsVar *jsCtx = jsvNewStringOfLength(hashFunctions[hash_type].ctx_size, hashFunctions[hash_type].data); - - jsvObjectSetChildAndUnLock(hashobj, "block_size", jsvNewFromInteger((JsVarInt)hashFunctions[hash_type].block_size)); - jsvObjectSetChildAndUnLock(hashobj, "context", jsCtx); - jsvObjectSetChildAndUnLock(hashobj, "digest_size", jsvNewFromInteger((JsVarInt)hashFunctions[hash_type].digest_size)); - jsvObjectSetChildAndUnLock(hashobj, "hash_type", jsvNewFromInteger(hash_type)); - jsvObjectSetChildAndUnLock(hashobj, "name", jsvNewFromString(hashFunctions[hash_type].name)); - - return hashobj; -} - - -/*JSON{ - "type" : "method", - "class" : "HASH", - "name" : "update", - "generate" : "jswrap_hashlib_hash_update", - "params" : [ - ["message","JsVar","part of message"] - ] -} -*/ -void jswrap_hashlib_hash_update(JsVar *parent, JsVar *message) { - int type; - char buff[SHA256_DIGEST_SIZE]; - - JsVar *jsCtx = jsvObjectGetChild(parent, "context", 0); - JsVar *child = jsvObjectGetChild(parent, "hash_type", 0); - - type = jsvGetInteger(child); - jsvUnLock(child); - - jsvGetString(jsCtx, hashFunctions[type].data, hashFunctions[type].ctx_size + 1); // trailing zero - - if (jsvIsString(message)) { - size_t i; - size_t len = jsvGetStringLength(message); - for(i = 0; i < len; i += sizeof(buff)) { - int read = (int)jsvGetStringChars(message, i, buff, sizeof(buff)); - hashFunctions[type].update(hashFunctions[type].data, buff, read); - } - jsvSetString(jsCtx, hashFunctions[type].data, hashFunctions[type].ctx_size); - } - - jsvUnLock(jsCtx); -} - -/*JSON{ - "type" : "method", - "class" : "HASH", - "name" : "digest", - "generate" : "jswrap_hashlib_hash_digest", - "params" : [ - ["message","JsVar","part of message"] - ], - "return" : ["JsVar","Hash digest"] -} -*/ -JsVar *jswrap_hashlib_hash_digest(JsVar *parent) { - int type; - char buff[SHA256_DIGEST_SIZE]; - JsVar *jsCtx = NULL; - JsVar *child = NULL; - - child = jsvObjectGetChild(parent, "hash_type", 0); - type = jsvGetInteger(child); - jsvUnLock(child); - - jsCtx = jsvObjectGetChild(parent, "context", 0); - - jsvGetString(jsCtx, hashFunctions[type].data, hashFunctions[type].ctx_size + 1); // trailing zero - jsvUnLock(jsCtx); - - hashFunctions[type].final(hashFunctions[type].data, buff); - JsVar *digest = jsvNewStringOfLength(hashFunctions[type].digest_size, buff); - - return digest; -} - -/*JSON{ - "type" : "method", - "class" : "HASH", - "name" : "hexdigest", - "generate" : "jswrap_hashlib_hash_hexdigest", - "params" : [ - ["message","JsVar","part of message"] - ], - "return" : ["JsVar","Hash hexdigest"] -} -*/ -JsVar *jswrap_hashlib_hash_hexdigest(JsVar *parent) { - int type; - char buff[SHA256_DIGEST_SIZE]; - char a[] = "0123456789abcdef"; - JsVar *jsCtx = NULL; - JsVar *child = NULL; - JsVar *digest = NULL; - - child = jsvObjectGetChild(parent, "hash_type", 0); - type = jsvGetInteger(child); - jsvUnLock(child); - - digest = jsvNewFromEmptyString(); // hashFunctions[type].digest_size*2 - if (!digest) return 0; // out of memory - - jsCtx = jsvObjectGetChild(parent, "context", 0); - jsvGetString(jsCtx, hashFunctions[type].data, hashFunctions[type].ctx_size + 1); // trailing zero - jsvUnLock(jsCtx); - - hashFunctions[type].final(hashFunctions[type].data, buff); - - unsigned int i; - for(i = 0; i < hashFunctions[type].digest_size; i++) { - char c[2]; - c[0] = a[ (unsigned char)(buff[i]) >> 4 ]; - c[1] = a[ (unsigned char)(buff[i]) & 0x0F ]; - jsvAppendStringBuf(digest, c, sizeof(c)); - } - - return digest; -} diff --git a/libs/hashlib/jswrap_hashlib.h b/libs/hashlib/jswrap_hashlib.h deleted file mode 100644 index ce40add76..000000000 --- a/libs/hashlib/jswrap_hashlib.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of Espruino, a JavaScript interpreter for Microcontrollers - * - * Copyright (C) 2013 Gordon Williams - * - * 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/. - * - * ---------------------------------------------------------------------------- - * Contains built-in functions for SHA hashes - * ---------------------------------------------------------------------------- - */ -#include "jsutils.h" -#include "jsparse.h" -#include "jsvar.h" -#include "sha2.h" - -typedef struct { - sha256_ctx context; - unsigned char _blank; ///< this is needed as jsvGetString for 'context' wants to add a trailing zero -} PACKED_FLAGS JsHash256; - -typedef struct { - sha512_ctx context; - unsigned char _blank; ///< this is needed as jsvGetString for 'context' wants to add a trailing zero -} PACKED_FLAGS JsHash512; - -typedef struct { - char *name; - char *data; - void (*init)(); // (void *ctx); - void (*update)(); // (void *ctx, const unsigned char *message, unsigned int len); - void (*final)(); // (void *, unsigned char *digest); - unsigned int digest_size; - unsigned int block_size; - unsigned int ctx_size; -} JsHashLib; - -typedef enum { - HASH_SHA224, - HASH_SHA256/*, - HASH_SHA384, - HASH_SHA512*/ -} JsHashType; - -JsVar *jswrap_hashlib_sha224(JsVar *message); -JsVar *jswrap_hashlib_sha256(JsVar *message); -// JsVar *jswrap_hashlib_sha384(JsVar *message); -// JsVar *jswrap_hashlib_sha512(JsVar *message); - -JsVar *jswrap_hashlib_sha2(JsHashType hash_type); - -JsVar *jswrap_hashlib_hash_digest(JsVar *parent); -JsVar *jswrap_hashlib_hash_hexdigest(JsVar *parent); -void jswrap_hashlib_hash_update(JsVar *parent, JsVar *message); diff --git a/libs/hashlib/sha2.c b/libs/hashlib/sha2.c deleted file mode 100644 index b28119bd2..000000000 --- a/libs/hashlib/sha2.c +++ /dev/null @@ -1,949 +0,0 @@ -/* - * FIPS 180-2 SHA-224/256/384/512 implementation - * Last update: 02/02/2007 - * Issue date: 04/30/2005 - * - * Copyright (C) 2005, 2007 Olivier Gay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#define UNROLL_LOOPS /* Enable loops unrolling */ -#endif - -#include - -#include "sha2.h" - -#define SHFR(x, n) (x >> n) -#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) -#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) -#define CH(x, y, z) ((x & y) ^ (~x & z)) -#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) - -#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) -#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) -#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) -#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) - -#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) -#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) -#define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7)) -#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6)) - -#define UNPACK32(x, str) \ -{ \ - *((str) + 3) = (uint8) ((x) ); \ - *((str) + 2) = (uint8) ((x) >> 8); \ - *((str) + 1) = (uint8) ((x) >> 16); \ - *((str) + 0) = (uint8) ((x) >> 24); \ -} - -#define PACK32(str, x) \ -{ \ - *(x) = ((uint32) *((str) + 3) ) \ - | ((uint32) *((str) + 2) << 8) \ - | ((uint32) *((str) + 1) << 16) \ - | ((uint32) *((str) + 0) << 24); \ -} - -#define UNPACK64(x, str) \ -{ \ - *((str) + 7) = (uint8) ((x) ); \ - *((str) + 6) = (uint8) ((x) >> 8); \ - *((str) + 5) = (uint8) ((x) >> 16); \ - *((str) + 4) = (uint8) ((x) >> 24); \ - *((str) + 3) = (uint8) ((x) >> 32); \ - *((str) + 2) = (uint8) ((x) >> 40); \ - *((str) + 1) = (uint8) ((x) >> 48); \ - *((str) + 0) = (uint8) ((x) >> 56); \ -} - -#define PACK64(str, x) \ -{ \ - *(x) = ((uint64) *((str) + 7) ) \ - | ((uint64) *((str) + 6) << 8) \ - | ((uint64) *((str) + 5) << 16) \ - | ((uint64) *((str) + 4) << 24) \ - | ((uint64) *((str) + 3) << 32) \ - | ((uint64) *((str) + 2) << 40) \ - | ((uint64) *((str) + 1) << 48) \ - | ((uint64) *((str) + 0) << 56); \ -} - -/* Macros used for loops unrolling */ - -#define SHA256_SCR(i) \ -{ \ - w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ - + SHA256_F3(w[i - 15]) + w[i - 16]; \ -} - -#define SHA512_SCR(i) \ -{ \ - w[i] = SHA512_F4(w[i - 2]) + w[i - 7] \ - + SHA512_F3(w[i - 15]) + w[i - 16]; \ -} - -#define SHA256_EXP(a, b, c, d, e, f, g, h, j) \ -{ \ - t1 = wv[h] + SHA256_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \ - + sha256_k[j] + w[j]; \ - t2 = SHA256_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ - wv[d] += t1; \ - wv[h] = t1 + t2; \ -} - -#define SHA512_EXP(a, b, c, d, e, f, g ,h, j) \ -{ \ - t1 = wv[h] + SHA512_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \ - + sha512_k[j] + w[j]; \ - t2 = SHA512_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ - wv[d] += t1; \ - wv[h] = t1 + t2; \ -} - -const uint32 sha224_h0[8] = - {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, - 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4}; - -const uint32 sha256_h0[8] = - {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - -const uint64 sha384_h0[8] = - {0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, - 0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL, - 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, - 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL}; - -const uint64 sha512_h0[8] = - {0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL}; - -const uint32 sha256_k[64] = - {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; - -const uint64 sha512_k[80] = - {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, - 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, - 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, - 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, - 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, - 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, - 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, - 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, - 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, - 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, - 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, - 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, - 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, - 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, - 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, - 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, - 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, - 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, - 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, - 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, - 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, - 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, - 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, - 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, - 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, - 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, - 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; - -/* SHA-256 functions */ - -void sha256_transf(sha256_ctx *ctx, const unsigned char *message, - unsigned int block_nb) -{ - uint32 w[64]; - uint32 wv[8]; - uint32 t1, t2; - const unsigned char *sub_block; - int i; - -#ifndef UNROLL_LOOPS - int j; -#endif - - for (i = 0; i < (int) block_nb; i++) { - sub_block = message + (i << 6); - -#ifndef UNROLL_LOOPS - for (j = 0; j < 16; j++) { - PACK32(&sub_block[j << 2], &w[j]); - } - - for (j = 16; j < 64; j++) { - SHA256_SCR(j); - } - - for (j = 0; j < 8; j++) { - wv[j] = ctx->h[j]; - } - - for (j = 0; j < 64; j++) { - t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) - + sha256_k[j] + w[j]; - t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - - for (j = 0; j < 8; j++) { - ctx->h[j] += wv[j]; - } -#else - PACK32(&sub_block[ 0], &w[ 0]); PACK32(&sub_block[ 4], &w[ 1]); - PACK32(&sub_block[ 8], &w[ 2]); PACK32(&sub_block[12], &w[ 3]); - PACK32(&sub_block[16], &w[ 4]); PACK32(&sub_block[20], &w[ 5]); - PACK32(&sub_block[24], &w[ 6]); PACK32(&sub_block[28], &w[ 7]); - PACK32(&sub_block[32], &w[ 8]); PACK32(&sub_block[36], &w[ 9]); - PACK32(&sub_block[40], &w[10]); PACK32(&sub_block[44], &w[11]); - PACK32(&sub_block[48], &w[12]); PACK32(&sub_block[52], &w[13]); - PACK32(&sub_block[56], &w[14]); PACK32(&sub_block[60], &w[15]); - - SHA256_SCR(16); SHA256_SCR(17); SHA256_SCR(18); SHA256_SCR(19); - SHA256_SCR(20); SHA256_SCR(21); SHA256_SCR(22); SHA256_SCR(23); - SHA256_SCR(24); SHA256_SCR(25); SHA256_SCR(26); SHA256_SCR(27); - SHA256_SCR(28); SHA256_SCR(29); SHA256_SCR(30); SHA256_SCR(31); - SHA256_SCR(32); SHA256_SCR(33); SHA256_SCR(34); SHA256_SCR(35); - SHA256_SCR(36); SHA256_SCR(37); SHA256_SCR(38); SHA256_SCR(39); - SHA256_SCR(40); SHA256_SCR(41); SHA256_SCR(42); SHA256_SCR(43); - SHA256_SCR(44); SHA256_SCR(45); SHA256_SCR(46); SHA256_SCR(47); - SHA256_SCR(48); SHA256_SCR(49); SHA256_SCR(50); SHA256_SCR(51); - SHA256_SCR(52); SHA256_SCR(53); SHA256_SCR(54); SHA256_SCR(55); - SHA256_SCR(56); SHA256_SCR(57); SHA256_SCR(58); SHA256_SCR(59); - SHA256_SCR(60); SHA256_SCR(61); SHA256_SCR(62); SHA256_SCR(63); - - wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; - wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; - wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; - wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; - - SHA256_EXP(0,1,2,3,4,5,6,7, 0); SHA256_EXP(7,0,1,2,3,4,5,6, 1); - SHA256_EXP(6,7,0,1,2,3,4,5, 2); SHA256_EXP(5,6,7,0,1,2,3,4, 3); - SHA256_EXP(4,5,6,7,0,1,2,3, 4); SHA256_EXP(3,4,5,6,7,0,1,2, 5); - SHA256_EXP(2,3,4,5,6,7,0,1, 6); SHA256_EXP(1,2,3,4,5,6,7,0, 7); - SHA256_EXP(0,1,2,3,4,5,6,7, 8); SHA256_EXP(7,0,1,2,3,4,5,6, 9); - SHA256_EXP(6,7,0,1,2,3,4,5,10); SHA256_EXP(5,6,7,0,1,2,3,4,11); - SHA256_EXP(4,5,6,7,0,1,2,3,12); SHA256_EXP(3,4,5,6,7,0,1,2,13); - SHA256_EXP(2,3,4,5,6,7,0,1,14); SHA256_EXP(1,2,3,4,5,6,7,0,15); - SHA256_EXP(0,1,2,3,4,5,6,7,16); SHA256_EXP(7,0,1,2,3,4,5,6,17); - SHA256_EXP(6,7,0,1,2,3,4,5,18); SHA256_EXP(5,6,7,0,1,2,3,4,19); - SHA256_EXP(4,5,6,7,0,1,2,3,20); SHA256_EXP(3,4,5,6,7,0,1,2,21); - SHA256_EXP(2,3,4,5,6,7,0,1,22); SHA256_EXP(1,2,3,4,5,6,7,0,23); - SHA256_EXP(0,1,2,3,4,5,6,7,24); SHA256_EXP(7,0,1,2,3,4,5,6,25); - SHA256_EXP(6,7,0,1,2,3,4,5,26); SHA256_EXP(5,6,7,0,1,2,3,4,27); - SHA256_EXP(4,5,6,7,0,1,2,3,28); SHA256_EXP(3,4,5,6,7,0,1,2,29); - SHA256_EXP(2,3,4,5,6,7,0,1,30); SHA256_EXP(1,2,3,4,5,6,7,0,31); - SHA256_EXP(0,1,2,3,4,5,6,7,32); SHA256_EXP(7,0,1,2,3,4,5,6,33); - SHA256_EXP(6,7,0,1,2,3,4,5,34); SHA256_EXP(5,6,7,0,1,2,3,4,35); - SHA256_EXP(4,5,6,7,0,1,2,3,36); SHA256_EXP(3,4,5,6,7,0,1,2,37); - SHA256_EXP(2,3,4,5,6,7,0,1,38); SHA256_EXP(1,2,3,4,5,6,7,0,39); - SHA256_EXP(0,1,2,3,4,5,6,7,40); SHA256_EXP(7,0,1,2,3,4,5,6,41); - SHA256_EXP(6,7,0,1,2,3,4,5,42); SHA256_EXP(5,6,7,0,1,2,3,4,43); - SHA256_EXP(4,5,6,7,0,1,2,3,44); SHA256_EXP(3,4,5,6,7,0,1,2,45); - SHA256_EXP(2,3,4,5,6,7,0,1,46); SHA256_EXP(1,2,3,4,5,6,7,0,47); - SHA256_EXP(0,1,2,3,4,5,6,7,48); SHA256_EXP(7,0,1,2,3,4,5,6,49); - SHA256_EXP(6,7,0,1,2,3,4,5,50); SHA256_EXP(5,6,7,0,1,2,3,4,51); - SHA256_EXP(4,5,6,7,0,1,2,3,52); SHA256_EXP(3,4,5,6,7,0,1,2,53); - SHA256_EXP(2,3,4,5,6,7,0,1,54); SHA256_EXP(1,2,3,4,5,6,7,0,55); - SHA256_EXP(0,1,2,3,4,5,6,7,56); SHA256_EXP(7,0,1,2,3,4,5,6,57); - SHA256_EXP(6,7,0,1,2,3,4,5,58); SHA256_EXP(5,6,7,0,1,2,3,4,59); - SHA256_EXP(4,5,6,7,0,1,2,3,60); SHA256_EXP(3,4,5,6,7,0,1,2,61); - SHA256_EXP(2,3,4,5,6,7,0,1,62); SHA256_EXP(1,2,3,4,5,6,7,0,63); - - ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; - ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; - ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; - ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; -#endif /* !UNROLL_LOOPS */ - } -} - -void sha256(const unsigned char *message, unsigned int len, unsigned char *digest) -{ - sha256_ctx ctx; - - sha256_init(&ctx); - sha256_update(&ctx, message, len); - sha256_final(&ctx, digest); -} - -void sha256_init(sha256_ctx *ctx) -{ -#ifndef UNROLL_LOOPS - int i; - for (i = 0; i < 8; i++) { - ctx->h[i] = sha256_h0[i]; - } -#else - ctx->h[0] = sha256_h0[0]; ctx->h[1] = sha256_h0[1]; - ctx->h[2] = sha256_h0[2]; ctx->h[3] = sha256_h0[3]; - ctx->h[4] = sha256_h0[4]; ctx->h[5] = sha256_h0[5]; - ctx->h[6] = sha256_h0[6]; ctx->h[7] = sha256_h0[7]; -#endif /* !UNROLL_LOOPS */ - - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha256_update(sha256_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA256_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA256_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA256_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha256_transf(ctx, ctx->block, 1); - sha256_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA256_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 6], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 6; -} - -void sha256_final(sha256_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - -#ifndef UNROLL_LOOPS - int i; -#endif - - block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) - < (ctx->len % SHA256_BLOCK_SIZE))); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 6; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha256_transf(ctx, ctx->block, block_nb); - -#ifndef UNROLL_LOOPS - for (i = 0 ; i < 8; i++) { - UNPACK32(ctx->h[i], &digest[i << 2]); - } -#else - UNPACK32(ctx->h[0], &digest[ 0]); - UNPACK32(ctx->h[1], &digest[ 4]); - UNPACK32(ctx->h[2], &digest[ 8]); - UNPACK32(ctx->h[3], &digest[12]); - UNPACK32(ctx->h[4], &digest[16]); - UNPACK32(ctx->h[5], &digest[20]); - UNPACK32(ctx->h[6], &digest[24]); - UNPACK32(ctx->h[7], &digest[28]); -#endif /* !UNROLL_LOOPS */ -} - -/* SHA-512 functions */ - -void sha512_transf(sha512_ctx *ctx, const unsigned char *message, - unsigned int block_nb) -{ - uint64 w[80]; - uint64 wv[8]; - uint64 t1, t2; - const unsigned char *sub_block; - int i, j; - - for (i = 0; i < (int) block_nb; i++) { - sub_block = message + (i << 7); - -#ifndef UNROLL_LOOPS - for (j = 0; j < 16; j++) { - PACK64(&sub_block[j << 3], &w[j]); - } - - for (j = 16; j < 80; j++) { - SHA512_SCR(j); - } - - for (j = 0; j < 8; j++) { - wv[j] = ctx->h[j]; - } - - for (j = 0; j < 80; j++) { - t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) - + sha512_k[j] + w[j]; - t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - - for (j = 0; j < 8; j++) { - ctx->h[j] += wv[j]; - } -#else - PACK64(&sub_block[ 0], &w[ 0]); PACK64(&sub_block[ 8], &w[ 1]); - PACK64(&sub_block[ 16], &w[ 2]); PACK64(&sub_block[ 24], &w[ 3]); - PACK64(&sub_block[ 32], &w[ 4]); PACK64(&sub_block[ 40], &w[ 5]); - PACK64(&sub_block[ 48], &w[ 6]); PACK64(&sub_block[ 56], &w[ 7]); - PACK64(&sub_block[ 64], &w[ 8]); PACK64(&sub_block[ 72], &w[ 9]); - PACK64(&sub_block[ 80], &w[10]); PACK64(&sub_block[ 88], &w[11]); - PACK64(&sub_block[ 96], &w[12]); PACK64(&sub_block[104], &w[13]); - PACK64(&sub_block[112], &w[14]); PACK64(&sub_block[120], &w[15]); - - SHA512_SCR(16); SHA512_SCR(17); SHA512_SCR(18); SHA512_SCR(19); - SHA512_SCR(20); SHA512_SCR(21); SHA512_SCR(22); SHA512_SCR(23); - SHA512_SCR(24); SHA512_SCR(25); SHA512_SCR(26); SHA512_SCR(27); - SHA512_SCR(28); SHA512_SCR(29); SHA512_SCR(30); SHA512_SCR(31); - SHA512_SCR(32); SHA512_SCR(33); SHA512_SCR(34); SHA512_SCR(35); - SHA512_SCR(36); SHA512_SCR(37); SHA512_SCR(38); SHA512_SCR(39); - SHA512_SCR(40); SHA512_SCR(41); SHA512_SCR(42); SHA512_SCR(43); - SHA512_SCR(44); SHA512_SCR(45); SHA512_SCR(46); SHA512_SCR(47); - SHA512_SCR(48); SHA512_SCR(49); SHA512_SCR(50); SHA512_SCR(51); - SHA512_SCR(52); SHA512_SCR(53); SHA512_SCR(54); SHA512_SCR(55); - SHA512_SCR(56); SHA512_SCR(57); SHA512_SCR(58); SHA512_SCR(59); - SHA512_SCR(60); SHA512_SCR(61); SHA512_SCR(62); SHA512_SCR(63); - SHA512_SCR(64); SHA512_SCR(65); SHA512_SCR(66); SHA512_SCR(67); - SHA512_SCR(68); SHA512_SCR(69); SHA512_SCR(70); SHA512_SCR(71); - SHA512_SCR(72); SHA512_SCR(73); SHA512_SCR(74); SHA512_SCR(75); - SHA512_SCR(76); SHA512_SCR(77); SHA512_SCR(78); SHA512_SCR(79); - - wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; - wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; - wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; - wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; - - j = 0; - - do { - SHA512_EXP(0,1,2,3,4,5,6,7,j); j++; - SHA512_EXP(7,0,1,2,3,4,5,6,j); j++; - SHA512_EXP(6,7,0,1,2,3,4,5,j); j++; - SHA512_EXP(5,6,7,0,1,2,3,4,j); j++; - SHA512_EXP(4,5,6,7,0,1,2,3,j); j++; - SHA512_EXP(3,4,5,6,7,0,1,2,j); j++; - SHA512_EXP(2,3,4,5,6,7,0,1,j); j++; - SHA512_EXP(1,2,3,4,5,6,7,0,j); j++; - } while (j < 80); - - ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; - ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; - ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; - ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; -#endif /* !UNROLL_LOOPS */ - } -} - -void sha512(const unsigned char *message, unsigned int len, - unsigned char *digest) -{ - sha512_ctx ctx; - - sha512_init(&ctx); - sha512_update(&ctx, message, len); - sha512_final(&ctx, digest); -} - -void sha512_init(sha512_ctx *ctx) -{ -#ifndef UNROLL_LOOPS - int i; - for (i = 0; i < 8; i++) { - ctx->h[i] = sha512_h0[i]; - } -#else - ctx->h[0] = sha512_h0[0]; ctx->h[1] = sha512_h0[1]; - ctx->h[2] = sha512_h0[2]; ctx->h[3] = sha512_h0[3]; - ctx->h[4] = sha512_h0[4]; ctx->h[5] = sha512_h0[5]; - ctx->h[6] = sha512_h0[6]; ctx->h[7] = sha512_h0[7]; -#endif /* !UNROLL_LOOPS */ - - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha512_update(sha512_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA512_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA512_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA512_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha512_transf(ctx, ctx->block, 1); - sha512_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA512_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 7], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 7; -} - -void sha512_final(sha512_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - -#ifndef UNROLL_LOOPS - int i; -#endif - - block_nb = 1 + ((SHA512_BLOCK_SIZE - 17) - < (ctx->len % SHA512_BLOCK_SIZE)); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 7; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha512_transf(ctx, ctx->block, block_nb); - -#ifndef UNROLL_LOOPS - for (i = 0 ; i < 8; i++) { - UNPACK64(ctx->h[i], &digest[i << 3]); - } -#else - UNPACK64(ctx->h[0], &digest[ 0]); - UNPACK64(ctx->h[1], &digest[ 8]); - UNPACK64(ctx->h[2], &digest[16]); - UNPACK64(ctx->h[3], &digest[24]); - UNPACK64(ctx->h[4], &digest[32]); - UNPACK64(ctx->h[5], &digest[40]); - UNPACK64(ctx->h[6], &digest[48]); - UNPACK64(ctx->h[7], &digest[56]); -#endif /* !UNROLL_LOOPS */ -} - -/* SHA-384 functions */ - -void sha384(const unsigned char *message, unsigned int len, - unsigned char *digest) -{ - sha384_ctx ctx; - - sha384_init(&ctx); - sha384_update(&ctx, message, len); - sha384_final(&ctx, digest); -} - -void sha384_init(sha384_ctx *ctx) -{ -#ifndef UNROLL_LOOPS - int i; - for (i = 0; i < 8; i++) { - ctx->h[i] = sha384_h0[i]; - } -#else - ctx->h[0] = sha384_h0[0]; ctx->h[1] = sha384_h0[1]; - ctx->h[2] = sha384_h0[2]; ctx->h[3] = sha384_h0[3]; - ctx->h[4] = sha384_h0[4]; ctx->h[5] = sha384_h0[5]; - ctx->h[6] = sha384_h0[6]; ctx->h[7] = sha384_h0[7]; -#endif /* !UNROLL_LOOPS */ - - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha384_update(sha384_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA384_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA384_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA384_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha512_transf(ctx, ctx->block, 1); - sha512_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA384_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 7], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 7; -} - -void sha384_final(sha384_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - -#ifndef UNROLL_LOOPS - int i; -#endif - - block_nb = (1 + ((SHA384_BLOCK_SIZE - 17) - < (ctx->len % SHA384_BLOCK_SIZE))); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 7; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha512_transf(ctx, ctx->block, block_nb); - -#ifndef UNROLL_LOOPS - for (i = 0 ; i < 6; i++) { - UNPACK64(ctx->h[i], &digest[i << 3]); - } -#else - UNPACK64(ctx->h[0], &digest[ 0]); - UNPACK64(ctx->h[1], &digest[ 8]); - UNPACK64(ctx->h[2], &digest[16]); - UNPACK64(ctx->h[3], &digest[24]); - UNPACK64(ctx->h[4], &digest[32]); - UNPACK64(ctx->h[5], &digest[40]); -#endif /* !UNROLL_LOOPS */ -} - -/* SHA-224 functions */ - -void sha224(const unsigned char *message, unsigned int len, - unsigned char *digest) -{ - sha224_ctx ctx; - - sha224_init(&ctx); - sha224_update(&ctx, message, len); - sha224_final(&ctx, digest); -} - -void sha224_init(sha224_ctx *ctx) -{ -#ifndef UNROLL_LOOPS - int i; - for (i = 0; i < 8; i++) { - ctx->h[i] = sha224_h0[i]; - } -#else - ctx->h[0] = sha224_h0[0]; ctx->h[1] = sha224_h0[1]; - ctx->h[2] = sha224_h0[2]; ctx->h[3] = sha224_h0[3]; - ctx->h[4] = sha224_h0[4]; ctx->h[5] = sha224_h0[5]; - ctx->h[6] = sha224_h0[6]; ctx->h[7] = sha224_h0[7]; -#endif /* !UNROLL_LOOPS */ - - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha224_update(sha224_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA224_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA224_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA224_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha256_transf(ctx, ctx->block, 1); - sha256_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA224_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 6], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 6; -} - -void sha224_final(sha224_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - -#ifndef UNROLL_LOOPS - int i; -#endif - - block_nb = (1 + ((SHA224_BLOCK_SIZE - 9) - < (ctx->len % SHA224_BLOCK_SIZE))); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 6; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha256_transf(ctx, ctx->block, block_nb); - -#ifndef UNROLL_LOOPS - for (i = 0 ; i < 7; i++) { - UNPACK32(ctx->h[i], &digest[i << 2]); - } -#else - UNPACK32(ctx->h[0], &digest[ 0]); - UNPACK32(ctx->h[1], &digest[ 4]); - UNPACK32(ctx->h[2], &digest[ 8]); - UNPACK32(ctx->h[3], &digest[12]); - UNPACK32(ctx->h[4], &digest[16]); - UNPACK32(ctx->h[5], &digest[20]); - UNPACK32(ctx->h[6], &digest[24]); -#endif /* !UNROLL_LOOPS */ -} - -#ifdef TEST_VECTORS - -/* FIPS 180-2 Validation tests */ - -#include -#include - -void test(const char *vector, unsigned char *digest, - unsigned int digest_size) -{ - char output[2 * SHA512_DIGEST_SIZE + 1]; - int i; - - output[2 * digest_size] = '\0'; - - for (i = 0; i < (int) digest_size ; i++) { - sprintf(output + 2 * i, "%02x", digest[i]); - } - - printf("H: %s\n", output); - if (strcmp(vector, output)) { - fprintf(stderr, "Test failed.\n"); - exit(EXIT_FAILURE); - } -} - -int main(void) -{ - static const char *vectors[4][3] = - { /* SHA-224 */ - { - "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", - "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", - "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67", - }, - /* SHA-256 */ - { - "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", - "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", - "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", - }, - /* SHA-384 */ - { - "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed" - "8086072ba1e7cc2358baeca134c825a7", - "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712" - "fcc7c71a557e2db966c3e9fa91746039", - "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b" - "07b8b3dc38ecc4ebae97ddd87f3d8985", - }, - /* SHA-512 */ - { - "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" - "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", - "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" - "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", - "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" - "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b" - } - }; - - static const char message1[] = "abc"; - static const char message2a[] = "abcdbcdecdefdefgefghfghighijhi" - "jkijkljklmklmnlmnomnopnopq"; - static const char message2b[] = "abcdefghbcdefghicdefghijdefghijkefghij" - "klfghijklmghijklmnhijklmnoijklmnopjklm" - "nopqklmnopqrlmnopqrsmnopqrstnopqrstu"; - unsigned char *message3; - unsigned int message3_len = 1000000; - unsigned char digest[SHA512_DIGEST_SIZE]; - - message3 = malloc(message3_len); - if (message3 == NULL) { - fprintf(stderr, "Can't allocate memory\n"); - return -1; - } - memset(message3, 'a', message3_len); - - printf("SHA-2 FIPS 180-2 Validation tests\n\n"); - printf("SHA-224 Test vectors\n"); - - sha224((const unsigned char *) message1, strlen(message1), digest); - test(vectors[0][0], digest, SHA224_DIGEST_SIZE); - sha224((const unsigned char *) message2a, strlen(message2a), digest); - test(vectors[0][1], digest, SHA224_DIGEST_SIZE); - sha224(message3, message3_len, digest); - test(vectors[0][2], digest, SHA224_DIGEST_SIZE); - printf("\n"); - - printf("SHA-256 Test vectors\n"); - - sha256((const unsigned char *) message1, strlen(message1), digest); - test(vectors[1][0], digest, SHA256_DIGEST_SIZE); - sha256((const unsigned char *) message2a, strlen(message2a), digest); - test(vectors[1][1], digest, SHA256_DIGEST_SIZE); - sha256(message3, message3_len, digest); - test(vectors[1][2], digest, SHA256_DIGEST_SIZE); - printf("\n"); - - printf("SHA-384 Test vectors\n"); - - sha384((const unsigned char *) message1, strlen(message1), digest); - test(vectors[2][0], digest, SHA384_DIGEST_SIZE); - sha384((const unsigned char *)message2b, strlen(message2b), digest); - test(vectors[2][1], digest, SHA384_DIGEST_SIZE); - sha384(message3, message3_len, digest); - test(vectors[2][2], digest, SHA384_DIGEST_SIZE); - printf("\n"); - - printf("SHA-512 Test vectors\n"); - - sha512((const unsigned char *) message1, strlen(message1), digest); - test(vectors[3][0], digest, SHA512_DIGEST_SIZE); - sha512((const unsigned char *) message2b, strlen(message2b), digest); - test(vectors[3][1], digest, SHA512_DIGEST_SIZE); - sha512(message3, message3_len, digest); - test(vectors[3][2], digest, SHA512_DIGEST_SIZE); - printf("\n"); - - printf("All tests passed.\n"); - - return 0; -} - -#endif /* TEST_VECTORS */ - diff --git a/libs/hashlib/sha2.h b/libs/hashlib/sha2.h deleted file mode 100644 index 60f52e34d..000000000 --- a/libs/hashlib/sha2.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * FIPS 180-2 SHA-224/256/384/512 implementation - * Last update: 02/02/2007 - * Issue date: 04/30/2005 - * - * Copyright (C) 2005, 2007 Olivier Gay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef SHA2_H -#define SHA2_H - -#define SHA224_DIGEST_SIZE ( 224 / 8) -#define SHA256_DIGEST_SIZE ( 256 / 8) -#define SHA384_DIGEST_SIZE ( 384 / 8) -#define SHA512_DIGEST_SIZE ( 512 / 8) - -#define SHA256_BLOCK_SIZE ( 512 / 8) -#define SHA512_BLOCK_SIZE (1024 / 8) -#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE -#define SHA224_BLOCK_SIZE SHA256_BLOCK_SIZE - -#ifndef SHA2_TYPES -#define SHA2_TYPES -typedef unsigned char uint8; -typedef unsigned int uint32; -typedef unsigned long long uint64; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - unsigned int tot_len; - unsigned int len; - unsigned char block[2 * SHA256_BLOCK_SIZE]; - uint32 h[8]; -} sha256_ctx; - -typedef struct { - unsigned int tot_len; - unsigned int len; - unsigned char block[2 * SHA512_BLOCK_SIZE]; - uint64 h[8]; -} sha512_ctx; - -typedef sha512_ctx sha384_ctx; -typedef sha256_ctx sha224_ctx; - -void sha224_init(sha224_ctx *ctx); -void sha224_update(sha224_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha224_final(sha224_ctx *ctx, unsigned char *digest); -void sha224(const unsigned char *message, unsigned int len, - unsigned char *digest); - -void sha256_init(sha256_ctx * ctx); -void sha256_update(sha256_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha256_final(sha256_ctx *ctx, unsigned char *digest); -void sha256(const unsigned char *message, unsigned int len, - unsigned char *digest); - -void sha384_init(sha384_ctx *ctx); -void sha384_update(sha384_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha384_final(sha384_ctx *ctx, unsigned char *digest); -void sha384(const unsigned char *message, unsigned int len, - unsigned char *digest); - -void sha512_init(sha512_ctx *ctx); -void sha512_update(sha512_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha512_final(sha512_ctx *ctx, unsigned char *digest); -void sha512(const unsigned char *message, unsigned int len, - unsigned char *digest); - -#ifdef __cplusplus -} -#endif - -#endif /* !SHA2_H */ - diff --git a/libs/hexbadge/jswrap_hexbadge.c b/libs/hexbadge/jswrap_hexbadge.c index 81761c982..d95f29509 100644 --- a/libs/hexbadge/jswrap_hexbadge.c +++ b/libs/hexbadge/jswrap_hexbadge.c @@ -198,7 +198,7 @@ Return an approximate battery percentage remaining based on a normal CR2032 battery (2.8 - 2.2v) */ int jswrap_badge_getBatteryPercentage() { - JsVarFloat v = jswrap_nrf_bluetooth_getBattery(); + JsVarFloat v = jswrap_ble_getBattery(); int pc = (v-2.2)*100/0.6; if (pc>100) pc=100; if (pc<0) pc=0; diff --git a/libs/js/AT.min.js b/libs/js/AT.min.js new file mode 100644 index 000000000..8a6641fc2 --- /dev/null +++ b/libs/js/AT.min.js @@ -0,0 +1,4 @@ +exports.connect=function(n){var m=!1,b="",g,d=0,l,e={},h={},k=[];n.on("data",function q(a){if(d){b&&(a=b+a,b="");if(a.length<=d){d-=a.length;l(a);0==d&&(l=void 0);return}l(a.substr(0,d));a=a.substr(d);d=0;l=void 0}b+=a;m&&console.log("] "+JSON.stringify(a));"\n"==b[0]&&(b=b.substr(1));if(e)for(var c in e)for(;b.substr(0,c.length)==c;)if(a=b,b=e[c](b),a==b)return;for(a=b.indexOf("\r");0<=a;){var f=b.substr(0,a),p=!1;if(0",function(){b.unregister(">");b.write(e+"\u001a\r");return""});b.cmd('AT+CMGS="'+a+'"\r\n',1E4,function f(a){b.unregister(">");if(a&&"+CMGS"==a.substr(0,5))return f;d&&d("OK"==a?null:"CMGS ERROR "+a)})};c.prototype.list=function(a,e){var d=[],b;this.at.cmd('AT+CMGL="'+a+'"\r\n',1E4,function f(a){if(void 0!==b&&void 0!==a)return b.text=g(a),b=void 0,f;if(a&&"+CMGL: "==a.substr(0,7)){try{var c= +JSON.parse("["+a.substr(7)+"]");b={index:c[0],isRead:"REC READ"==c[1],oaddr:c[2],oname:c[3],time:c[4],text:""};d.push(b)}catch(m){}return f}e&&e("OK"==a?null:"CMGL ERROR "+a,d)})};c.prototype.get=function(a,e){var d;this.at.cmd("AT+CMGR="+a+"\r\n",1E4,function h(a){if("OK"==a)return e(null,d);if(void 0!==d&&void 0!==a&&"OK"!=a)return d.text=g(a),h;if(a&&"+CMGR: "==a.substr(0,7)){try{var c=JSON.parse("["+a.substr(7)+"]");d={isRead:"REC READ"==c[0],oaddr:c[1],oname:c[2],time:c[3],text:""}}catch(l){}return h}e&& +e("OK"==a?null:"CMGR ERROR "+a,list)})};c.prototype["delete"]=function(a,c){"ALL"==a&&(a="1,4");this.at.cmd("AT+CMGD="+a+"\r\n",1E3,function(a){c&&c("OK"==a?null:"CMGD ERROR "+a)})};exports=c \ No newline at end of file diff --git a/libs/nordic_thingy/BH1745.min.js b/libs/js/BH1745.min.js similarity index 100% rename from libs/nordic_thingy/BH1745.min.js rename to libs/js/BH1745.min.js diff --git a/libs/js/BME280.min.js b/libs/js/BME280.min.js new file mode 100644 index 000000000..e19eb4245 --- /dev/null +++ b/libs/js/BME280.min.js @@ -0,0 +1,4 @@ +function f(a,b,c){this.read=b;this.write=c;this.write(242,1);this.write(244,39);this.write(245,160);this.readCoefficients()}function d(a,b){var c=(a[b+1]<<8)+a[b];c&32768&&(c-=65536);return c}f.prototype.setPower=function(a){this.read(244,1)};f.prototype.readCoefficients=function(){var a=new Uint8Array(32);a.set(this.read(136,24),0);a.set(this.read(161,1),24);a.set(this.read(225,7),25);this.dT=[,a[1]<<8|a[0],d(a,2),d(a,4)];this.dP=[,a[7]<<8|a[6],d(a,8),d(a,10),d(a,12),d(a,14),d(a,16), +d(a,18),d(a,20),d(a,22)];this.dH=[,a[24],d(a,25),a[27],a[28]<<4|15&a[29],a[30]<<4|a[29]>>4&15,a[31]]};f.prototype.readRawData=function(){var a=this.read(247,8);this.pres_raw=a[0]<<12|a[1]<<4|a[2]>>4;this.temp_raw=a[3]<<12|a[4]<<4|a[5]>>4;this.hum_raw=a[6]<<8|a[7]};f.prototype.calibration_T=function(a){var b,c=this.dT;b=(a/16384-c[1]/1024)*c[2];a=(a/131072-c[1]/8192)*(a/131072-c[1]/8192)*c[3];this.t_fine=b+a;return(b+a)/5120*100};f.prototype.calibration_P=function(a){var b,c,e=this.dP;b=this.t_fine/ +2-64E3;c=b*b*e[6]/32768;c+=b*e[5]*2;c=c/4+65536*e[4];b=(e[3]*b*b/524288+e[2]*b)/524288;b=(1+b/32768)*e[1];if(0===b)return 0;a=6250*(1048576-a-c/4096)/b;b=e[9]*a*a/2147483648;c=a*e[8]/32768;return a+=(b+c+e[7])/16};f.prototype.calibration_H=function(a){var b,c=this.dH;b=this.t_fine-76800;b=((a<<14)-(c[4]<<20)-c[5]*b+16384>>15)*((((b*c[6]>>10)*((b*c[3]>>11)+32768)>>10)+2097152)*c[2]+8192>>14);b-=((b>>15)*(b>>15)>>7)*c[1]>>4;b=Math.clip(b,0,419430400);return b>>12};f.prototype.getData=function(a){this.readRawData(); +return{temp:this.calibration_T(this.temp_raw)/100,pressure:this.calibration_P(this.pres_raw)/100,humidity:this.calibration_H(this.hum_raw)/1024}};exports.connect=function(a,b){return new f(b,function(b,e){a.writeTo(118,b);return a.readFrom(118,e)},function(b,e){a.writeTo(118,[b,e])})};exports.connectSPI=function(a,b,c){return new f(c,function(c,d){new Uint8Array(d+1);return a.send([c|128,new Uint8Array(d)],b).slice(1)},function(c,d){a.write(c&127,d,b)})} \ No newline at end of file diff --git a/libs/nordic_thingy/CCS811.min.js b/libs/js/CCS811.min.js similarity index 100% rename from libs/nordic_thingy/CCS811.min.js rename to libs/js/CCS811.min.js diff --git a/libs/js/GPS.min.js b/libs/js/GPS.min.js new file mode 100644 index 000000000..4791eb5e2 --- /dev/null +++ b/libs/js/GPS.min.js @@ -0,0 +1,2 @@ +exports.connect=function(f,e){var b={line:""};if(e)b.on("line",function(a){var b;if(b="GGA"==a.substr(3,3)){b=a.length;for(var c=0,d=1;d>12))};exports.OPT3001=a;exports.connectI2C=function(b, +c){var d=c&&c.addr||68;return new a(c,function(a){b.writeTo(d,a);a=b.readFrom(d,2);return a[1]|a[0]<<8},function(a,c){b.writeTo(d,[a,c>>8,c])})} \ No newline at end of file diff --git a/libs/js/QuectelBG96.min.js b/libs/js/QuectelBG96.min.js new file mode 100644 index 000000000..96b0ca8ee --- /dev/null +++ b/libs/js/QuectelBG96.min.js @@ -0,0 +1,6 @@ +function l(){}function h(a,b){return new Promise(function(c,f){var m="";d.cmd(a+"\r\n",b||1E3,function p(b){if(void 0===b||"ERROR"==b)f(a+": "+b?b:"TIMEOUT");else if("OK"==b)c(m);else return m+=(m?"\n":"")+b,p})})}var d,g=[],e=" ".split(" "),k=!1,q={create:function(a,b){var c=0;if(void 0===a)return l("Server not implemented"),-1;for(;void 0!==g[c];)c++;if(6<=c)throw Error("No free sockets.");g[c]="Wait";e[c]="";k=!0;d.cmd("AT+QIOPEN=1,"+c+',"TCP",'+JSON.stringify(a)+","+b+",0,1\r\n", +1E4,function(a){"OK"!=a&&(g[c]=void 0,k=!1)});return c},close:function(a){void 0!==g[a]&&(e[a]="",d.cmd("AT+QICLOSE="+a+"\r\n",1E3,function(){g[a]=void 0}))},accept:function(a){return-1},recv:function(a,b){if(e[a]){if(e[a].length>b){var c=e[a].substr(0,b);e[a]=e[a].substr(b)}else c=e[a],e[a]="";return c}return g[a]?"":-1},send:function(a,b){if(k||d.isBusy()||"Wait"==g[a])return 0;if(!g[a])return-1;k=!0;d.cmd("AT+QISEND="+a+","+b.length+"\r\n",1E4,function m(f){l("AT+QISEND response "+JSON.stringify(f)); +if("OK"==f)return d.register("> ",function(a){d.unregister("> ");d.write(b);return a.substr(2)}),m;"SEND OK"==f?k=!1:(d.unregister("> "),g[a]=null)});return b.length}},n={debug:function(a){l=a||void 0===a?function(a){print("[BG96]",a)}:function(){};return{socks:g,sockData:e,busy:k}},getVersion:function(a){h("AT+GMR").then(function(b){a(null,b)})["catch"](function(b){a(b)})},getIP:function(a){var b;d.cmd("AT+QISTATE=0,1\r\n",1E3,function(c){"OK"==c?a(null,b):c.startsWith("+QISTATE")?b=c.split(",")[2]: +a(null,c)})}};exports.connect=function(a,b,c){b=b||{};a.removeAllListeners();n.at=d=require("AT").connect(a);require("NetworkJS").create(q);d.register('+QIURC: "recv"',function(a){var b=a.indexOf("\r\n");if(0>b)return a;var f=a.split(",");a=a.substr(b+2);e[0|f[1]]+=a;l(f);d.getData((0|f[2])-a.length,function(a){e[0|f[1]]+=a});return""});d.registerLine('+QIURC: "closed"',function(a){g[0|a.substr(17)]=null;k=!1});d.registerLine("+QIOPEN: ",function(a){a=a.substr(9).split(",");g[0|a[0]]=0==a[1]?!0:void 0; +k=!1});d.registerLine("+CME ERROR",function(a){console.log(a)});h("ATE0").then(function(){return h("AT+CEREG=2")}).then(function(){return new Promise(function(a,c){var f=60,e=setInterval(function(){d.cmd(b.lte?"AT+CEREG?\r":"AT+CREG?\r",500,function(b){b=b.split(",")[1];if(1==b||5==b)clearInterval(e),a()});0>=f--&&(clearInterval(e),c("Timeout while registering."))},1E3)})}).then(function(a){return h("AT+CGATT=1",1E4)}).then(function(){return h("AT+CGREG?")}).then(function(a){var b=a.split(",")[1]; +if(2==b)throw Error("GPRS still connecting, "+a);if(1!=b&&5!=b)throw Error("GPRS not registered, "+a);return h("AT+COPS?")}).then(function(a){var c=a.split(",")[2];if(!c)throw Error("No Operator, "+a);l("Operator "+c);return h("AT+QICSGP=1,1,"+JSON.stringify(b.apn||"")+","+JSON.stringify(b.username||"")+","+JSON.stringify(b.password||""),6E4)}).then(function(){return h("AT+QIACT=1",6E4)}).then(function(){c&&c(null)})["catch"](function(a){c&&c(a)});return n} \ No newline at end of file diff --git a/libs/js/QuectelM35.min.js b/libs/js/QuectelM35.min.js new file mode 100644 index 000000000..5228047f4 --- /dev/null +++ b/libs/js/QuectelM35.min.js @@ -0,0 +1,6 @@ +function g(){}function p(a){var b=a.substring(10).split(",");g(b);d.getData(0|b[1],function(a){e[0|b[0]]+=a})}function q(a){g(a);var b=a[0];a=a.substr(3);"CONNECT OK"==a?c[b]=!0:"CONNECT FAIL"==a?c[b]=void 0:"CLOSED"==a?c[b]=void 0:"SEND OK"!=a&&"SEND FAIL"==a&&(c[b]=void 0);k=!1;return""}function h(a,b){return new Promise(function(c,f){var l="";d.cmd(a+"\r\n",b||1E3,function r(b){if(void 0===b||"ERROR"==b)f(a+": "+b?b:"TIMEOUT");else if("OK"==b)c(l);else return l+=(l?"\n":"")+b,r})})} +var d,c=[],e=" ".split(" "),k=!1,t={create:function(a,b){function m(a){d.cmd("AT+QIOPEN="+f+',"TCP",'+JSON.stringify(a)+","+b+"\r\n",1E4,function(a){"OK"!=a&&(c[f]=void 0,k=!1)})}var f=0;if(void 0===a)return g("Server not implemented"),-1;for(;void 0!==c[f];)f++;if(6<=f)throw Error("No free sockets.");c[f]="Wait";e[f]="";k=!0;a.match(/\d+.\d+.\d+.\d+/)?m(a):(g("Lookup host "+a),d.cmd("AT+QIDNSGIP="+JSON.stringify(a)+"\r\n",1E4,function(a){if("OK"==a)return function(a){m(a)};c[f]=void 0;k=!1;g("Host not found")})); +return f},close:function(a){c[a]&&d.cmd("AT+QICLOSE="+a+"\r\n",1E3,function(){c[a]=void 0})},accept:function(a){return-1},recv:function(a,b){if(e[a]){if(e[a].length>b){var d=e[a].substr(0,b);e[a]=e[a].substr(b)}else d=e[a],e[a]="";return d}return c[a]?"":-1},send:function(a,b){if(k||d.isBusy()||"Wait"==c[a])return 0;if(!c[a])return-1;k=!0;d.write("AT+QISEND="+a+","+b.length+"\r\n");setTimeout(function(){d.write(b)},1E3);return b.length}},n={debug:function(a){g=a||void 0===a?function(a){print("[M35]", +a)}:function(){};return{socks:c,sockData:e}},getVersion:function(a){h("AT+GMR").then(function(b){a(null,b)})["catch"](function(b){a(b)})},getIP:function(a){d.cmd("AT+QILOCIP\r\n",1E3,function(b){"ERROR"==b?a(b):a(null,b)})}};exports.connect=function(a,b,c){a.removeAllListeners();n.at=d=require("AT").connect(a);require("NetworkJS").create(t);d.registerLine("+RECEIVE",p);for(a=0;6>a;a++)d.registerLine(a+", ",q);d.registerLine("+CME ERROR",function(a){console.log(a)});h("ATE0").then(function(){return h("AT+CREG?")}).then(function(a){var b= +a.split(",")[1];if(1!=b&&5!=b)throw Error("GSM not registered, "+a);g("Forcing GPRS connect");return h("AT+CGATT=1",1E4)}).then(function(){return h("AT+CGREG?")}).then(function(a){var b=a.split(",")[1];if(2==b)throw Error("GPRS still connecting, "+a);if(1!=b&&5!=b)throw Error("GPRS not registered, "+a);return h("AT+COPS?")}).then(function(a){var b=a.split(",")[2];if(!b)throw Error("No Operator, "+a);g("Operator "+b);return h("AT+QIMUX=1")}).then(function(){return h("AT+QIPROMPT=0")}).then(function(){c&& +c(null)})["catch"](function(a){c&&c(a)});return n} \ No newline at end of file diff --git a/libs/js/UG96.min.js b/libs/js/UG96.min.js new file mode 100644 index 000000000..f18542d2e --- /dev/null +++ b/libs/js/UG96.min.js @@ -0,0 +1,24 @@ +function k(a){console.log(a)}function b(a){B&&console.log(a)}function v(a,e,f,h){p?(b("Opening is not yet possible, busy state on socket "+a),5>h?setTimeout(function(){k("opening later...");v(a,e,f,h+1)},500):(b("Force the closure of socket "+a),g[a]="tobeclosed")):c.isBusy()?(b("Opening is not yet possible, AT busy state on socket "+a),5>h?setTimeout(function(){k("opening later...");v(a,e,f,h+1)},500):(b("Force the closure of socket "+a),g[a]="tobeclosed")):(b("AT+QIOPEN=1,"+a+',"TCP",'+ +JSON.stringify(e)+","+f+",0,1"),c.cmd("AT+QIOPEN=1,"+a+',"TCP",'+JSON.stringify(e)+","+f+",0,1\r\n",15E3,function d(c){if("OK"==c)return d;c=="+QIOPEN: "+a+",0"?(b(c),b("AT+QIOPEN completed with socket: "+a),g[a]=!0):c=="+QIOPEN: "+a+",565"?(b("AT+QIOPEN failure DNS parse failed..."),5>h?setTimeout(function(){k("repeat opening the socket ...");v(a,e,f,h+1)},1E3):(b("Force the closure of socket "+a),g[a]="tobeclosed")):c=="+QIOPEN: "+a+",566"?(b("AT+QIOPEN failure could not connect socket ..."),5> +h?setTimeout(function(){k("repeat opening the socket ...");v(a,e,f,h+1)},3E3):(b("Force the closure of socket "+a),g[a]="tobeclosed")):(c=="+QIOPEN: "+a+",563"?b("AT+QIOPEN socket identity has been used..., socket is:"+a):b("AT+QIOPEN failed on socket:"+a),g[a]="tobeclosed");return""}))}function q(a,e){g[a]?0e?(k("socket "+a+" is emptying..."),setTimeout(function(){k("closing later...");q(a,e+1)},500)):p?(b("at register currenly programmed"),5>e?setTimeout(function(){k("closing later..."); +q(a,e+1)},500):(b("several busy situation on socket "+a),g[a]=void 0)):c.isBusy()?(b("AT busy"),5>e?setTimeout(function(){k("closing again...");q(a,e+1)},500):(b("several AT busy situation on socket "+a),g[a]=void 0)):(b("sending AT+QICLOSE for socket "+a),c.cmd("AT+QICLOSE="+a+"\r\n",2E3,function G(c){if(void 0===c)return 5>e?setTimeout(function(){k("closing again...");q(a,e+1)},500):(b("cannot properly close socket "+a),g[a]=void 0),"";if("OK"==c)return b("socket "+a+" is properly closed"),g[a]= +void 0,"";b("socket "+a+" is not yet closed");return G})):b("socket already closed")}function C(a){var e=a.indexOf("\r\n");if(0>e)return a;var b=a.substring(15,e).split(",");b[1]|=0;var h=a.length-(e+2);if(h>=b[1])return 1==g[b[0]]&&(m[b[0]]+=a.substr(e+2,b[1])),a.substr(e+b[1]+3);m[b[0]]+=a.substr(e+2,h);c.getData(b[1]-h,function(a){m[b[0]]+=a});return""}function H(a){b("closehandler:"+a);var e=a.indexOf("\r\n");if(0>e&&(b("not enough data "+a),e=a.indexOf("\r"),0>e))return b("definitively not enough data "+ +a),a;a=a.substring(0,e).split(",");a[1]|=0;q(a[1],0);return""}function I(a){b("pdpdeacthandler:"+a);c.cmd("AT+QIDEACT=1\r\n",1E3,function(a){b(a);for(a=0;12>a;a++)g[a]=void 0,m[a]=""});return""}function J(a){b("QindHandler in: "+a);var e=a.indexOf("\r\n"),c=a.substr(e,a.length);k(a.substr(e,a.length));return c}function K(a){b("QusimHandler in: "+a);var e=a.indexOf("\r\n"),c=a.substr(e,a.length);k(a.substr(e,a.length));return c}function L(a){b("CfunHandler in: "+a);var e=a.indexOf("\r\n"),c=a.substr(e, +a.length);k(a.substr(e,a.length));return c}function M(a){b("RdyHandler in: "+a);var c=a.indexOf("\r\n"),f=a.substr(c,a.length);k(a.substr(c,a.length));return f}function N(a){b("PowerDownHandler in: "+a);k("Modem is entering in power down");digitalWrite(w,y);return""}var c,p=!1,g=[],m=" ".split(" "),r,z,w,y,D=!1,t="",A=!1,B=!1,F,u=0,n=0,O={create:function(a,e){var f=0;if(void 0===a)return b("WARNING: this has not been fully ported for UGxx"),f=12,g[f]="Wait",m[f]="",c.cmd("AT+CIPSERVER=1,"+ +e+"\r\n",1E4,function(a){if("OK"==a)g[f]=!0;else throw g[f]=void 0,Error("CIPSERVER failed");}),12;for(;void 0!==g[f];)f++;12<=f&&(b("WORKAROUND closing the socket: "+f),f--,q(f,0));g[f]="Wait";m[f]="";443==e?(b("delaying the TLS socket opening"),setTimeout(function(){v(f,a,e,0)},3E3)):v(f,a,e,0);b("(create) sckt "+f+" state = "+g[f]);return f},close:function(a){b("(local) Closing of the socket: "+a);q(a,0)},accept:function(a){b("Accept Socket "+a);for(a=0;12>a;a++)if(m[a]&&void 0===g[a])return g[a]= +!0,a;return-1},recv:function(a,b){if(c.isBusy()||"Wait"==g[a])return"";if(m[a]){if(m[a].length>b){var e=m[a].substr(0,b);m[a]=m[a].substr(b)}else e=m[a],m[a]="";return e}return g[a]&&"tobeclosed"!=g[a]?"":-1},send:function(a,e){if(p||c.isBusy()||"Wait"==g[a])return 0;if("tobeclosed"==g[a]||!g[a])return-1;if(1460");g[a]="tobeclosed"},5E3);c.register(">",function(){b("Prompt coming, sending data ...");c.unregister(">");clearTimeout(u);u=0;b("writing data amount of "+e.length);c.write(e);return""});c.registerLine("SEND OK",function(){b("UGxx - SEND OK"); +c.unregisterLine("SEND OK");c.unregisterLine("SEND FAIL");c.unregisterLine("ERROR");clearTimeout(n);n=0;p=!1;return""});c.registerLine("SEND FAIL",function(a){b("UGxx - SEND FAIL");c.unregisterLine("SEND OK");c.unregisterLine("SEND FAIL");c.unregisterLine("ERROR");clearTimeout(n);n=0;p=!1;g[a]="tobeclosed";return""});c.registerLine("ERROR",function(a){b("UGxx - ERROR communication");u&&(c.unregister(">"),clearTimeout(u),u=0);c.unregisterLine("SEND OK");c.unregisterLine("SEND FAIL");c.unregisterLine("ERROR"); +clearTimeout(n);n=0;p=!1;g[a]="tobeclosed";return""});b("AT+QISEND="+a+","+e.length);c.write("AT+QISEND="+a+","+e.length+"\r\n");b("(send) sckt "+a+" state = "+g[a]);b("send "+e.length);return e.length}},x={receiveHandler:C,debug:function(a,b){B=void 0===a?!1:a;(void 0===b?0:b)&&c.debug();return{socks:g,sockData:m}},init:function(a){var e=0,f=0,h=0,g=!1,l=function(d){switch(h){case 0:b("debug AT_SYNCHRO :"+d);if("AT"===d)return l;"OK"===d?(k("Synchronisation with module passed"),h=1,c.cmd("ATV1\r\n", +1E3,l)):(h=0,e++,b("AT sync failed: "+d),10>=e?setTimeout(function(){c.cmd("AT\r\n",1E3,l)},500):(b("No OK return after 10 times"),b("Check the module is power on"),a("Error in AT sync: "+d)));break;case 1:b("debug AT_RSP_FORMAT :"+d);if("OK"===d)h=2,c.cmd("ATE0\r\n",1E3,l);else{if("ATV1"===d)return l;d&&a("Error in ATV1: "+d)}void 0===d&&(b("Cannot set TA response format"),a("ATV1 time-out !!!"));break;case 2:b("debug AT_ECHO_OFF :"+d);if("IIIIATE0"===d||d==="IIII"+String.fromCharCode(255)+"ATE0"|| +"ATE0"===d)return l;"OK"===d?(b("ATE0 passed: "+d),D?(h=11,c.cmd("AT+IFC=2,2\r\n",2E3,l)):(h=3,c.cmd("AT+CPIN?\r\n",5E3,l))):"atE0"===d?(b("UGxx returns "+d),h=3,c.cmd("AT+CPIN?\r\n",5E3,l)):d&&a("Error in ATE0: "+d);void 0===d&&(b("ATE0 time-out !!!"),h=3,c.cmd("AT+CPIN?\r\n",5E3,l));break;case 11:b("debug AT_HW_FLOW_CONTROL :"+d);"OK"===d?b("HW flow control establismnent succeeded"):a("HW flow control establismnent failed");h=3;c.cmd("AT+CPIN?\r\n",5E3,l);break;case 3:b("debug AT_CPIN :"+d);if("+CPIN: READY"=== +d)return l;"OK"===d?(h=4,c.cmd("AT+QCCID\r\n",2E3,l)):d&&a("Error in CPIN: "+d);void 0===d&&(b("SIM cannot be read, SIM is either protected, blocked or not accessible"),a("AT+CPIN time-out !!!"));break;case 4:b("debug AT_SHOW_SIM_ID :"+d);if(d&&"+QCCID:"==d.substr(0,7))return b("SIM ID :"+d),F=d.substring(8,d.length-1),l;"OK"===d?(h=5,c.cmd("AT+CFUN=1\r\n",15E3,l)):d&&a("Error in QCCID: "+d);void 0===d&&a("AT+QCCID time-out !!!");break;case 5:b("debug AT_RADIO_ON :"+d);"OK"===d?(h=6,c.cmd("AT+CSQ\r\n", +2E3,l)):d&&a("Error in CFUN: "+d);void 0===d&&a("AT+CFUN time-out !!!");break;case 6:b("debug AT_QUERY_SIGNAL_QUALITY :"+d);if(d&&"+CSQ:"==d.substr(0,5))return d&&"+CSQ: 99,99"==d.substr(0,11)?(b("Signal not known or not detectable yet"),g=!1):(g=!0,d=d.substring(6,d.length).split(","),d[0]|=0,k("quality_level_dbm = "+(-113+2*d[0])+"dBm")),l;"OK"===d?g?(h=7,b("PS attachment is starting. It may take until a minute, please wait ... "),setTimeout(function(){c.cmd("AT+CGATT=1\r\n",75E3,l)},5E3)):setTimeout(function(){c.cmd("AT+CSQ\r\n", +2E3,l)},1E3):d&&a("Error in QCCID: "+d);void 0===d&&a("AT+CSQ time-out !!!");break;case 7:b("debug AT_PS_ATTACMENT :"+d);"OK"===d?(k("PS attachment succeeded"),h=10,c.cmd("AT+COPS?\r\n",2E4,l)):d&&(f+=1,b("Error in CGATT: "+d+" - retry: "+f),1==f?(b("Trying an automatic registration first. It may take until 3 minutes, please wait ..."),h=8,setTimeout(function(){c.cmd("AT+COPS=0\r\n",18E4,l)},2E3)):4>f?(b("Retrying CGATT with retry "+f),h=7,b("PS attacment is attempting again. It may take until a minute, please wait ... "), +setTimeout(function(){c.cmd("AT+CGATT=1\r\n",75E3,l)},2E3*f)):a("Unrecoverable Error, PS attachment failed: "+d));break;case 8:b("COPS returns: "+d);h=9;setTimeout(function(){c.cmd("AT+COPS?\r\n",1E4,l)},5E3);break;case 9:b(d+" - Now retrying PS attachment");h=7;setTimeout(function(){c.cmd("AT+CGATT=1\r\n",3E4,l)},3E4);break;case 10:b("debug AT_CURRENT_OPERATOR :"+d);if("OK"===d)a(null);else{if(d&&"+COPS:"==d.substr(0,6))return k("currently selected operator :"+d),l;d&&a("Error in COPS? "+d)}void 0=== +d&&a("AT+COPS? time-out !!!")}};setTimeout(function(){c.cmd("AT\r\n",1E3,l)},500)},reset:function(a){function c(a){digitalWrite(r,z);setTimeout(function(){digitalWrite(r,!z);setTimeout(function(){x.init(a)},6E3)},200)}function f(a){digitalWrite(w,y);setTimeout(function(){digitalWrite(w,!y);setTimeout(function(){r?c(a):x.init(a)},5E3)},200)}for(var h=0;12>h;h++)g[h]=void 0,m[h]="";if(void 0===r&&void 0===w)return x.init(a);b("Here we go trhough reset sequence");r&&digitalWrite(r,!z);w?f(a):c(a);k("Cellular module initialization started, please wait ...")}, +getVersion:function(a){c.cmd("AT+GMR\r\n",1E3,function(b){a(null,b)})},connect:function(a,e,f,h){var g=0,l=function(a){switch(g){case 0:b("connect callback after PDP context configuration");"OK"===a?(g=1,b("PDP context successfully configured"),c.cmd("AT+QIACT=1\r\n",3E4,l)):a&&h("Error in "+g+": "+a);void 0===a&&b("PDP context activation failed, timeout...");break;case 1:b("connect callback after PDP context activation"),"OK"===a?(k("PDP context successfully activated"),h(null)):a?h("Error in "+ +g+": "+a):h(null)}};b("in the connect function: AT+QICSGP stage");c.cmd('AT+QICSGP=1,1,"'+a+'", "'+e+'", "'+f+'"\r\n',3E4,l)},initflowctrl:function(a){D=void 0===a?!1:a},getIP:function(a){var e="",f=function(c){b("AT+CGPADDR callback: "+c);if(void 0===c)b("timeout : any IP address allocated ..."),a(null,e);else if("OK"===c)a(null,e),k("IP address allocated, modem is ready to use");else return e+=c,b(c),f};b("getIP is AT+CGPADDR");c.cmd("AT+CGPADDR=1\r\n",3E4,f)},geoLocGet:function(a){k("Getting geolocalization data"); +a(t)},geoLocStart:function(a){k("Starting GeoLocalization");1E4>a&&k("unpredictive behaviour with period not greater than 10 s !");A=!0;var e=function(f){if(void 0===f)b("GeoLocalization timeout"),t="";else if("OK"===f)k("GeoLocalization acquisition done : "+t);else{if(f&&"+QCELLLOC:"==f.substr(0,10)){var g=f.length,m=f.indexOf(" ");t=f.substring(1+m,g);b(t);return e}t="";k("Geolocalization error : "+f)}A&&setTimeout(function(){c.cmd("AT+QCELLLOC=1\r\n",2E3,e)},a)};c.cmd("AT+QCELLLOC=1\r\n",2E3,e)}, +geoLocStop:function(){k("Stopping GeoLocalization");A=!1;t=""},turnOff:function(){k("Turning Off the modem");c.cmd("AT+QPOWD=1\r\n",2E3,function(a){"OK"===a?k("Please wait, disconnecting and saving data. It may last until 60 s"):k("Turn off error : "+a)})},getCCID:function(){return F}};resetOptions={rst:void 0,pwrkey:void 0,rst_active_level:1,pwrkey_active_level:1};exports.connect=function(a,b,f){b=b||{};r=b.rst||void 0;w=b.pwrkey||void 0;z=b.rst_active_level||0;y=b.pwrkey_active_level||0;x.at=c= +require("AT").connect(a);require("NetworkJS").create(O);c.register('+QIURC: "recv"',C);c.register('+QIURC: "closed"',H);c.register('+QIURC: "pdpdeact"',I);c.register("+QIND:",J);c.register("+QUSIM:",K);c.register("+CFUN:",L);c.register("RDY",M);c.register("POWERED DOWN",N);x.reset(f);return x} \ No newline at end of file diff --git a/libs/js/espruino_wifi/AT.min.js b/libs/js/espruino_wifi/AT.min.js deleted file mode 100644 index e9eba7841..000000000 --- a/libs/js/espruino_wifi/AT.min.js +++ /dev/null @@ -1,4 +0,0 @@ -exports.connect=function(p){var n=!1,b="",d,f=0,m,c={},k={},l=[];p.on("data",function(a){if(f){if(a.length<=f){f-=a.length;m(a);0==f&&(m=void 0);return}m(a.substr(0,f));a=a.substr(f);f=0;m=void 0}b+=a;n&&console.log("] "+JSON.stringify(a));"\n"==b[0]&&(b=b.substr(1));if(c)for(var e in c)b.substr(0,e.length)==e&&(b=c[e](b));for(a=b.indexOf("\r");0<=a;){var g=b.substr(0,a);if(0"]&&">"==b[0]&&(b=c[">"](b)),c)b.substr(0,e.length)==e&&(b=c[e](b));a=b.indexOf("\r")}});var h={debug:function(a){n=!1!==a;return{line:b,lineCallback:d,handlers:c,lineHandlers:k,waiting:l}},cmd:function(a,b,g){if(d)n&&console.log("Queued "+JSON.stringify(a)),l.push([a,b,g]);else if(n&&console.log("["+JSON.stringify(a)),p.write(a),b){var e=setTimeout(function(){d=void 0;g&&g();void 0===d&&0b)return a;var d=a.substring(5,b).split(",");d[1]|=0;var c=a.length-(b+1),e=d[0];if(t[e]){var l=(d[2]||"0.0.0.0").split(".").map(function(a){return 0|a}),z=0|d[3];g[e]+=String.fromCharCode(l[0],l[1],l[2],l[3],z&255,z>>8,c&255,c>>8)}if(c>=d[1])return g[e]+=a.substr(b+1, -d[1]),a.substr(b+d[1]+1);g[e]+=a.substr(b+1,c);f.getData(d[1]-c,function(a){g[e]+=a});return""}function u(a,b){if(b)return a(b);f.cmd("AT+CWMODE="+m+"\r\n",1E3,function(b){"no change"!=b&&"OK"!=b&&"WIFI DISCONNECT"!=b?a("CWMODE failed: "+(b?b:"Timeout")):a(null)})}function n(a){var b=a[0];void 0===e[b]&&e[5]?e[b]="Accept":"Wait"==e[b]?e[b]=!0:f.cmd("AT+CIPCLOSE="+b+"\r\n",1E3,function(a){e[b]=void 0})}function p(a){e[a[0]]=""!=g[a[0]]?"DataClose":void 0}function r(a,b){var d=0==m;m|=a;d?("1v91"== -process.version?(v.reset(),q.setup(115200,{rx:A3,tx:A2})):q.setup(115200,{rx:A3,tx:A2,cts:v}),f=require("AT").connect(q),f.register("+IPD",C),f.registerLine("0,CONNECT",n),f.registerLine("1,CONNECT",n),f.registerLine("2,CONNECT",n),f.registerLine("3,CONNECT",n),f.registerLine("4,CONNECT",n),f.registerLine("0,CLOSED",p),f.registerLine("1,CLOSED",p),f.registerLine("2,CLOSED",p),f.registerLine("3,CLOSED",p),f.registerLine("4,CLOSED",p),f.registerLine("WIFI CONNECTED",function(){w=!0;exports.emit("associated")}), -f.registerLine("WIFI GOT IP",function(){exports.emit("connected")}),f.registerLine("WIFI DISCONNECTED",function(){w=!1;exports.emit("disconnected")}),exports.at=f,require("NetworkJS").create(A),f.cmd("\r\nAT+RST\r\n",1E4,function l(a){if("ready"==a||"Ready."==a)setTimeout(function(){f.cmd("ATE0\r\n",1E3,function D(a){if("ATE0"==a)return D;"OK"==a?f.cmd("AT+CIPDINFO=1\r\n",1E3,function(a){if("OK"!=a)return b("CIPDINFO failed: "+(a?a:"Timeout"));f.cmd("AT+CIPMUX=1\r\n",1E3,function(a){if("OK"!=a)return b("CIPMUX failed: "+ -(a?a:"Timeout"));f.cmd("AT+UART_CUR=115200,8,1,0,2\r\n",500,function(a){if("OK"!=a)return b("UART_CUR failed: "+(a?a:"Timeout"));u(b)})})}):b("ATE0 failed: "+(a?a:"Timeout"))})},500);else if(void 0===a)b("No 'ready' after AT+RST");else return l}),digitalWrite(F,1),digitalWrite(x,1)):u(b)}function B(a,b){b=b||function(){};(m&=~a)?u(b,null):(q.removeAllListeners(),f=void 0,exports.at=void 0,digitalWrite(x,0),e=[],setTimeout(b,1))}var F=A13,x=A14,v=A15,q=Serial2;digitalWrite(x,0);var G=["open","wep", -"wpa_psk","wpa2_psk","wpa_wpa2_psk"],m=0,w=!1,f,e=[],t=[],g=["","","","",""],A={create:function(a,b,d){if(!f||!w)return-1;if(void 0===a&&2!=d)return c=5,e[c]="Wait",g[c]="",f.cmd("AT+CIPSERVER=1,"+b+"\r\n",1E4,function(a){"OK"==a?e[c]=!0:(e[c]=void 0,setTimeout(function(){throw Error("CIPSERVER failed ("+(a?a:"Timeout")+")");},0))}),5;for(var c=0;void 0!==e[c];)c++;if(5<=c)return-7;g[c]="";e[c]="Wait";var h;2==d?(b?h="AT+CIPSTART="+c+',"UDP","255.255.255.255",'+b+","+b+",2\r\n":e[c]="UDP",t[c]=!0): -(h="AT+CIPSTART="+c+',"TCP",'+JSON.stringify(a)+","+b+"\r\n",delete t[c]);h&&f.cmd(h,1E4,function k(a){if("ALREADY CONNECTED"==a)return k;if("OK"!=a||!0!==e[c])e[c]=-6});return c},close:function(a){"Wait"==e[a]?e[a]="WaitClose":void 0!==e[a]&&(0>e[a]||"UDP"==e[a]?e[a]=void 0:f.cmd((5==a?"AT+CIPSERVER=0":"AT+CIPCLOSE="+a)+"\r\n",1E3,function(b){e[a]=void 0}))},accept:function(a){for(a=0;5>a;a++)if("Accept"==e[a])return e[a]=!0,a;return-1},recv:function(a,b,d){return g[a]?(g[a].length>b?(d=g[a].substr(0, -b),g[a]=g[a].substr(b)):(d=g[a],g[a]="","DataClose"==e[a]&&(e[a]=void 0)),d):0>e[a]?e[a]:e[a]?"":-1},send:function(a,b,d){if(!f)return-1;if(f.isBusy()||"Wait"==e[a])return 0;if(0>e[a])return e[a];if(!e[a])return-1;if("UDP"==e[a])return d=y(b),e[a]="Wait",f.cmd("AT+CIPSTART="+a+',"UDP","'+d.ip+'",'+d.port+","+d.port+",2\r\n",1E4,function(b){"OK"!=b&&(e[a]=-6)}),0;var c=b.length,h="";2==d&&(d=y(b),h=',"'+d.ip+'",'+d.port,b=b.substr(8,d.len),c=8+d.len);f.cmd("AT+CIPSEND="+a+","+b.length+h+"\r\n",1E4, -function k(c){if("OK"==c)return f.register("> ",function(){f.unregister("> ");f.write(b);return""}),k;if(c=="Recv "+b.length+" bytes"||"busy s..."==c)return k;"SEND OK"==c?("WaitClose"==e[a]&&A.close(a),e[a]=!0):(e[a]=void 0,f.unregister("> "))});e[a]="Wait";return c}};exports.connect=function(a,b,d){var c="";d=d||function(){};void 0!==b.password&&(c=b.password);r(1,function(b){if(b)return d(b);f.cmd("AT+CWJAP="+JSON.stringify(a)+","+JSON.stringify(c)+"\r\n",2E4,function k(a){if(0<=["WIFI DISCONNECT", -"WIFI CONNECTED","WIFI GOT IP","+CWJAP:1"].indexOf(a))return k;"OK"!=a?setTimeout(d,0,"WiFi connect failed: "+(a?a:"Timeout")):setTimeout(d,0,null)})})};exports.disconnect=function(a){B(1,a)};exports.startAP=function(a,b,d){b=b||{};if(!b.password||8>b.password.length)throw Error("Password must be at least 8 characters");var c=b.password?"3":"0";if(b.authMode&&(c={open:0,wpa:2,wpa2:3,wpa_wpa2:4}[b.authMode],void 0===c))throw Error("Unknown authMode "+b.authMode);void 0===b.channel&&(b.channel=5);r(2, -function(e){if(e)return d(e);f.cmd("AT+CWSAP="+JSON.stringify(a)+","+JSON.stringify(b.password)+","+b.channel+","+c+"\r\n",5E3,function(a){"OK"!=a?d("CWSAP failed: "+(a?a:"Timeout")):d(null)})})};exports.stopAP=function(a){B(2,a)};exports.scan=function(a){var b=[];r(1,function(d){if(d)return a(d);f.cmdReg("AT+CWLAP\r\n",5E3,"+CWLAP:",function(a){a=a.slice(8,-1).split(",");b.push({ssid:JSON.parse(a[1]),authMode:G[a[0]],rssi:parseInt(a[2]),mac:JSON.parse(a[3]),channel:JSON.parse(a[4])})},function(c){a(null, -b)})})};exports.getIP=function(a){var b={};f.cmd("AT+CIFSR\r\n",1E3,function h(c){if(void 0===c)a("Timeout");else{if("+CIFSR:STAIP"==c.substr(0,12))b.ip=c.slice(14,-1);else if("+CIFSR:STAMAC"==c.substr(0,13))b.mac=c.slice(15,-1);else if("OK"==c){a(null,b);return}return h}})};exports.setIP=function(a,b){if("object"==typeof a&&a.ip){var d=[JSON.stringify(a.ip)];a.gw&&(d.push(JSON.stringify(a.gw)),d.push(JSON.stringify(a.netmask||"255.255.255.0")));d="AT+CIPSTA_CUR="+d.join(",")+"\r\n";var c=3E3}else d= -"AT+CWDHCP_CUR=1,1\r\n",c=2E4;f.cmd(d,c,function(a){if("OK"==a)b(null);else return b("setIP failed: "+(a?a:"Timeout"))})};exports.getAPIP=function(a){var b={};f.cmd("AT+CIPAP_CUR?\r\n",1E3,function h(c){if(void 0===c)a("Timeout");else if("OK"==c)f.cmd("AT+CIPAPMAC_CUR?\r\n",1E3,function k(c){if(void 0===c)a("Timeout");else if("OK"==c)a(null,b);else return"+CIPAPMAC_CUR"==c.substr(0,14)&&(b.mac=JSON.parse(c.substr(10))),k});else return"+CIPAP_CUR"==c.substr(0,10)&&(c=c.split(":"),"gateway"==c[1]&& -"gw"==c[1],b[c[1]]=JSON.parse(c[2])),h})};exports.setAPIP=function(a,b){var d=[JSON.stringify(a.ip)];a.gw&&(d.push(JSON.stringify(a.gw)),d.push(JSON.stringify(a.netmask||"255.255.255.0")));f.cmd("AT+CIPAP_CUR="+d.join(",")+"\r\n",3E3,function(a){if("OK"==a)b(null);else return b("setAPIP failed: "+(a?a:"Timeout"))})};exports.setHostname=function(a,b){r(1,function(d){if(d)return b(d);f.cmd("AT+CWHOSTNAME="+JSON.stringify(a)+"\r\n",500,b)})};exports.ping=function(a,b){var d;f.cmd('AT+PING="'+a+'"\r\n', -1E3,function l(a){if(a&&"+"==a[0])return d=a.substr(1),l;"OK"==a?b(d):b()})};exports.turbo=function(a,b){var d=a?!0===a?921600:a:115200;f.cmd("AT+UART_CUR="+d+",8,1,0,2\r\n",500,function(a){"OK"!=a?b&&b("Baud rate switch to "+d+" failed: "+(a?a:"Timeout")):(q.setup(d,{rx:A3,tx:A2,cts:v}),b&&b(null))})};exports.debug=function(){return{wifiMode:m,socks:e,sockData:g}} \ No newline at end of file +function z(a){return{ip:a.charCodeAt(0)+"."+a.charCodeAt(1)+"."+a.charCodeAt(2)+"."+a.charCodeAt(3),port:a.charCodeAt(5)<<8|a.charCodeAt(4),len:a.charCodeAt(7)<<8|a.charCodeAt(6)}}function D(a){var b=a.indexOf(":");if(0>b)return a;var c=a.substring(5,b).split(",");c[1]|=0;var d=a.length-(b+1),e=c[0];if(v[e]){var l=(c[2]||"0.0.0.0").split(".").map(function(a){return 0|a}),A=0|c[3];g[e]+=String.fromCharCode(l[0],l[1],l[2],l[3],A&255,A>>8,d&255,d>>8)}if(d>=c[1])return g[e]+=a.substr(b+1, +c[1]),a.substr(b+c[1]+1);g[e]+=a.substr(b+1,d);f.getData(c[1]-d,function(a){g[e]+=a});return""}function w(a,b){if(b)return a(b);f.cmd("AT+CWMODE="+p+"\r\n",1E3,function(b){"no change"!=b&&"OK"!=b&&"WIFI DISCONNECT"!=b?a("CWMODE failed: "+(b?b:"Timeout")):a(null)})}function q(a){var b=a[0];void 0===e[b]&&e[5]?e[b]="Accept":"Wait"==e[b]?e[b]=!0:f.cmd("AT+CIPCLOSE="+b+"\r\n",1E3,function(a){e[b]=void 0})}function r(a){e[a[0]]=""!=g[a[0]]?"DataClose":void 0}function u(a,b){var c=0==p;p|=a;c?("1v91"== +process.version?(x.reset(),t.setup(115200,{rx:A3,tx:A2})):t.setup(115200,{rx:A3,tx:A2,cts:x}),f=require("AT").connect(t),f.register("+IPD",D),f.registerLine("0,CONNECT",q),f.registerLine("1,CONNECT",q),f.registerLine("2,CONNECT",q),f.registerLine("3,CONNECT",q),f.registerLine("4,CONNECT",q),f.registerLine("0,CLOSED",r),f.registerLine("1,CLOSED",r),f.registerLine("2,CLOSED",r),f.registerLine("3,CLOSED",r),f.registerLine("4,CLOSED",r),f.registerLine("WIFI CONNECTED",function(){n|=k.CLIENT;exports.emit("associated")}), +f.registerLine("WIFI GOT IP",function(){exports.emit("connected")}),f.registerLine("WIFI DISCONNECTED",function(){n&=~k.CLIENT;exports.emit("disconnected")}),exports.at=f,require("NetworkJS").create(B),f.cmd("\r\nAT+RST\r\n",1E4,function l(a){if("ready"==a||"Ready."==a)setTimeout(function(){f.cmd("ATE0\r\n",1E3,function F(a){if("ATE0"==a)return F;"OK"==a?f.cmd("AT+CIPDINFO=1\r\n",1E3,function(a){if("OK"!=a)return b("CIPDINFO failed: "+(a?a:"Timeout"));f.cmd("AT+CIPMUX=1\r\n",1E3,function(a){if("OK"!= +a)return b("CIPMUX failed: "+(a?a:"Timeout"));f.cmd("AT+UART_CUR=115200,8,1,0,2\r\n",500,function(a){if("OK"!=a)return b("UART_CUR failed: "+(a?a:"Timeout"));w(b)})})}):b("ATE0 failed: "+(a?a:"Timeout"))})},500);else if(void 0===a)b("No 'ready' after AT+RST");else return l}),digitalWrite(G,1),digitalWrite(y,1)):w(b)}function C(a,b){b=b||function(){};(p&=~a)?w(b,null):(t.removeAllListeners(),f=void 0,exports.at=void 0,digitalWrite(y,0),e=[],setTimeout(b,1))}var G=A13,y=A14,x=A15,t=Serial2;digitalWrite(y, +0);var k={CLIENT:1,AP:2},H=["open","wep","wpa_psk","wpa2_psk","wpa_wpa2_psk"],p=0,n=0,f,e=[],v=[],g=["","","","",""],B={create:function(a,b,c){if(!f||!n)return-1;if(void 0===a&&2!=c)return d=5,e[d]="Wait",g[d]="",f.cmd("AT+CIPSERVER=1,"+b+"\r\n",1E4,function(a){"OK"==a?e[d]=!0:(e[d]=void 0,setTimeout(function(){throw Error("CIPSERVER failed ("+(a?a:"Timeout")+")");},0))}),5;for(var d=0;void 0!==e[d];)d++;if(5<=d)return-7;g[d]="";e[d]="Wait";var h;2==c?(b?h="AT+CIPSTART="+d+',"UDP","255.255.255.255",'+ +b+","+b+",2\r\n":e[d]="UDP",v[d]=!0):(h="AT+CIPSTART="+d+',"TCP",'+JSON.stringify(a)+","+b+"\r\n",delete v[d]);h&&f.cmd(h,1E4,function m(a){if("ALREADY CONNECTED"==a)return m;if("OK"!=a||!0!==e[d])e[d]=-6});return d},close:function(a){"Wait"==e[a]?e[a]="WaitClose":void 0!==e[a]&&(0>e[a]||"UDP"==e[a]?e[a]=void 0:f.cmd((5==a?"AT+CIPSERVER=0":"AT+CIPCLOSE="+a)+"\r\n",1E3,function(b){e[a]=void 0}))},accept:function(a){for(a=0;5>a;a++)if("Accept"==e[a])return e[a]=!0,a;return-1},recv:function(a,b,c){return g[a]? +(g[a].length>b?(c=g[a].substr(0,b),g[a]=g[a].substr(b)):(c=g[a],g[a]="","DataClose"==e[a]&&(e[a]=void 0)),c):0>e[a]?e[a]:e[a]?"":-1},send:function(a,b,c){if(!f)return-1;if(f.isBusy()||"Wait"==e[a])return 0;if(0>e[a])return e[a];if(!e[a])return-1;if("UDP"==e[a])return c=z(b),e[a]="Wait",f.cmd("AT+CIPSTART="+a+',"UDP","'+c.ip+'",'+c.port+","+c.port+",2\r\n",1E4,function(b){"OK"!=b&&(e[a]=-6)}),0;var d=b.length,h="";2==c&&(c=z(b),h=',"'+c.ip+'",'+c.port,b=b.substr(8,c.len),d=8+c.len);f.cmd("AT+CIPSEND="+ +a+","+b.length+h+"\r\n",2E3,function m(d){if("OK"==d)f.register("> ",function(a){f.unregister("> ");f.write(b);return a.substr(2)});else if(d!="Recv "+b.length+" bytes"&&"busy s..."!=d){"SEND OK"==d?("WaitClose"==e[a]&&B.close(a),e[a]=!0):(e[a]=void 0,f.unregister("> "));return}return m});e[a]="Wait";return d}};exports.connect=function(a,b,c){var d="";c=c||function(){};void 0!==b.password&&(d=b.password);u(k.CLIENT,function(b){if(b)return c(b);f.cmd("AT+CWJAP="+JSON.stringify(a)+","+JSON.stringify(d)+ +"\r\n",2E4,function m(a){if(0<=["WIFI DISCONNECT","WIFI CONNECTED","WIFI GOT IP","+CWJAP:1"].indexOf(a))return m;"OK"!=a?setTimeout(c,0,"WiFi connect failed: "+(a?a:"Timeout")):setTimeout(c,0,null)})})};exports.disconnect=function(a){C(k.CLIENT,a)};exports.startAP=function(a,b,c){c=c||function(){};b=b||{};if(!b.password||8>b.password.length)throw Error("Password must be at least 8 characters");var d=b.password?"3":"0";if(b.authMode&&(d={open:0,wpa:2,wpa2:3,wpa_wpa2:4}[b.authMode],void 0===d))throw Error("Unknown authMode "+ +b.authMode);void 0===b.channel&&(b.channel=5);u(k.AP,function(e){if(e)return c(e);f.cmd("AT+CWSAP="+JSON.stringify(a)+","+JSON.stringify(b.password)+","+b.channel+","+d+"\r\n",5E3,function(a){"OK"!=a?c("CWSAP failed: "+(a?a:"Timeout")):(n|=k.AP,c(null))})})};exports.stopAP=function(a){n&=~k.AP;C(k.AP,a)};exports.scan=function(a){var b=[];u(k.CLIENT,function(c){if(c)return a(c);f.cmdReg("AT+CWLAP\r\n",5E3,"+CWLAP:",function(a){a=a.slice(8,-1).split(",");b.push({ssid:JSON.parse(a[1]),authMode:H[a[0]], +rssi:parseInt(a[2]),mac:JSON.parse(a[3]),channel:JSON.parse(a[4])})},function(d){a(null,b)})})};exports.getIP=function(a){var b={};f.cmd("AT+CIFSR\r\n",1E3,function h(d){if(void 0===d)a("Timeout");else{if("+CIFSR:STAIP"==d.substr(0,12))b.ip=d.slice(14,-1);else if("+CIFSR:STAMAC"==d.substr(0,13))b.mac=d.slice(15,-1);else if("OK"==d){a(null,b);return}return h}})};exports.setIP=function(a,b){if("object"==typeof a&&a.ip){var c=[JSON.stringify(a.ip)];a.gw&&(c.push(JSON.stringify(a.gw)),c.push(JSON.stringify(a.netmask|| +"255.255.255.0")));c="AT+CIPSTA_CUR="+c.join(",")+"\r\n";var d=3E3}else c="AT+CWDHCP_CUR=1,1\r\n",d=2E4;f.cmd(c,d,function(a){if("OK"==a)b(null);else return b("setIP failed: "+(a?a:"Timeout"))})};exports.getAPIP=function(a){var b={};f.cmd("AT+CIPAP_CUR?\r\n",1E3,function h(d){if(void 0===d)a("Timeout");else if("OK"==d)f.cmd("AT+CIPAPMAC_CUR?\r\n",1E3,function m(d){if(void 0===d)a("Timeout");else if("OK"==d)a(null,b);else return"+CIPAPMAC_CUR"==d.substr(0,14)&&(b.mac=JSON.parse(d.substr(10))),m}); +else return"+CIPAP_CUR"==d.substr(0,10)&&(d=d.split(":"),"gateway"==d[1]&&"gw"==d[1],b[d[1]]=JSON.parse(d[2])),h})};exports.setAPIP=function(a,b){var c=[JSON.stringify(a.ip)];a.gw&&(c.push(JSON.stringify(a.gw)),c.push(JSON.stringify(a.netmask||"255.255.255.0")));f.cmd("AT+CIPAP_CUR="+c.join(",")+"\r\n",3E3,function(a){if("OK"==a)b(null);else return b("setAPIP failed: "+(a?a:"Timeout"))})};exports.setHostname=function(a,b){u(k.CLIENT,function(c){if(c)return b(c);f.cmd("AT+CWHOSTNAME="+JSON.stringify(a)+ +"\r\n",500,function(a){b("OK"==a?null:a)})})};exports.ping=function(a,b){var c;f.cmd('AT+PING="'+a+'"\r\n',1E3,function l(a){if(a&&"+"==a[0])return c=a.substr(1),l;"OK"==a?b(c):b()})};exports.turbo=function(a,b){var c=a?!0===a?921600:a:115200;f.cmd("AT+UART_CUR="+c+",8,1,0,2\r\n",500,function(a){"OK"!=a?b&&b("Baud rate switch to "+c+" failed: "+(a?a:"Timeout")):(t.setup(c,{rx:A3,tx:A2,cts:x}),b&&b(null))})};exports.debug=function(){return{wifiMode:p,connected:n,socks:e,sockData:g}} \ No newline at end of file diff --git a/libs/js/graphical_menu.min.js b/libs/js/graphical_menu.min.js new file mode 100644 index 000000000..03a05f148 --- /dev/null +++ b/libs/js/graphical_menu.min.js @@ -0,0 +1,4 @@ +exports.list=function(a,m){var b=m[""],e=Object.keys(m);b&&e.splice(e.indexOf(""),1);b instanceof Object||(b={});void 0===b.selected&&(b.selected=0);b.fontHeight||(b.fontHeight=6);var k=0|b.x,n=b.x2||a.getWidth()-1,g=0|b.y,q=b.y2||a.getHeight()-1;b.title&&(g+=b.fontHeight+2);var d={draw:function(){a.clear();a.setColor(-1);a.setFontAlign(0,-1);b.predraw&&b.predraw(a);b.title&&(a.drawString(b.title,(k+n)/2,g-b.fontHeight-2),a.drawLine(k,g-2,n,g-2));for(var p=0|Math.min((q-g)/b.fontHeight, +e.length),c=E.clip(b.selected-(p>>1),0,e.length-p),h=g;p--;){var f=k;c==b.selected&&(a.fillRect(k,h,n,h+b.fontHeight-1),a.setColor(0),d.selectEdit&&(a.drawImage({width:12,height:5,buffer:" \u0007\x00\u00f9\u00f0\u000e\x00@",transparent:0},k,h),f+=15));a.setFontAlign(-1,-1);var l=e[c++];a.drawString(l,f,h);f=m[l];"object"==typeof f&&(a.setFontAlign(1,-1),l=f.value,a.drawString(f.format?f.format(l):l,n,h));a.setColor(-1);h+=b.fontHeight}a.setFontAlign(-1,-1);b.preflip&&b.preflip(a);a.flip&&a.flip()}, +select:function(a){a=m[e[b.selected]];if("function"==typeof a)a(d);else if("object"==typeof a){if("number"==typeof a.value)d.selectEdit=d.selectEdit?void 0:a;else if("boolean"==typeof a.value&&(a.value=!a.value),a.onchange)a.onchange(a.value);d.draw()}},move:function(a){if(d.selectEdit){var c=d.selectEdit;c.value+=(a||1)*(c.step||1);void 0!==c.min&&c.valuec.max&&(c.value=c.max);if(c.onchange)c.onchange(c.value)}else b.selected=0|Math.clip(b.selected+ +a,0,e.length-1);d.draw()}};d.draw();return d} \ No newline at end of file diff --git a/libs/js/nordic/Thingy.js b/libs/js/nordic/Thingy.js new file mode 100644 index 000000000..d087d60ff --- /dev/null +++ b/libs/js/nordic/Thingy.js @@ -0,0 +1,337 @@ +/* Copyright (c) 2018 Gordon Williams, Pur3 Ltd. See the file LICENSE for copying permission. */ +/* Nordic Thingy Support Library */ + + +var MPU_PWR_CTRL = V8; +var MPU_INT = D6; +var MIC_PWR_CTRL = V9; +var MIC_DOUT = D25; +var MIC_CLK = D26; +var CCS_PWR_CTRL = V10; +var CCS_RST = V11; +var CCS_WAKE = V12; +var CCS_INT = D22; +var LIS_INT = D12; +var SENSE_LEDR = V13; +var SENSE_LEDG = V14; +var SENSE_LEDB = V15; +var SENSE_LEDS = [SENSE_LEDR,SENSE_LEDG,SENSE_LEDB]; +var LPS_INT = D23; +var HTS_INT = D24; +var BH_INT = D31; +var BATTERY_CHARGE = D17; +var BATTERY_VOLTAGE = D28; +var BATTERY_MONITOR = V4; +var SPEAKER = D27; +var SPK_PWR_CTRL= D29; + +var i2c = new I2C(); +i2c.setup({sda:7,scl:8,bitrate:400000}); +exports.I2C = i2c; +var i2ce = new I2C(); +i2ce.setup({sda:14,scl:15,bitrate:400000}); +exports.I2CE = i2ce; + +// ------------------------------------------------------------------------------------------- LIS2DH12 +// Get repeated callbacks with {x,y,z}. Call with no argument to disable +exports.onAcceleration = function(callback) { + if (callback) { + if (!this.accel) this.accel = require("LIS2DH12").connectI2C(i2ce/*, { int:LIS_INT } - not used */); + this.accel.callback = callback; + this.accel.setPowerMode("low"); + } else { + if (this.accel) this.accel.setPowerMode("powerdown"); + this.accel = undefined; + } +}; +// Get one callback with a new acceleration value ({x,y,z}) +exports.getAcceleration = function(callback) { + if (!this.accel) { + require("LIS2DH12").connectI2C(i2ce/*, { int:LIS_INT } - not used */).readXYZ(callback); + } else { + this.accel.readXYZ(callback); + } +} +// ------------------------------------------------------------------------------------------- MPU9250 +// Get repeated callbacks with {accel,gyro,mag} from the MPU. Call with no argument to disable +exports.onMPU = function(callback) { + if (callback) { + if (!this.mpu) { + MPU_PWR_CTRL.set(); // MPU on + this.mpu = require("MPU9250").connectI2C(i2c); + this.mpu.samplerate = 10; // Hz + this.mpu.callback = callback; + this.mpu.watch = setWatch(function(){ + this.mpu.callback(this.mpu.read()); + }.bind(this),MPU_INT,{repeat:1,edge:"rising"}); + setTimeout(this.mpu.initMPU9250.bind(this.mpu), 10); + } else { + this.mpu.callback = callback; + } + } else { + if (this.mpu) + clearWatch(this.mpu.watch); + MPU_PWR_CTRL.reset(); // MPU off + this.mpu = undefined; + } +}; +// Get one callback with a {accel,gyro,mag} value from the MPU +exports.getMPU = function(callback) { + if (!this.mpu) { + this.onMPU(function(d) { + this.onMPU(); + callback(d); + }.bind(this)); + } else { + callback(this.mpu.read()); + } +} +// ------------------------------------------------------------------------------------------- LPS22HB +// Get repeated callbacks with {pressure,temperature}. Call with no argument to disable +exports.onPressure = function(callback) { + if (callback) { + if (!this.pressure) { + this.pressure = require("LPS22HB").connectI2C(i2c, {int:LPS_INT}); + this.pressure.on('data',function(d) { this.pressureCallback(d); }.bind(this)); + } + this.pressureCallback = callback; + } else { + if (this.pressure) this.pressure.stop(); + this.pressure = undefined; + this.pressureCallback = undefined; + } +}; +// Get one callback with a new {pressure,temperature} value +exports.getPressure = function(callback) { + if (!this.pressure) { + var p = require("LPS22HB").connectI2C(i2c, {int:LPS_INT}); + p.read(function(d) { + p.stop(); + callback(d); + }); + } else { + this.pressure.read(callback); + } +} +// ------------------------------------------------------------------------------------------- HTS221 +// Get repeated callbacks with {humidity,temperature}. Call with no argument to disable +exports.onHumidity = function(callback) { + if (callback) { + if (!this.humidity) { + this.humidity = require("HTS221").connect(i2c, {int:HTS_INT}); + this.humidity.on('data',function(d) { this.humidityCallback(d); }.bind(this)); + } + this.humidityCallback = callback; + } else { + if (this.humidity) this.humidity.stop(); + this.humidity = undefined; + this.humidityCallback = undefined; + } +}; +// Get one callback with a new {humidity,temperature} value +exports.getHumidity = function(callback) { + if (!this.humidity) { + this.onHumidity(function(d) { + this.onHumidity(); + callback(d); + }.bind(this)); + } else { + this.humidity.read(callback); + } +} +// ------------------------------------------------------------------------------------------- HTS221 +// Get repeated callbacks with air quality `{eC02,TVOC}`. Call with no argument to disable +exports.onGas = function(callback) { + if (callback) { + if (!this.gas) { + CCS_PWR_CTRL.set(); // CCS on + CCS_RST.set(); // no reset + CCS_WAKE.reset(); // wake + this.gas = require("CCS811").connectI2C(i2c, { int : CCS_INT }); + this.gas.on('data',function(d) { this.gasCallback(d); }.bind(this)); + } + this.gasCallback = callback; + } else { + if (this.gas) { + this.gas.stop(); + CCS_RST.reset(); // reset (so it doesn't power it via RST pin) + CCS_PWR_CTRL.reset(); // CCS off + } + this.gas = undefined; + this.gasCallback = undefined; + } +}; +/* Get one callback with a new air quality value `{eC02,TVOC}`. This may not be useful +as the sensor takes a while to warm up and produce useful values */ +exports.getGas = function(callback) { + if (!this.gas) { + this.onGas(function(d) { + this.onGas(); + callback(d); + }.bind(this)); + } else { + callback(this.gas.get()); + } +} +// ------------------------------------------------------------------------------------------- HTS221 +// Get repeated callbacks with color `{r,g,b,c}`. Call with no argument to disable +exports.onColor = function(callback) { + if (callback) { + if (!this.color) { + digitalWrite(SENSE_LEDS,0); // all LEDs on + this.color = require("BH1745").connectI2C(i2c/*, {int : BH_INT}*/); + this.colorInt = setInterval(function() { + this.colorCallback(this.color.read()); + }.bind(this), 200); + } + this.colorCallback = callback; + } else { + if (this.color) { + clearInterval(this.colorInt); + this.color.stop(); + digitalWrite(SENSE_LEDS,7); // all LEDs off + } + this.color = undefined; + this.colorInt = undefined; + this.colorCallback = undefined; + } +}; +// Get one callback with a new color value `{r,g,b,c}` +exports.getColor = function(callback) { + if (!this.color) { + this.onColor(function(d) { + this.onColor(); + callback(d); + }.bind(this)); + } else { + callback(this.color.read()); + } +} +// ------------------------------------------------------------------------------------------- Battery +// Returns the state of the battery (immediately, or via callback) as { charging : bool, voltage : number } +exports.getBattery = function(callback) { + BATTERY_MONITOR.set(); + var result = { + charging : BATTERY_CHARGE.read(), + voltage : E.getAnalogVRef()*analogRead(BATTERY_VOLTAGE)*1500/180 + }; + BATTERY_MONITOR.reset(); + if (callback) callback(result); + return result; +} +// ------------------------------------------------------------------------------------------- Speaker +// Play a sound, supply a string/uint8array/arraybuffer, samples per second, and a callback to use when done +// This can play up to 3 sounds at a time (assuming ~4000 samples per second) +exports.sound = function(waveform, pitch, callback) { + if (!this.sounds) this.sounds=0; + if (this.sounds>2) throw new Error("Too many sounds playing at once"); + var w = new Waveform(waveform.length); + w.buffer.set(waveform); + w.on("finish", function(buf) { + this.sounds--; + if (!this.sounds) { + SPK_PWR_CTRL.reset(); + digitalWrite(SPEAKER,0); + } + if (callback) callback(); + }.bind(this)); + if (!this.sounds) { + analogWrite(SPEAKER, 0.5, {freq:40000}); + SPK_PWR_CTRL.set(); + } + this.sounds++; + w.startOutput(SPEAKER, pitch); +}; +// Make a simple beep noise. frequency in Hz, length in milliseconds. Both are optional. +exports.beep = function(freq, length) { + length = (length>0)?length:250; + freq = (freq>0)?freq:500; + analogWrite(SPEAKER, 0.5, {freq:freq}); + SPK_PWR_CTRL.set(); + if (this.beepTimeout) clearTimeout(this.beepTimeout); + this.beepTimeout = setTimeout(function() { + delete this.beepTimeout; + SPK_PWR_CTRL.reset(); + digitalWrite(SPEAKER,0); + }.bind(this), length); +}; +// Record audio for the given number of samples, at 8192 Hz 8 bit. +// This can then be fed into Thingy.sound(waveform, 8192). RAM is scarce, so realistically 1 sec is a maximum. +exports.record = function(samples, callback) { + var gain = 0x48; // gain ( 0 -> 0x50, 0x28 default ) + var buf = new ArrayBuffer(2049); // 2x 1k byte sample buffers for DMA (+1 for byte shift) + var bufAddr = E.getAddressOf(buf,true); + if (!bufAddr) throw new Error("Unable to create a buffer"); + var result = new Uint8Array(samples); + var resultIdx = 0; + var p = 0; // 1 or 0 (which buffer) + MIC_PWR_CTRL.set(); + MIC_CLK.mode("output"); + MIC_DOUT.mode("input"); + poke32(0x4001D504,0x08400000); // 1.032MHz clock default + poke32(0x4001D508,1); // mono, left on falling edge + poke32(0x4001D518,gain); // gain left + poke32(0x4001D51C,gain); // gain right + poke32(0x4001D540,MIC_CLK.getInfo().num); // CLK + poke32(0x4001D544,MIC_DOUT.getInfo().num); // DIN + poke32(0x4001D560,bufAddr); // PTR + poke32(0x4001D564,512); // MAXCNT + poke32(0x4001D500,1); // enable PDM + poke8(0x4001D100,0); // event start + poke8(0x4001D104,0); // event stop + poke8(0x4001D108,0); // event end + poke8(0x4001D000,1); // START TASK + poke32(0x4001D560,bufAddr+1024); // second pointer + // 16kHz output 16 bit, 32kByte/sec = will be done in 30ms + // Poll more often so we're more likely to catch it (no way to hook IRQs yet) + var i = setInterval(function() { + if (peek8(0x4001D108)) { // end event + poke8(0x4001D108,0); // clear end + poke32(0x4001D560,bufAddr+p*1024); // push new address in + // quick copy, drop every other sample, 16->8 bits + result.set(new Uint32Array(buf,1+(p*1024),256),resultIdx); + // advance and stop if required + p=1-p; + resultIdx+=256; + if (resultIdx>=result.length) stop(); + } + },5); + function stop() { + clearInterval(i); // stop polling + poke8(0x4001D004,1); // STOP TASK + poke8(0x4001D500,0); // disable PDM + poke32(0x4001D540,0xFFFFFFFF); // disconnect CLK + poke32(0x4001D544,0xFFFFFFFF); // disconnect DIN + MIC_PWR_CTRL.reset(); // mic off + // Now we have some time, add 128 to each item + E.mapInPlace(result,result,function(x){return x+128;}); + // call the callback + if (callback) setTimeout(callback,0,result); + } + return result; +}; + +// Reinitialise any hardware that might have been set up before being saved +E.on('init',function() { + if (exports.accel && exports.accel.callback) { + var c = exports.accel.callback; + exports.accel = undefined; + exports.onAcceleration(c); + } + if (exports.pressureCallback) { + exports.pressure = undefined; + exports.onPressure(exports.pressureCallback); + } + if (exports.humidityCallback) { + exports.humidity = undefined; + exports.onHumidity(exports.humidityCallback); + } + if (exports.gasCallback) { + exports.gas = undefined; + exports.onGas(exports.gasCallback); + } + if (exports.colorCallback) { + exports.color = undefined; + exports.onColor(exports.colorCallback); + } +}); diff --git a/libs/js/nordic/Thingy.min.js b/libs/js/nordic/Thingy.min.js new file mode 100644 index 000000000..f33832c96 --- /dev/null +++ b/libs/js/nordic/Thingy.min.js @@ -0,0 +1,10 @@ +var k=V8,v=D6,l=V9,m=D25,n=D26,p=V10,q=V11,w=V12,x=D22,r=[V13,V14,V15],t=D23,y=D24,z=D17,A=D28,u=V4,d=D27,f=D29,c=new I2C;c.setup({sda:7,scl:8,bitrate:4E5});exports.I2C=c;var e=new I2C;e.setup({sda:14,scl:15,bitrate:4E5});exports.I2CE=e;exports.onAcceleration=function(a){a?(this.accel||(this.accel=require("LIS2DH12").connectI2C(e)),this.accel.callback=a,this.accel.setPowerMode("low")):(this.accel&&this.accel.setPowerMode("powerdown"),this.accel=void 0)};exports.getAcceleration=function(a){this.accel? +this.accel.readXYZ(a):require("LIS2DH12").connectI2C(e).readXYZ(a)};exports.onMPU=function(a){a?this.mpu?this.mpu.callback=a:(k.set(),this.mpu=require("MPU9250").connectI2C(c),this.mpu.samplerate=10,this.mpu.callback=a,this.mpu.watch=setWatch(function(){this.mpu.callback(this.mpu.read())}.bind(this),v,{repeat:1,edge:"rising"}),setTimeout(this.mpu.initMPU9250.bind(this.mpu),10)):(this.mpu&&clearWatch(this.mpu.watch),k.reset(),this.mpu=void 0)};exports.getMPU=function(a){if(this.mpu)a(this.mpu.read()); +else this.onMPU(function(b){this.onMPU();a(b)}.bind(this))};exports.onPressure=function(a){a?(this.pressure||(this.pressure=require("LPS22HB").connectI2C(c,{"int":t}),this.pressure.on("data",function(a){this.pressureCallback(a)}.bind(this))),this.pressureCallback=a):(this.pressure&&this.pressure.stop(),this.pressureCallback=this.pressure=void 0)};exports.getPressure=function(a){if(this.pressure)this.pressure.read(a);else{var b=require("LPS22HB").connectI2C(c,{"int":t});b.read(function(c){b.stop(); +a(c)})}};exports.onHumidity=function(a){a?(this.humidity||(this.humidity=require("HTS221").connect(c,{"int":y}),this.humidity.on("data",function(a){this.humidityCallback(a)}.bind(this))),this.humidityCallback=a):(this.humidity&&this.humidity.stop(),this.humidityCallback=this.humidity=void 0)};exports.getHumidity=function(a){if(this.humidity)this.humidity.read(a);else this.onHumidity(function(b){this.onHumidity();a(b)}.bind(this))};exports.onGas=function(a){a?(this.gas||(p.set(),q.set(),w.reset(), +this.gas=require("CCS811").connectI2C(c,{"int":x}),this.gas.on("data",function(a){this.gasCallback(a)}.bind(this))),this.gasCallback=a):(this.gas&&(this.gas.stop(),q.reset(),p.reset()),this.gasCallback=this.gas=void 0)};exports.getGas=function(a){if(this.gas)a(this.gas.get());else this.onGas(function(b){this.onGas();a(b)}.bind(this))};exports.onColor=function(a){a?(this.color||(digitalWrite(r,0),this.color=require("BH1745").connectI2C(c),this.colorInt=setInterval(function(){this.colorCallback(this.color.read())}.bind(this), +200)),this.colorCallback=a):(this.color&&(clearInterval(this.colorInt),this.color.stop(),digitalWrite(r,7)),this.colorCallback=this.colorInt=this.color=void 0)};exports.getColor=function(a){if(this.color)a(this.color.read());else this.onColor(function(b){this.onColor();a(b)}.bind(this))};exports.getBattery=function(a){u.set();var b={charging:z.read(),voltage:E.getAnalogVRef()*analogRead(A)*1500/180};u.reset();a&&a(b);return b};exports.sound=function(a,b,c){this.sounds||(this.sounds=0);if(2=h.length&&c())},5);return h};E.on("init",function(){if(exports.accel&&exports.accel.callback){var a=exports.accel.callback;exports.accel=void 0;exports.onAcceleration(a)}exports.pressureCallback&& +(exports.pressure=void 0,exports.onPressure(exports.pressureCallback));exports.humidityCallback&&(exports.humidity=void 0,exports.onHumidity(exports.humidityCallback));exports.gasCallback&&(exports.gas=void 0,exports.onGas(exports.gasCallback));exports.colorCallback&&(exports.color=void 0,exports.onColor(exports.colorCallback))}) \ No newline at end of file diff --git a/libs/js/rak/RAK8211.js b/libs/js/rak/RAK8211.js new file mode 100644 index 000000000..ead736b09 --- /dev/null +++ b/libs/js/rak/RAK8211.js @@ -0,0 +1,175 @@ +var PINS = { + BME_CS : D2, + BME_SDI : D3, + BME_SCK : D4, + BME_SDO : D5, + PWR_GPRS_ON : D6, // 1=on, 0=off + GPS_STANDBY : D7, // 1=powered, 0=standby (OK to leave open) + GPS_RXD : D8, // 9600 baud + GPS_TXD : D9, // 9600 baud + PWR_GPS_ON : D10, // 1=on, 0=off + LIS2MDL_SCL : D11, + GPRS_TXD : D12, + LIS2MDL_SDA : D13, + GPRS_RESET : D14, + GPRS_PWRKEY : D15, + LIS2MDL_INT : D16, + BQ_EN : D17, + LIS3DH_SCL : D18, + LIS3DH_SDA : D19, + GPRS_RXD : D20, + // D21 is reset + OPT_SDA : D26, + OPT_INT : D22, + OPT_SCL : D23, + LIS3DH_INT1 : D25, + LIS3DH_RES : D26, + LIS3DH_INT2 : D27, + SENSOR_DOUT1 : D28, + SENSOR_DOUT2 : D29, + TILT_DOUT : D30, + GPS_RESET : D31 // 1=normal, 0=reset (internal pullup) +}; + +// Return GPS instance. callback is called whenever data is available! +exports.setGPSOn = function(isOn, callback) { + Serial1.removeAllListeners(); + delete this.GPS; + if (isOn) { + // Set up GPS + Serial1.setup(9600,{tx:PINS.GPS_RXD,rx:PINS.GPS_TXD}); + PINS.PWR_GPS_ON.set(); + return this.GPS = require("GPS").connect(Serial1, callback); + } else { + // Power off GPS + PINS.PWR_GPS_ON.reset(); + } +}; + +/// Returns BME280 instance. callback when initialised. Call 'getData' to get the information +exports.setEnvOn = function(isOn, callback) { + if (this.BME280) this.BME280.setPower(false); + delete this.BME280; + if (isOn) { + var spi = new SPI(); + spi.setup({miso : PINS.BME_SDO, mosi : PINS.BME_SDI, sck: PINS.BME_SCK }); + if (callback) setTimeout(callback, 100, this.BME280); // wait for first reading + return this.BME280 = require("BME280").connectSPI(spi, PINS.BME_CS); + } +}; + +/// Returns a LIS2MDL instance. callback when initialised. Then use 'read' to get data +exports.setMagOn = function(isOn, callback) { + if (this.LIS2MDL) this.LIS2MDL.off(); + delete this.LIS2MDL; + if (isOn) { + var i2c = new I2C(); + i2c.setup({sda:PINS.LIS2MDL_SDA, scl:PINS.LIS2MDL_SCL}); + if (callback) setTimeout(callback, 100, this.LIS2MDL); // wait for first reading + // {int:pin} isn't used yet, but at some point the module might include support + return this.LIS2MDL = require("LIS2MDL").connectI2C(i2c, { int : PINS.LIS2MDL_INT }); + } +}; + +/// Returns a LIS3DH instance. callback when initialised. Then use 'read' to get data +exports.setAccelOn = function(isOn, callback) { + if (this.LIS3DH) this.LIS3DH.off(); + delete this.LIS3DH; + if (isOn) { + var i2c = new I2C(); + i2c.setup({sda:PINS.LIS3DH_SDA, scl:PINS.LIS3DH_SCL}); + if (callback) setTimeout(callback, 100, this.LIS3DH); // wait for first reading + // {int:pin} isn't used yet, but at some point the module might include support + return this.LIS3DH = require("LIS3DH").connectI2C(i2c, { int : PINS.LIS3DH_INT1}); + } +}; + +/// Returns a OPT3001 instance. callback when initialised. Then use 'read' to get data +exports.setOptoOn = function(isOn, callback) { + if (this.OPT3001) this.OPT3001.off(); + delete this.OPT3001; + if (isOn) { + var i2c = new I2C(); + i2c.setup({sda:PINS.OPT_SDA, scl:PINS.OPT_SCL,bitrate:400000}); + if (callback) setTimeout(callback, 1000, this.OPT3001); // wait for first reading + // {int:pin} isn't used yet, but at some point the module might include support + return this.OPT3001 = require("OPT3001").connectI2C(i2c, { int : PINS.OPT_INT }); + } +}; + +// Turn cell connectivity on for 8211-G - will take around 8 seconds. Calls the `callback(usart)` when done. You then need to connect either SMS or QuectelM35 to the serial device `usart` +exports.setCellOn = function(isOn, callback) { + if (isOn) { + if (this.cellOn) { + setTimeout(callback,10,Serial1); + return; + } + var that=this; + return new Promise(function(resolve) { + Serial1.removeAllListeners(); + Serial1.on('data', function(x) {}); // suck up any data that gets transmitted from the modem as it boots (RDY, etc) + Serial1.setup(115200,{tx:PINS.GPRS_TXD,rx:PINS.GPRS_RXD}); + PINS.PWR_GPRS_ON.reset(); + setTimeout(resolve,200); + }).then(function() { + PINS.PWR_GPRS_ON.set(); + return new Promise(function(resolve){setTimeout(resolve,200);}); + }).then(function() { + PINS.GPRS_PWRKEY.set(); + return new Promise(function(resolve){setTimeout(resolve,2000);}); + }).then(function() { + PINS.GPRS_PWRKEY.reset(); + return new Promise(function(resolve){setTimeout(resolve,5000);}); + }).then(function() { + this.cellOn = true; + Serial1.removeAllListeners(); + if (callback) setTimeout(callback,10,Serial1); + }); + } else { + this.cellOn = false; + PINS.PWR_GPRS_ON.reset(); // turn power off. + if (callback) setTimeout(callback,1000); + } +}; + +// Turn cell connectivity on for 8211-NB - will take around 8 seconds. Calls the `callback(usart)` when done. You then need to connect to the serial device `usart` +exports.setNBCellOn = function(isOn, callback) { + if (isOn) { + if (this.cellOn) { + setTimeout(callback,10,Serial1); + return; + } + var that=this; + return new Promise(function(resolve) { + Serial1.removeAllListeners(); + Serial1.on('data', function(x) {}); // suck up any data that gets transmitted from the modem as it boots (RDY, etc) + Serial1.setup(9600,{tx:PINS.GPRS_RXD,rx:PINS.GPRS_TXD}); + PINS.PWR_GPRS_ON.reset(); + setTimeout(resolve,200); + }).then(function() { + PINS.PWR_GPRS_ON.set(); + return new Promise(function(resolve){setTimeout(resolve,200);}); + }).then(function() { + PINS.GPRS_PWRKEY.set(); + return new Promise(function(resolve){setTimeout(resolve,2000);}); + }).then(function() { + PINS.GPRS_PWRKEY.reset(); + return new Promise(function(resolve){setTimeout(resolve,5000);}); + }).then(function() { + this.cellOn = true; + Serial1.removeAllListeners(); + if (callback) setTimeout(callback,10,Serial1); + }); + } else { + this.cellOn = false; + PINS.PWR_GPRS_ON.reset(); // turn power off. + if (callback) setTimeout(callback,1000); + } +}; + +/// Set whether the BQ24210 should charge the battery (default is yes) +exports.setCharging = function(isCharging) { + PINS.BQ_EN.write(!isCharging); +}; + + diff --git a/libs/js/rak/RAK8211.min.js b/libs/js/rak/RAK8211.min.js new file mode 100644 index 000000000..c592ea10e --- /dev/null +++ b/libs/js/rak/RAK8211.min.js @@ -0,0 +1,5 @@ +var k=D2,l=D3,m=D4,n=D5,d=D6,p=D8,q=D9,f=D10,r=D11,g=D12,t=D13,e=D15,u=D16,v=D17,w=D18,x=D19,h=D20,y=D26,z=D22,A=D23,B=D25;exports.setGPSOn=function(c,b){Serial1.removeAllListeners();delete this.GPS;if(c)return Serial1.setup(9600,{tx:p,rx:q}),f.set(),this.GPS=require("GPS").connect(Serial1,b);f.reset()};exports.setEnvOn=function(c,b){this.BME280&&this.BME280.setPower(!1);delete this.BME280;if(c){var a=new SPI;a.setup({miso:n,mosi:l,sck:m});b&&setTimeout(b,100,this.BME280);return this.BME280= +require("BME280").connectSPI(a,k)}};exports.setMagOn=function(c,b){this.LIS2MDL&&this.LIS2MDL.off();delete this.LIS2MDL;if(c){var a=new I2C;a.setup({sda:t,scl:r});b&&setTimeout(b,100,this.LIS2MDL);return this.LIS2MDL=require("LIS2MDL").connectI2C(a,{"int":u})}};exports.setAccelOn=function(c,b){this.LIS3DH&&this.LIS3DH.off();delete this.LIS3DH;if(c){var a=new I2C;a.setup({sda:x,scl:w});b&&setTimeout(b,100,this.LIS3DH);return this.LIS3DH=require("LIS3DH").connectI2C(a,{"int":B})}};exports.setOptoOn= +function(c,b){this.OPT3001&&this.OPT3001.off();delete this.OPT3001;if(c){var a=new I2C;a.setup({sda:y,scl:A,bitrate:4E5});b&&setTimeout(b,1E3,this.OPT3001);return this.OPT3001=require("OPT3001").connectI2C(a,{"int":z})}};exports.setCellOn=function(c,b){if(c)if(this.cellOn)setTimeout(b,10,Serial1);else return(new Promise(function(a){Serial1.removeAllListeners();Serial1.on("data",function(a){});Serial1.setup(115200,{tx:g,rx:h});d.reset();setTimeout(a,200)})).then(function(){d.set();return new Promise(function(a){setTimeout(a, +200)})}).then(function(){e.set();return new Promise(function(a){setTimeout(a,2E3)})}).then(function(){e.reset();return new Promise(function(a){setTimeout(a,5E3)})}).then(function(){this.cellOn=!0;Serial1.removeAllListeners();b&&setTimeout(b,10,Serial1)});else this.cellOn=!1,d.reset(),b&&setTimeout(b,1E3)};exports.setNBCellOn=function(c,b){if(c)if(this.cellOn)setTimeout(b,10,Serial1);else return(new Promise(function(a){Serial1.removeAllListeners();Serial1.on("data",function(a){});Serial1.setup(9600, +{tx:h,rx:g});d.reset();setTimeout(a,200)})).then(function(){d.set();return new Promise(function(a){setTimeout(a,200)})}).then(function(){e.set();return new Promise(function(a){setTimeout(a,2E3)})}).then(function(){e.reset();return new Promise(function(a){setTimeout(a,5E3)})}).then(function(){this.cellOn=!0;Serial1.removeAllListeners();b&&setTimeout(b,10,Serial1)});else this.cellOn=!1,d.reset(),b&&setTimeout(b,1E3)};exports.setCharging=function(c){v.write(!c)} \ No newline at end of file diff --git a/libs/js/rak/RAK8212.js b/libs/js/rak/RAK8212.js new file mode 100644 index 000000000..fe6adf3cd --- /dev/null +++ b/libs/js/rak/RAK8212.js @@ -0,0 +1,168 @@ +var PINS = { + BME_CS : D2, + BME_SDI : D3, + BME_SCK : D4, + BME_SDO : D5, + PWR_GPRS_ON : D6, // 1=on, 0=off + LTE_RXD : D7, + LTE_CTS : D8, + LTE_TXD : D9, + LTE_RTS : D10, + LIS2MDL_SCL : D11, + LIS2MDL_SDA : D13, + GPRS_RESET : D14, + GPRS_PWRKEY : D15, + LIS2MDL_INT : D16, + BQ_EN : D17, + LIS3DH_SCL : D18, + LIS3DH_SDA : D19, + TP5 : D20, // test point + // D21 is reset + OPT_SDA : D26, + OPT_INT : D22, + OPT_SCL : D23, + LIS3DH_INT1 : D25, + LIS3DH_RES : D26, + LIS3DH_INT2 : D27, + SENSOR_DOUT1 : D28, + SENSOR_DOUT2 : D29, + TILT_DOUT : D30, + GPS_RESET : D31 // 1=normal, 0=reset (internal pullup) +}; + +/// Returns BME280 instance. callback when initialised. Call 'getData' to get the information +exports.setEnvOn = function(isOn, callback) { + if (this.BME280) this.BME280.setPower(false); + delete this.BME280; + if (isOn) { + var spi = new SPI(); + spi.setup({miso : PINS.BME_SDO, mosi : PINS.BME_SDI, sck: PINS.BME_SCK }); + if (callback) setTimeout(callback, 100, this.BME280); // wait for first reading + return this.BME280 = require("BME280").connectSPI(spi, PINS.BME_CS); + } +}; + +/// Returns a LIS2MDL instance. callback when initialised. Then use 'read' to get data +exports.setMagOn = function(isOn, callback) { + if (this.LIS2MDL) this.LIS2MDL.off(); + delete this.LIS2MDL; + if (isOn) { + var i2c = new I2C(); + i2c.setup({sda:PINS.LIS2MDL_SDA, scl:PINS.LIS2MDL_SCL}); + if (callback) setTimeout(callback, 100, this.LIS2MDL); // wait for first reading + // {int:pin} isn't used yet, but at some point the module might include support + return this.LIS2MDL = require("LIS2MDL").connectI2C(i2c, { int : PINS.LIS2MDL_INT }); + } +}; + +/// Returns a LIS3DH instance. callback when initialised. Then use 'read' to get data +exports.setAccelOn = function(isOn, callback) { + if (this.LIS3DH) this.LIS3DH.off(); + delete this.LIS3DH; + if (isOn) { + var i2c = new I2C(); + i2c.setup({sda:PINS.LIS3DH_SDA, scl:PINS.LIS3DH_SCL}); + if (callback) setTimeout(callback, 100, this.LIS3DH); // wait for first reading + // {int:pin} isn't used yet, but at some point the module might include support + return this.LIS3DH = require("LIS3DH").connectI2C(i2c, { int : PINS.LIS3DH_INT1}); + } +}; + +/// Returns a OPT3001 instance. callback when initialised. Then use 'read' to get data +exports.setOptoOn = function(isOn, callback) { + if (this.OPT3001) this.OPT3001.off(); + delete this.OPT3001; + if (isOn) { + var i2c = new I2C(); + i2c.setup({sda:PINS.OPT_SDA, scl:PINS.OPT_SCL,bitrate:400000}); + if (callback) setTimeout(callback, 1000, this.OPT3001); // wait for first reading + // {int:pin} isn't used yet, but at some point the module might include support + return this.OPT3001 = require("OPT3001").connectI2C(i2c, { int : PINS.OPT_INT }); + } +}; + +// Turn cell connectivity on - will take around 8 seconds. Calls the `callback(usart)` when done. You then need to connect either SMS or QuectelM35 to the serial device `usart` +exports.setCellOn = function(isOn, callback) { + if (isOn) { + if (this.cellOn) { + setTimeout(callback,10,Serial1); + return; + } + var that=this; + return new Promise(function(resolve) { + Serial1.removeAllListeners(); + Serial1.on('data', function(x) {}); // suck up any data that gets transmitted from the modem as it boots (RDY, etc) + Serial1.setup(115200,{tx:PINS.LTE_TXD, rx:PINS.LTE_RXD, cts:PINS.LTE_RTS}); + PINS.PWR_GPRS_ON.reset(); + setTimeout(resolve,200); + }).then(function() { + PINS.PWR_GPRS_ON.set(); + return new Promise(function(resolve){setTimeout(resolve,200);}); + }).then(function() { + PINS.GPRS_PWRKEY.set(); + return new Promise(function(resolve){setTimeout(resolve,2000);}); + }).then(function() { + PINS.GPRS_PWRKEY.reset(); + return new Promise(function(resolve){setTimeout(resolve,5000);}); + }).then(function() { + this.cellOn = true; + Serial1.removeAllListeners(); + if (callback) setTimeout(callback,10,Serial1); + }); + } else { + this.cellOn = false; + PINS.PWR_GPRS_ON.reset(); // turn power off. + if (callback) setTimeout(callback,1000); + } +}; + +/// Set whether the BQ24210 should charge the battery (default is yes) +exports.setCharging = function(isCharging) { + PINS.BQ_EN.write(!isCharging); +}; + +/// Set whether the BQ24210 should charge the battery (default is yes) +exports.setCharging = function(isCharging) { + PINS.BQ_EN.write(!isCharging); +}; + +// Return GPS instance. callback is called whenever data is available! +exports.setGPSOn = function(isOn, callback) { + if (!isOn) this.setCellOn(false,callback); + else this.setCellOn(isOn, function(usart) { + var at = require("AT").connect(usart); + var gps = { at:at,on:function(callback) { + callback=callback||function(){}; + at.cmd("AT+QGPS=1\r\n",1000,function cb(d) { // speed-optimal + if (d.startsWith("AT+")) return cb; // echo + callback(d=="OK"?null:d); + }); + },off:function(callback) { + callback=callback||function(){}; + at.cmd("AT+QGPSEND\r\n",1000,function cb(d) { + if (d.startsWith("AT+")) return cb; // echo + callback(d=="OK"?null:d); + }); + },get:function(callback) { + // ERROR: 516 means 'no fix' + callback=callback||function(){}; + at.cmd("AT+QGPSLOC=2\r\n",1000,function cb(d) { + if (d.startsWith("AT+")) return cb; // echo + if (d.startsWith("+CME ERROR:")) callback({error:d.substr(5)}); + else if (d.startsWith("+QGPSLOC:")) { + //+QGPSLOC: ,,,,,,,,,, + d = d.substr(9).trim(); + var a = d.split(","); + callback({ + raw : d, + UTC:a[0],lat:+a[1],lon:+a[2],alt:+a[4] + }); + } else callback({error:d}); + }); + }}; + gps.on(function(err) { + callback(err, err?undefined:gps); + }); + }); +}; + diff --git a/libs/js/rak/RAK8212.min.js b/libs/js/rak/RAK8212.min.js new file mode 100644 index 000000000..b411b74b5 --- /dev/null +++ b/libs/js/rak/RAK8212.min.js @@ -0,0 +1,5 @@ +var h=D2,k=D3,l=D4,m=D5,d=D6,n=D7,p=D9,q=D10,r=D11,t=D13,f=D15,u=D16,g=D17,v=D18,w=D19,x=D26,y=D22,z=D23,A=D25;exports.setEnvOn=function(a,c){this.BME280&&this.BME280.setPower(!1);delete this.BME280;if(a){var b=new SPI;b.setup({miso:m,mosi:k,sck:l});c&&setTimeout(c,100,this.BME280);return this.BME280=require("BME280").connectSPI(b,h)}};exports.setMagOn=function(a,c){this.LIS2MDL&&this.LIS2MDL.off();delete this.LIS2MDL;if(a){var b=new I2C;b.setup({sda:t,scl:r});c&&setTimeout(c,100,this.LIS2MDL); +return this.LIS2MDL=require("LIS2MDL").connectI2C(b,{"int":u})}};exports.setAccelOn=function(a,c){this.LIS3DH&&this.LIS3DH.off();delete this.LIS3DH;if(a){var b=new I2C;b.setup({sda:w,scl:v});c&&setTimeout(c,100,this.LIS3DH);return this.LIS3DH=require("LIS3DH").connectI2C(b,{"int":A})}};exports.setOptoOn=function(a,c){this.OPT3001&&this.OPT3001.off();delete this.OPT3001;if(a){var b=new I2C;b.setup({sda:x,scl:z,bitrate:4E5});c&&setTimeout(c,1E3,this.OPT3001);return this.OPT3001=require("OPT3001").connectI2C(b, +{"int":y})}};exports.setCellOn=function(a,c){if(a)if(this.cellOn)setTimeout(c,10,Serial1);else return(new Promise(function(b){Serial1.removeAllListeners();Serial1.on("data",function(b){});Serial1.setup(115200,{tx:p,rx:n,cts:q});d.reset();setTimeout(b,200)})).then(function(){d.set();return new Promise(function(b){setTimeout(b,200)})}).then(function(){f.set();return new Promise(function(b){setTimeout(b,2E3)})}).then(function(){f.reset();return new Promise(function(b){setTimeout(b,5E3)})}).then(function(){this.cellOn= +!0;Serial1.removeAllListeners();c&&setTimeout(c,10,Serial1)});else this.cellOn=!1,d.reset(),c&&setTimeout(c,1E3)};exports.setCharging=function(a){g.write(!a)};exports.setCharging=function(a){g.write(!a)};exports.setGPSOn=function(a,c){a?this.setCellOn(a,function(b){var a=require("AT").connect(b),d={at:a,on:function(b){b=b||function(){};a.cmd("AT+QGPS=1\r\n",1E3,function e(a){if(a.startsWith("AT+"))return e;b("OK"==a?null:a)})},off:function(b){b=b||function(){};a.cmd("AT+QGPSEND\r\n",1E3,function e(a){if(a.startsWith("AT+"))return e; +b("OK"==a?null:a)})},get:function(b){b=b||function(){};a.cmd("AT+QGPSLOC=2\r\n",1E3,function e(a){if(a.startsWith("AT+"))return e;if(a.startsWith("+CME ERROR:"))b({error:a.substr(5)});else if(a.startsWith("+QGPSLOC:")){a=a.substr(9).trim();var c=a.split(",");b({raw:a,UTC:c[0],lat:+c[1],lon:+c[2],alt:+c[4]})}else b({error:a})})}};d.on(function(a){c(a,a?void 0:d)})}):this.setCellOn(!1,c)} \ No newline at end of file diff --git a/libs/js/update_js.sh b/libs/js/update_js.sh new file mode 100755 index 000000000..f9e6cf4d5 --- /dev/null +++ b/libs/js/update_js.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +cd `dirname $0` + +# Get all JS modules we're using +wget https://www.espruino.com/modules/AT.min.js -O AT.min.js +wget https://www.espruino.com/modules/QuectelM35.min.js -O QuectelM35.min.js +wget https://www.espruino.com/modules/QuectelBG96.min.js -O QuectelBG96.min.js +wget https://www.espruino.com/modules/ATSMS.min.js -O ATSMS.min.js +wget https://www.espruino.com/modules/GPS.min.js -O GPS.min.js +wget https://www.espruino.com/modules/graphical_menu.min.js -O graphical_menu.min.js +wget https://www.espruino.com/modules/BME280.min.js -O BME280.min.js +wget https://www.espruino.com/modules/LIS2DH12.min.js -O LIS2DH12.min.js +wget https://www.espruino.com/modules/LIS2MDL.min.js -O LIS2MDL.min.js +wget https://www.espruino.com/modules/LIS3DH.min.js -O LIS3DH.min.js +wget https://www.espruino.com/modules/OPT3001.min.js -O OPT3001.min.js +wget https://www.espruino.com/modules/MPU9250.min.js -O MPU9250.min.js +wget https://www.espruino.com/modules/LPS22HB.min.js -O LPS22HB.min.js +wget https://www.espruino.com/modules/HTS221.min.js -O HTS221.min.js +wget https://www.espruino.com/modules/CCS811.min.js -O CCS811.min.js +wget https://www.espruino.com/modules/BH1745.min.js -O BH1745.min.js +#wget https://www.espruino.com/modules/PCA9685.min.js -O PCA9685.min.js +#wget https://www.espruino.com/modules/Smartibot.min.js -O Smartibot.min.js + +# Other libs +wget https://www.espruino.com/modules/EspruinoWiFi.min.js -O espruino_wifi/Wifi.min.js +node ../../../EspruinoDocs/bin/minify.js nordic/Thingy.js nordic/Thingy.min.js +node ../../../EspruinoDocs/bin/minify.js rak/RAK8211.js rak/RAK8211.min.js +node ../../../EspruinoDocs/bin/minify.js rak/RAK8212.js rak/RAK8212.min.js + diff --git a/libs/math/jswrap_math.c b/libs/math/jswrap_math.c index a30507dc5..3d119ad60 100644 --- a/libs/math/jswrap_math.c +++ b/libs/math/jswrap_math.c @@ -208,13 +208,28 @@ double jswrap_math_atan(double x) { "ifndef" : "SAVE_ON_FLASH", "class" : "Math", "name" : "atan2", - "generate" : "atan2", + "generate" : "jswrap_math_atan2", "params" : [ ["y","float","The Y-part of the angle to get the arc tangent of"], ["x","float","The X-part of the angle to get the arc tangent of"] ], "return" : ["float","The arctangent of Y/X, between -PI and PI"] }*/ +double jswrap_math_atan2(double y, double x) { +#ifdef SAVE_ON_FLASH_MATH + if (x>0) return jswrap_math_atan(y/x); + if (x<0) { + if (y>=0) return jswrap_math_atan(y/x)+PI; + else return jswrap_math_atan(y/x)-PI; + } else { // X==0 + if (y>0) return PI/2; + else if (y<0) return -PI/2; + else return NAN; + } +#else + return atan2(y, x); +#endif +} /* we use sin here, not cos, to try and save a bit of code space */ /*JSON{ @@ -325,10 +340,10 @@ double jswrap_math_pow(double x, double y) { JsVar *jswrap_math_round(double x) { if (!isfinite(x) || isNegativeZero(x)) return jsvNewFromFloat(x); x += (x<0) ? -0.4999999999 : 0.4999999999; - JsVarInt i = (JsVarInt)x; + long long i = (long long)x; if (i==0 && (x<0)) return jsvNewFromFloat(-0.0); // pass -0 through - return jsvNewFromInteger(i); + return jsvNewFromLongInteger(i); } /*JSON{ @@ -446,6 +461,8 @@ JsVarFloat jswrap_math_clip(JsVarFloat x, JsVarFloat min, JsVarFloat max) { ], "return" : ["float","The value of x, wrapped so as not to be below min or above max."] } +DEPRECATED - This is not part of standard JavaScript libraries + Wrap a number around if it is less than 0 or greater than or equal to max. For instance you might do: ```Math.wrap(angleInDegrees, 360)``` */ diff --git a/libs/math/jswrap_math.h b/libs/math/jswrap_math.h index b4feb1241..7de61ec98 100644 --- a/libs/math/jswrap_math.h +++ b/libs/math/jswrap_math.h @@ -27,5 +27,6 @@ JsVar *jswrap_math_round(double x); double jswrap_math_sqrt(double x); double jswrap_math_sin(double x); double jswrap_math_atan(double x); +double jswrap_math_atan2(double y, double x); JsVarFloat jswrap_math_clip(JsVarFloat x, JsVarFloat min, JsVarFloat max); JsVarFloat jswrap_math_minmax(JsVar *args, bool isMax); diff --git a/libs/neopixel/jswrap_neopixel.c b/libs/neopixel/jswrap_neopixel.c index b1f43d6a3..d98723ce4 100644 --- a/libs/neopixel/jswrap_neopixel.c +++ b/libs/neopixel/jswrap_neopixel.c @@ -42,7 +42,7 @@ bool neopixelWrite(Pin pin, unsigned char *rgbData, size_t rgbSize); "type" : "library", "class" : "neopixel" } -This library allows you to write to Neopixel/WS281x/APA10x LED strips +This library allows you to write to Neopixel/WS281x/APA10x/SK6812 LED strips These use a high speed single-wire protocol which needs platform-specific implementation on some devices - hence this library to simplify things. @@ -55,7 +55,7 @@ implementation on some devices - hence this library to simplify things. "generate" : "jswrap_neopixel_write", "params" : [ ["pin", "pin", "The Pin the LEDs are connected to"], - ["data","JsVar","The data to write to the LED strip"] + ["data","JsVar","The data to write to the LED strip (must be a multiple of 3 bytes long)"] ] } Write to a strip of NeoPixel/WS281x/APA104/APA106/SK6812-style LEDs @@ -89,6 +89,10 @@ setInterval(function() { * Different types of LED have the data in different orders - so don't be surprised by RGB or BGR orderings! +* Some LED strips (SK6812) actually take 4 bytes per LED (red, green, blue and white). +These are still supported but the array of data supplied must still be a multiple of 3 +bytes long. Just round the size up - it won't cause any problems. + * On some platforms like STM32, pins capable of hardware SPI MOSI are required. diff --git a/libs/network/esp32/jswrap_esp32_network.c b/libs/network/esp32/jswrap_esp32_network.c index 151f2552b..735b92dd9 100644 --- a/libs/network/esp32/jswrap_esp32_network.c +++ b/libs/network/esp32/jswrap_esp32_network.c @@ -1,3 +1,4 @@ +/* /* * This file is part of Espruino, a JavaScript interpreter for Microcontrollers * @@ -24,16 +25,29 @@ #include "esp_wifi.h" #include "esp_event_loop.h" #include "tcpip_adapter.h" +#include "mdns/include/mdns.h" + +#include "lwip/apps/ping/ping.h" +#include "lwip/apps/ping/esp_ping.h" +#include "apps/sntp/sntp.h" +#include "lwip/dns.h" #include "jsinteractive.h" #include "network.h" #include "jswrap_modules.h" #include "jswrap_esp32_network.h" +#include "jswrap_storage.h" #include "jsutils.h" #define UNUSED(x) (void)(x) +#ifndef RELEASE + #define jsDebug(format, ...) jsWarn(format, ## __VA_ARGS__) +#else + #define jsDebug(format, ...) do { } while(0) +#endif + static void sendWifiCompletionCB( JsVar **g_jsCallback, //!< Pointer to the global callback variable char *reason //!< NULL if successful, error string otherwise @@ -45,6 +59,12 @@ static JsVar *g_jsDisconnectCallback; // A callback function to be invoked when we have an IP address. static JsVar *g_jsGotIpCallback; +// A callback function to be invoked on ping responses. +static JsVar *g_jsPingCallback; + +// A callback function to be invoked on gethostbyname responses. +static JsVar *g_jsHostByNameCallback; + // A callback function to be invoked when we complete an access point scan. static JsVar *g_jsScanCallback; @@ -64,6 +84,28 @@ static bool g_isStaConnected = false; #define EXPECT_OPT_EXCEPTION(jsOPT) jsExceptionHere(JSET_ERROR, "Expecting options object but got %t", jsOPT) +//===== mDNS +static bool mdns_started = 0; + +void stopMDNS() { + jsWarn( "Wifi:stopMDNS"); + mdns_free(); + mdns_started = false; +} + +void startMDNS(char *hostname) { + jsWarn( "Wifi:startMDNS - %s", hostname); + if (mdns_started) stopMDNS(); + + // start mDNS + ESP_ERROR_CHECK( mdns_init() ); + //set mDNS hostname (required if you want to advertise services) + ESP_ERROR_CHECK( mdns_hostname_set(hostname) ); + mdns_service_add(NULL, "_telnet", "_tcp", 23, NULL, 0); + + mdns_started = true; +} + /** * Convert an wifi_auth_mode_t data type to a string value. */ @@ -108,6 +150,9 @@ static char *cipherTypeToString(wifi_cipher_type_t cipherType) { } // End of authModeToString */ +/** + * check esp function +*/ /** * Convert an wifi_second_chan_t data type to a string value. @@ -188,7 +233,7 @@ static char *wifiReasonToString(uint8_t reason) { case WIFI_REASON_UNSUPP_RSN_IE_VERSION: return "REASON_UNSUPP_RSN_IE_VERSION"; } - jsWarn( "wifiReasonToString: Unknown reasonL %d", reason); + jsDebug( "wifiReasonToString: Unknown reason %d", reason); return "Unknown reason"; } // End of wifiReasonToString @@ -209,6 +254,63 @@ static char *wifiModeToString(wifi_mode_t mode) { return "UNKNOWN"; } // End of wifiModeToString +/** + * Convert an wifi event to a string value. + */ +static char *wifiEventToString(uint32_t event){ + switch(event){ + case SYSTEM_EVENT_STA_CONNECTED:return "STA_CONNECTED"; + case SYSTEM_EVENT_STA_DISCONNECTED:return "STA_DISCONNECTED"; + case SYSTEM_EVENT_STA_AUTHMODE_CHANGE:return "STA_AUTHMODE_CHANGE"; + case SYSTEM_EVENT_STA_GOT_IP:return "STA_GOT_IP"; + case SYSTEM_EVENT_AP_STACONNECTED:return "AP_STACONNECTED"; + case SYSTEM_EVENT_AP_STADISCONNECTED: return "AP_STADISCONNECTED"; + case SYSTEM_EVENT_AP_PROBEREQRECVED:return "AP_PROBEREQRECVED"; + case SYSTEM_EVENT_WIFI_READY: return "WIFI_READY"; + case SYSTEM_EVENT_SCAN_DONE: return "SCAN_DONE"; + case SYSTEM_EVENT_STA_START: return "STA_START"; + case SYSTEM_EVENT_STA_STOP: return "STA_STOP"; + case SYSTEM_EVENT_STA_LOST_IP: return "LOST_IP"; + case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: return "STA_WPS_ER_SUCCESS"; + case SYSTEM_EVENT_STA_WPS_ER_FAILED: return "STA_WPS_ER_FAILED"; + case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: return "STA_WPS_ER_TIMEOUT"; + case SYSTEM_EVENT_STA_WPS_ER_PIN: return "STA_WPS_ER_PIN"; + case SYSTEM_EVENT_AP_START: return "AP_START"; + case SYSTEM_EVENT_AP_STOP: return "AP_STOP"; + case SYSTEM_EVENT_GOT_IP6: return "GOT_IP6"; + case SYSTEM_EVENT_ETH_START: return "ETH_START"; + case SYSTEM_EVENT_ETH_STOP: return "ETH_STOP"; + case SYSTEM_EVENT_ETH_CONNECTED: return "ETH_CONNECTED"; + case SYSTEM_EVENT_ETH_DISCONNECTED: return "ETH_DISCONNECTED"; + case SYSTEM_EVENT_ETH_GOT_IP: return "ETH_GOT_IP"; + case SYSTEM_EVENT_MAX: return "MAX"; + default: return "unknown event"; + } +} + +/** + * convert WiFi error to a string value. + */ +static char *wifiErrorToString(esp_err_t err){ + switch(err){ + case 0x3001: return"WiFi driver was not installed by esp_wifi_init"; + case 0x3002: return"WiFi driver was not started by esp_wifi_start"; + case 0x3003: return"WiFi driver was not stopped by esp_wifi_stop"; + case 0x3004: return"WiFi interface error"; + case 0x3005: return"WiFi mode error"; + case 0x3006: return"WiFi internal state error"; + case 0x3007: return"WiFi internal control block of station or soft-AP error"; + case 0x3008: return"WiFi internal NVS module error"; + case 0x3009: return"MAC address is invalid"; + case 0x300A: return"SSID is invalid"; + case 0x300B: return"Password is invalid"; + case 0x300C: return"Timeout error"; + case 0x300D: return"WiFi is in sleep state(RF closed) and wakeup fail"; + case 0x300E: return"The caller would block"; + case 0x300F: return"Station still in disconnect status"; + default: return "no WiFi error, see esp_err_to_name.c"; + } +} /** * Callback function that is invoked at the culmination of a scan. @@ -246,19 +348,16 @@ static void scanCB() { jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "authMode", jsvNewFromString(authModeToString(list[i].authmode))); // The SSID may **NOT** be NULL terminated ... so handle that. - char ssid[32 + 1]; - strncpy((char *)ssid, list[i].ssid, 32); - ssid[32] = '\0'; - jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "ssid", jsvNewFromString(ssid)); - - /* - char macAddrString[6*3 + 1]; - os_sprintf(macAddrString, macFmt, - bssInfo->bssid[0], bssInfo->bssid[1], bssInfo->bssid[2], - bssInfo->bssid[3], bssInfo->bssid[4], bssInfo->bssid[5]); - jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "mac", jsvNewFromString(macAddrString)); - */ - + char temp[32 + 1]; + strncpy((char *)temp, list[i].ssid, 32); + temp[32] = '\0'; + jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "ssid", jsvNewFromString(temp)); + sprintf(temp, MACSTR, MAC2STR(list[i].bssid)); + jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "mac", jsvNewFromString(temp)); + sprintf(temp, "%d", list[i].primary); + jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "channel", jsvNewFromString(temp)); + // Can't find a flag for this? http://esp-idf.readthedocs.io/en/latest/api-reference/wifi/esp_wifi.html?highlight=wifi_ap_record_t + //jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "isHidden", jsvNewFromBool(list[i].ssid_hidden)); // Add the new record to the array jsvArrayPush(jsAccessPointArray, jsCurrentAccessPoint); jsvUnLock(jsCurrentAccessPoint); @@ -310,8 +409,12 @@ static JsVar *getWifiModule() { * * event_handler() * */ + +static int s_retry_num = 0; + static char *wifiGetEvent(uint32_t event) { - switch(event) { + jsDebug( "wifiGetEvent: Got event: %d", event); +switch(event) { case SYSTEM_EVENT_AP_PROBEREQRECVED: return "#onprobe_recv"; case SYSTEM_EVENT_AP_STACONNECTED: @@ -339,7 +442,7 @@ static char *wifiGetEvent(uint32_t event) { case SYSTEM_EVENT_WIFI_READY: break; } - jsWarn( "Unhandled wifi event type: %d", event); + jsDebug( "Unhandled wifi event type: %d", event); return NULL; } // End of wifiGetEvent @@ -363,7 +466,6 @@ static void sendWifiEvent( if (eventName == NULL) { return; } - jsiQueueObjectCallbacks(module, eventName, params, 1); jsvUnLock(module); return; @@ -386,8 +488,15 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) * * bssid * * reason */ + jsDebug("Wifi: Event(%d):SYSTEM_EVENT_%s\n",event->event_id,wifiEventToString(event->event_id)); + if (event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) { - g_isStaConnected = false; // Flag us as disconnected + if (--s_retry_num > 0 ) { + esp_wifi_connect(); + jsDebug("retry to AP connect"); + return; + } + g_isStaConnected = false; // Flag as disconnected g_lastEventStaDisconnected = event->event_info.disconnected; // Save the last disconnected info if (jsvIsFunction(g_jsDisconnectCallback)) { @@ -438,7 +547,16 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) return ESP_OK; } // End of handle SYSTEM_EVENT_STA_CONNECTED - + if (event->event_id == SYSTEM_EVENT_STA_START) { + s_retry_num = 5; // Try to connect 5 times + // Perform an esp_wifi_connect + esp_err_t err = esp_wifi_connect(); + if (err != ESP_OK) { + jsError( "Wifi: event_handler STA_START: esp_wifi_connect: %d(%s)", err,wifiErrorToString(err)); + return NULL; + } + return ESP_OK; + } /** * SYSTEM_EVENT_STA_GOT_IP * Structure contains: @@ -446,6 +564,7 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) * * ipinfo.netmask * * ip_info.gw */ + if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) { sendWifiCompletionCB(&g_jsGotIpCallback, NULL); JsVar *jsDetails = jsvNewObject(); @@ -459,12 +578,17 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) jsvObjectSetChildAndUnLock(jsDetails, "netmask", jsvNewFromString(temp)); sprintf(temp, IPSTR, IP2STR(&event->event_info.got_ip.ip_info.gw)); jsvObjectSetChildAndUnLock(jsDetails, "gw", jsvNewFromString(temp)); - + jsDebug("Wifi: About to emit connect!"); sendWifiEvent(event->event_id, jsDetails); + // start mDNS + const char * hostname; + esp_err_t err = tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &hostname); + if (hostname && hostname[0] != 0) { + startMDNS(hostname); + } return ESP_OK; } // End of handle SYSTEM_EVENT_STA_GOT_IP - /** * SYSTEM_EVENT_AP_STACONNECTED * Structure contains: @@ -518,7 +642,7 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) sendWifiCompletionCB(&g_jsAPStartedCallback, NULL); return ESP_OK; } - + jsDebug("Wifi: event_handler -> NOT HANDLED EVENT: %d", event->event_id ); return ESP_OK; } // End of event_handler @@ -528,10 +652,13 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) * handler. */ void esp32_wifi_init() { + tcpip_adapter_init(); ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + + jsDebug("esp32_wifi_init complete"); } // End of esp32_wifi_init @@ -547,6 +674,7 @@ static void sendWifiCompletionCB( JsVar **g_jsCallback, //!< Pointer to the global callback variable char *reason //!< NULL if successful, error string otherwise ) { + jsDebug("sendWifiCompletionCB"); // Check that we have a callback function. if (!jsvIsFunction(*g_jsCallback)){ return; // we have not got a function pointer: nothing to do @@ -571,6 +699,7 @@ static void sendWifiCompletionCB( * Perform a soft initialization of ESP32 networking. */ void jswrap_esp32_wifi_soft_init() { + jsDebug("jswrap_esp32_wifi_soft_init()"); JsNetwork net; networkCreate(&net, JSNETWORKTYPE_ESP32); // Set the network type to be ESP32 networkState = NETWORKSTATE_ONLINE; // Set the global state of the networking to be online @@ -595,6 +724,10 @@ void jswrap_wifi_disconnect(JsVar *jsCallback) { g_jsDisconnectCallback = jsvLockAgainSafe(jsCallback); // Call the ESP-IDF to disconnect us from the access point. + jsDebug("Disconnecting....."); + // turn off auto-connect + esp_wifi_set_auto_connect(false); + s_retry_num = 0; // flag so we don't attempt to reconnect esp_wifi_disconnect(); } // End of jswrap_wifi_disconnect @@ -625,7 +758,7 @@ void jswrap_wifi_stopAP(JsVar *jsCallback) { } err = esp_wifi_set_mode(mode); if (err != ESP_OK) { - jsWarn("jswrap_wifi_stopAP: esp_wifi_set_mode rc=%d", err); + jsDebug("jswrap_wifi_stopAP: esp_wifi_set_mode rc=%d(%s)", err,wifiErrorToString(err)); } if (jsvIsFunction(jsCallback)) { @@ -638,6 +771,8 @@ void jswrap_wifi_connect( JsVar *jsOptions, JsVar *jsCallback ) { + +jsDebug("jswrap_wifi_connect: entry"); // Check that the ssid value isn't obviously in error. if (!jsvIsString(jsSsid)) { @@ -686,14 +821,15 @@ void jswrap_wifi_connect( } jsvUnLock(jsPassword); } // End of we had options - + jsDebug("jswrap_wifi_connect: SSID, password, Callback done"); + // At this point, we have the ssid in "ssid" and the password in "password". // Perform an esp_wifi_set_mode wifi_mode_t mode; esp_err_t err; err = esp_wifi_get_mode(&mode); if (err != ESP_OK) { - jsError( "jswrap_wifi_connect: esp_wifi_get_mode: %d", err); + jsError( "jswrap_wifi_connect: esp_wifi_get_mode: %d(%s)", err,wifiErrorToString(err)); return; } switch(mode) { @@ -712,38 +848,41 @@ void jswrap_wifi_connect( err = esp_wifi_set_mode(mode); if (err != ESP_OK) { - jsError( "jswrap_wifi_connect: esp_wifi_set_mode: %d, mode=%d", err, mode); + jsError( "jswrap_wifi_connect: esp_wifi_set_mode: %d(%s), mode=%d", err,wifiErrorToString(err), mode); return; } - + jsDebug("jswrap_wifi_connect: esi_wifi_set_mode done"); + // Perform a an esp_wifi_set_config wifi_config_t staConfig; + + memset(&staConfig, 0, sizeof(staConfig)); memcpy(staConfig.sta.ssid, ssid, sizeof(staConfig.sta.ssid)); memcpy(staConfig.sta.password, password, sizeof(staConfig.sta.password)); staConfig.sta.bssid_set = false; - esp_wifi_set_auto_connect(false); // turn off default behaviour - err = esp_wifi_set_config(WIFI_IF_STA, &staConfig); + esp_wifi_set_auto_connect(true); + jsDebug("jswrap_wifi_connect: esp_wifi_set_autoconnect done"); + + err = esp_wifi_set_config(ESP_IF_WIFI_STA, &staConfig); if (err != ESP_OK) { - jsError( "jswrap_wifi_connect: esp_wifi_set_config: %d", err); + jsError( "jswrap_wifi_connect: esp_wifi_set_config: %d(%s)", err,wifiErrorToString(err)); return; } + jsDebug("jswrap_wifi_connect: esp_wifi_set_config done"); // Perform an esp_wifi_start + jsDebug("jswrap_wifi_connect: esp_wifi_start %s",ssid); err = esp_wifi_start(); if (err != ESP_OK) { - jsError( "jswrap_wifi_connect: esp_wifi_start: %d", err); + jsError( "jswrap_wifi_connect: esp_wifi_start: %d(%s)", err,wifiErrorToString(err)); return; } // Save the callback for later execution. g_jsGotIpCallback = jsvLockAgainSafe(jsCallback); - - // Perform an esp_wifi_connect + err = esp_wifi_connect(); - if (err != ESP_OK) { - jsError( "jswrap_wifi_connect: esp_wifi_connect: %d", err); - return; - } + } void jswrap_wifi_scan(JsVar *jsCallback) { @@ -769,7 +908,7 @@ void jswrap_wifi_scan(JsVar *jsCallback) { wifi_mode_t mode; esp_err_t err = esp_wifi_get_mode(&mode); if (err != ESP_OK) { - jsError( "jswrap_wifi_scan: esp_wifi_get_mode: %d", err); + jsError( "jswrap_wifi_scan: esp_wifi_get_mode: %d(%s)", err,wifiErrorToString(err)); return; } @@ -791,14 +930,14 @@ void jswrap_wifi_scan(JsVar *jsCallback) { err = esp_wifi_set_mode(mode); if (err != ESP_OK) { - jsError( "jswrap_wifi_scan: esp_wifi_set_mode: %d", err); + jsError( "jswrap_wifi_scan: esp_wifi_set_mode: %d(%s)", err,wifiErrorToString(err)); return; } // Perform an esp_wifi_start err = esp_wifi_start(); if (err != ESP_OK) { - jsError( "jswrap_wifi_connect: esp_wifi_start: %d", err); + jsError( "jswrap_wifi_connect: esp_wifi_start: %d(%s)", err,wifiErrorToString(err)); return; } @@ -920,18 +1059,18 @@ void jswrap_wifi_startAP( } } // End we have an options structure - // Set the mode to be accesss point - // FIX ... we can't hard code this to be just an access point. esp_err_t err; - // set callback if (jsvIsFunction(jsCallback)) { g_jsAPStartedCallback = jsvLockAgainSafe(jsCallback); } - err = esp_wifi_set_mode(WIFI_MODE_AP); + wifi_mode_t mode; + err = esp_wifi_get_mode(&mode); + + err = esp_wifi_set_mode( mode | WIFI_MODE_AP); if (err != ESP_OK) { - jsError( "jswrap_wifi_startAP: esp_wifi_set_mode: %d", err); + jsError( "jswrap_wifi_startAP: esp_wifi_set_mode: %d(%s)", err,wifiErrorToString(err)); return; } @@ -945,7 +1084,7 @@ void jswrap_wifi_startAP( // Perform an esp_wifi_start err = esp_wifi_start(); if (err != ESP_OK) { - jsError( "jswrap_wifi_startAP: esp_wifi_start: %d", err); + jsError( "jswrap_wifi_startAP: esp_wifi_start: %d(%s)", err,wifiErrorToString(err)); return; } } // End of jswrap_wifi_startAP @@ -1041,12 +1180,50 @@ JsVar *jswrap_wifi_getStatus(JsVar *jsCallback) { } // End of jswrap_wifi_getStatus - void jswrap_wifi_setConfig(JsVar *jsSettings) { - UNUSED(jsSettings); - jsError( "jswrap_wifi_setConfig - Not implemented"); -} // End of jswrap_wifi_setConfig + // Make sure jsSetings an object + if (!jsvIsObject(jsSettings)) { + EXPECT_OPT_EXCEPTION(jsSettings); + return; + } + // phy setting + JsVar *jsPhy = jsvObjectGetChild(jsSettings, "phy", 0); + if (jsvIsString(jsPhy)) { + if (jsvIsStringEqual(jsPhy, "11b")) { + esp_wifi_set_protocol(WIFI_IF_AP,WIFI_PROTOCOL_11B); + } else if (jsvIsStringEqual(jsPhy, "11g")) { + esp_wifi_set_protocol(WIFI_IF_AP,WIFI_PROTOCOL_11B| WIFI_PROTOCOL_11G); + } else if (jsvIsStringEqual(jsPhy, "11n")) { + esp_wifi_set_protocol(WIFI_IF_AP,WIFI_PROTOCOL_11B| WIFI_PROTOCOL_11G|WIFI_PROTOCOL_11N); + } else { + jsvUnLock(jsPhy); + jsExceptionHere(JSET_ERROR, "Unknown phy mode."); + return; + } + } + if (jsPhy != NULL) jsvUnLock(jsPhy); + + // powersave setting + // Call esp_wifi_set_ps(WIFI_PS_MIN_MODEM) to enable Modem-sleep minimum power save mode or esp_wifi_set_ps(WIFI_PS_MAX_MODEM) + JsVar *jsPowerSave = jsvObjectGetChild(jsSettings, "powersave", 0); + if (jsvIsString(jsPowerSave)) { + if (jsvIsStringEqual(jsPowerSave, "none")) { + esp_wifi_set_ps(WIFI_PS_NONE); + } else if (jsvIsStringEqual(jsPowerSave, "ps-poll")) { + esp_wifi_set_ps(WIFI_PS_MODEM); + } else if (jsvIsStringEqual(jsPowerSave, "min")) { + esp_wifi_set_ps(WIFI_PS_MIN_MODEM); + } else if (jsvIsStringEqual(jsPowerSave, "max")) { + esp_wifi_set_ps(WIFI_PS_MAX_MODEM); + } else { + jsvUnLock(jsPowerSave); + jsExceptionHere(JSET_ERROR, "Unknown powersave mode."); + return; + } + } + if (jsPowerSave != NULL) jsvUnLock(jsPowerSave); +} JsVar *jswrap_wifi_getDetails(JsVar *jsCallback) { // Check callback @@ -1129,42 +1306,161 @@ JsVar *jswrap_wifi_getAPDetails(JsVar *jsCallback) { return jsDetails; } // End of jswrap_wifi_getAPDetails - void jswrap_wifi_save(JsVar *what) { + jsDebug("Wifi.save"); + JsVar *o = jsvNewObject(); + if (!o) return; + if (jsvIsString(what) && jsvIsStringEqual(what, "clear")) { - esp_wifi_set_auto_connect(false); - } else { - esp_wifi_set_auto_connect(true); - } -} // End of jswrap_wifi_save - - -void jswrap_wifi_restore(void) { - bool auto_connect; - int err=esp_wifi_get_auto_connect(&auto_connect); - - if ( auto_connect ) { - err = esp_wifi_start(); - if (err != ESP_OK) { - jsError( "jswrap_wifi_restore: esp_wifi_start: %d", err); - } - - wifi_mode_t mode; - err = esp_wifi_get_mode(&mode); - if ( ( mode == WIFI_MODE_STA ) || ( mode == WIFI_MODE_APSTA ) ) { - // Perform an esp_wifi_start - err = esp_wifi_connect(); - if (err != ESP_OK) { - jsError( "jswrap_wifi_restore: esp_wifi_connect: %d", err - ESP_ERR_WIFI_BASE); - return; - } - } - } else { - // No previous wifi.save() + JsVar *name = jsvNewFromString(WIFI_CONFIG_STORAGE_NAME); + jswrap_storage_erase(name); + jsvUnLock(name); + jsDebug("Wifi.save(clear)"); + return; } -} // End of jswrap_wifi_restore + // station stuff + wifi_sta_config_t sta_config; + esp_wifi_get_config(WIFI_IF_STA, (wifi_config_t *)&sta_config); + jsvObjectSetChildAndUnLock(o, "ssid", jsvNewFromString((char *)sta_config.ssid)); + jsvObjectSetChildAndUnLock(o, "password", jsvNewFromString((char *)sta_config.password)); + + wifi_mode_t wifi_mode; + esp_wifi_get_mode(&wifi_mode); + jsvObjectSetChildAndUnLock(o, "mode", jsvNewFromInteger(wifi_mode)); + //jsvObjectSetChildAndUnLock(o, "phyMode", jsvNewFromInteger(wifi_get_phy_mode())); + wifi_ps_type_t psType; + esp_wifi_get_ps(&psType); + jsvObjectSetChildAndUnLock(o, "sleepType", jsvNewFromInteger(psType)); + + wifi_ap_config_t ap_config; + esp_wifi_get_config(WIFI_IF_AP, (wifi_config_t *)&ap_config); + + jsvObjectSetChildAndUnLock(o, "ssidAP", jsvNewFromString((char *)ap_config.ssid)); + jsvObjectSetChildAndUnLock(o, "passwordAP", jsvNewFromString((char *) ap_config.password)); + jsvObjectSetChildAndUnLock(o, "authmodeAP", jsvNewFromInteger(ap_config.authmode)); + jsvObjectSetChildAndUnLock(o, "hiddenAP", jsvNewFromInteger(ap_config.ssid_hidden)); + jsvObjectSetChildAndUnLock(o, "channelAP", jsvNewFromInteger(ap_config.channel)); + + const char * hostname; + esp_err_t err = tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &hostname); + if (hostname) jsvObjectSetChildAndUnLock(o, "hostname", jsvNewFromString((char *) hostname)); + + // save object + JsVar *name = jsvNewFromString(WIFI_CONFIG_STORAGE_NAME); + jswrap_storage_erase(name); + jswrap_storage_write(name,o,0,0); + jsvUnLock2(name,o); + + jsDebug("Wifi.save: write completed"); +} + +void jswrap_wifi_restore(void) { + jsDebug("jswrap_wifi_restore"); + + JsVar *name = jsvNewFromString(WIFI_CONFIG_STORAGE_NAME); + JsVar *o = jswrap_storage_readJSON(name); + if (!o) { // no data + jsDebug("jswrap_wifi_restore: No data - Starting default AP"); + esp_wifi_start(); + jsvUnLock2(name,o); + return; + } + + wifi_mode_t savedMode; + + JsVar *v = jsvObjectGetChild(o,"mode",0); + savedMode=jsvGetInteger(v); + esp_wifi_set_mode(savedMode); + jsvUnLock(v); + + //v = jsvObjectGetChild(o,"phyMode",0); + //wifi_set_phy_mode(jsvGetInteger(v)); + //jsvUnLock(v); + + //v = jsvObjectGetChild(o,"sleepType",0); + //esp_wifi_get_ps(jsvGetInteger(v)); + //jsvUnLock(v); + + wifi_ap_config_t apConfig; + bzero(&apConfig, sizeof(apConfig)); + + esp_err_t err; + if (savedMode & WIFI_MODE_AP) { + wifi_ap_config_t ap_config; + bzero(&apConfig, sizeof(ap_config)); + + v = jsvObjectGetChild(o,"authmodeAP",0); + ap_config.authmode =jsvGetInteger(v); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"hiddenAP",0); + ap_config.ssid_hidden = jsvGetInteger(v); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"ssidAP",0); + jsvGetString(v, (char *)ap_config.ssid, sizeof(ap_config.ssid)); + + ap_config.ssid_len = jsvGetStringLength(v); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"passwordAP",0); + jsvGetString(v, (char *)ap_config.password, sizeof(ap_config.password)); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"channelAP",0); + ap_config.channel = jsvGetInteger(v); + jsvUnLock(v); + + ap_config.max_connection = 4; + ap_config.beacon_interval = 100; + err = esp_wifi_set_config(WIFI_IF_AP, (wifi_config_t *)&apConfig); + jsWarn("jswrap_wifi_restore: AP=%s", ap_config.ssid); + } + + if (savedMode & WIFI_MODE_STA) { + + wifi_sta_config_t sta_config; + bzero(&sta_config, sizeof(sta_config)); + + v = jsvObjectGetChild(o,"ssid",0); + jsvGetString(v, (char *)sta_config.ssid, sizeof(sta_config.ssid)); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"password",0); + jsvGetString(v, (char *)sta_config.password, sizeof(sta_config.password)); + jsvUnLock(v); + + err = esp_wifi_set_config(ESP_IF_WIFI_STA, &sta_config); + jsWarn("Wifi.restore: STA=%s", sta_config.ssid); + + } + err = esp_wifi_start(); + if (err != ESP_OK) { + jsError( "jswrap_wifi_restore: esp_wifi_start: %d(%s)", err - ESP_ERR_WIFI_BASE,wifiErrorToString(err)); + return; + } + if (savedMode & WIFI_MODE_STA) { + v = jsvObjectGetChild(o,"hostname",0); + if (v) { + char hostname[64]; + jsvGetString(v, hostname, sizeof(hostname)); + jsWarn("Wifi.restore: hostname=%s", hostname); + tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA,hostname); + } + jsvUnLock(v); + } + if ( ( savedMode == WIFI_MODE_STA ) || ( savedMode == WIFI_MODE_APSTA ) ) { + err = esp_wifi_connect(); + if (err != ESP_OK) { + jsError( "jswrap_wifi_restore: esp_wifi_connect: %d(%s)", err - ESP_ERR_WIFI_BASE,wifiErrorToString(err)); + return; + } + } else { + jsDebug( "Wifi: Both STA AND APSTA are off"); + } +} // End of jswrap_wifi_restore /** * Get the ip info for the given interface. The interfaces are: @@ -1200,9 +1496,11 @@ static JsVar *getIPInfo(JsVar *jsCallback, tcpip_adapter_if_t interface) { // Schedule callback if a function was provided if (jsvIsFunction(jsCallback)) { - JsVar *params[1]; - params[0] = jsIpInfo; - jsiQueueEvents(NULL, jsCallback, params, 1); + JsVar *params[2]; + params[0] = jsvNewWithFlags(JSV_NULL); + params[1] = jsIpInfo; + jsiQueueEvents(NULL, jsCallback, params, 2); + jsvUnLock(params[0]); } return jsIpInfo; @@ -1220,35 +1518,194 @@ JsVar *jswrap_wifi_getAPIP(JsVar *jsCallback) { return jsIpInfo; } -void jswrap_wifi_getHostByName( - JsVar *jsHostname, - JsVar *jsCallback -) { - UNUSED(jsHostname); - UNUSED(jsCallback); - jsError( "jswrap_wifi_getHostByName - Not implemented - no api in esp-idf"); - // Could use net_esp32_gethostbyname in network_esp32.c -} - JsVar *jswrap_wifi_getHostname(JsVar *jsCallback) { - UNUSED(jsCallback); - jsError( "jswrap_wifi_getHostname - Not implemented"); - return NULL; + const char * hostname; + esp_err_t err = tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &hostname); + if (hostname == NULL) { + hostname = ""; + } + return jsvNewFromString(hostname); } void jswrap_wifi_setHostname( JsVar *jsHostname, //!< The hostname to set for device. JsVar *jsCallback ) { - UNUSED(jsHostname); - jsError( "jswrap_wifi_setHostname - Not implemented"); + char hostname[256]; + jsvGetString(jsHostname, hostname, sizeof(hostname)); + jsDebug("Wifi.setHostname: %s\n", hostname); + tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA,hostname); + + // now update mDNS + startMDNS(hostname); + + if (jsvIsFunction(jsCallback)) + jsiQueueEvents(0, jsCallback, 0, 0); +} + +static uint8_t seq_no; + +esp_err_t pingResults(ping_target_id_t msgType, esp_ping_found * pingResp){ + //printf("AvgTime:%.1fmS Sent:%d Rec:%d Err:%d min(mS):%d max(mS):%d ", + //(float)pf->total_time/pf->recv_count, pf->send_count, pf->recv_count, pf->err_count, pf->min_time, pf->max_time ); + //printf("Resp(mS):%d Timeouts:%d Total Time:%d\n",pf->resp_time, pf->timeout_count, pf->total_time); + if (g_jsPingCallback != NULL) { + JsVar *jsPingResponse = jsvNewObject(); + jsvObjectSetChildAndUnLock(jsPingResponse, "totalCount", jsvNewFromInteger(pingResp->send_count)); + jsvObjectSetChildAndUnLock(jsPingResponse, "totalBytes", jsvNewFromInteger(pingResp->total_bytes)); + jsvObjectSetChildAndUnLock(jsPingResponse, "totalTime", jsvNewFromInteger(pingResp->total_time)); + jsvObjectSetChildAndUnLock(jsPingResponse, "respTime", jsvNewFromInteger(pingResp->resp_time)); + // don't have a sequence + jsvObjectSetChildAndUnLock(jsPingResponse, "seqNo", jsvNewFromInteger(++seq_no)); + jsvObjectSetChildAndUnLock(jsPingResponse, "timeoutCount", jsvNewFromInteger(pingResp->timeout_count)); + jsvObjectSetChildAndUnLock(jsPingResponse, "bytes", jsvNewFromInteger(pingResp->bytes)); + jsvObjectSetChildAndUnLock(jsPingResponse, "error", jsvNewFromInteger(pingResp->err_count)); + JsVar *params[1]; + params[0] = jsPingResponse; + jsiQueueEvents(NULL, g_jsPingCallback, params, 1); + jsvUnLock(jsPingResponse); + } + return ESP_OK; } void jswrap_wifi_ping( JsVar *ipAddr, //!< A string or integer representation of an IP address. JsVar *pingCallback //!< Optional callback function. ) { - UNUSED(ipAddr); - UNUSED(pingCallback); - jsError( "jswrap_ESP32_ping - Not implemented"); + // If the parameter is a string, get the IP address from the string + // representation. + ip4_addr_t ip; + if (jsvIsString(ipAddr)) { + char ipString[20]; + int len = jsvGetString(ipAddr, ipString, sizeof(ipString)-1); + ipString[len] = '\0'; + ip.addr = networkParseIPAddress(ipString); + if (ip.addr == 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)) { + ip.addr = 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. + + uint32_t ping_count = 5; //how many pings per report + uint32_t ping_timeout = 1000; //mS till we consider it timed out + uint32_t ping_delay = 500; //mS between pings + esp_ping_set_target(PING_TARGET_IP_ADDRESS_COUNT, &ping_count, sizeof(uint32_t)); + esp_ping_set_target(PING_TARGET_RCV_TIMEO, &ping_timeout, sizeof(uint32_t)); + esp_ping_set_target(PING_TARGET_DELAY_TIME, &ping_delay, sizeof(uint32_t)); + esp_ping_set_target(PING_TARGET_IP_ADDRESS, &ip.addr, sizeof(uint32_t)); + esp_ping_set_target(PING_TARGET_RES_FN, &pingResults, sizeof(pingResults)); + seq_no=0; + ping_init(); +} + +void jswrap_wifi_setSNTP(JsVar *jsServer, JsVar *jsZone) { + if (!jsvIsString(jsZone)) { + jsExceptionHere(JSET_ERROR, "Zone is not a string"); + return; + } + + if (!jsvIsString(jsServer)) { + jsExceptionHere(JSET_ERROR, "Server is not a string"); + return; + } + char zone[64]; + jsvGetString(jsZone, zone, 64); + + char server[64]; + jsvGetString(jsServer, server, 64); + + setenv("TZ", zone, 1); + tzset(); + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, server); + sntp_init(); + jsWarn("SNTP: %s %s", server, zone); +} + +/** + * Handle a response from esp_gethostbyname. + * Invoke the callback function to inform the caller that a hostname has been converted to + * an IP address. The callback function should take a parameter that is the IP address. + */ +static void dnsFoundCallback( + const char *hostname, //!< The hostname that was converted to an IP address. + ip_addr_t *ipAddr, //!< The ip address retrieved. This may be 0. + void *arg //!< Parameter passed in from espconn_gethostbyname. + ) { + jsWarn("Wifi.getHostByName CB - %s %x", hostname, ipAddr ); + if (g_jsHostByNameCallback != NULL) { + JsVar *params[1]; + if (ipAddr == NULL) { + params[0] = jsvNewNull(); + } else { + params[0] = networkGetAddressAsString((uint8_t *)&ipAddr, 4, 10, '.'); + } + jsiQueueEvents(NULL, g_jsHostByNameCallback, params, 1); + jsvUnLock(params[0]); + jsvUnLock(g_jsHostByNameCallback); + g_jsHostByNameCallback = NULL; + } +} + +void jswrap_wifi_getHostByName( + JsVar *jsHostname, + JsVar *jsCallback +) { + ip_addr_t ipAddr; + char hostname[256]; + + jsWarn("Wifi.getHostByName"); + + if (!jsvIsString(jsHostname)) { + jsExceptionHere(JSET_ERROR, "Hostname parameter is not a string"); + return; + } + if (!jsvIsFunction(jsCallback)) { + jsExceptionHere(JSET_ERROR, "Callback is not a function"); + return; + } + // Save the callback unlocking an old callback if needed. + if (g_jsHostByNameCallback != NULL) jsvUnLock(g_jsHostByNameCallback); + g_jsHostByNameCallback = jsCallback; + jsvLockAgainSafe(g_jsHostByNameCallback); + + jsvGetString(jsHostname, hostname, sizeof(hostname)); + jsWarn("Wifi.getHostByName: %s\n", hostname); + esp_err_t err = dns_gethostbyname(hostname, &ipAddr, dnsFoundCallback, NULL); + if (err == ESP_OK) { + jsWarn("Already resolved\n"); + dnsFoundCallback(hostname, &ipAddr, NULL); + } else { + jsWarn("Error: %d from dns_gethostbyname", err); + dnsFoundCallback(hostname, NULL, NULL); + } } diff --git a/libs/network/esp32/network_esp32.c b/libs/network/esp32/network_esp32.c index 254a8cded..63928a950 100644 --- a/libs/network/esp32/network_esp32.c +++ b/libs/network/esp32/network_esp32.c @@ -71,7 +71,6 @@ bool net_esp32_checkError(JsNetwork *net) { /// if host=0, creates a server otherwise creates a client (and automatically connects). Returns >=0 on success int net_esp32_createsocket(JsNetwork *net, SocketType socketType, uint32_t host, unsigned short port, JsVar *options) { - NOT_USED(net); int ippProto = socketType & ST_UDP ? IPPROTO_UDP : IPPROTO_TCP; int scktType = socketType & ST_UDP ? SOCK_DGRAM : SOCK_STREAM; int sckt = -1; @@ -95,28 +94,27 @@ int net_esp32_createsocket(JsNetwork *net, SocketType socketType, uint32_t host, int optval = 1; if (setsockopt(sckt,SOL_SOCKET,SO_BROADCAST,(const char *)&optval,sizeof(optval))<0) jsWarn("setsockopt(SO_BROADCAST) failed\n"); - return sckt; - } + } else { + sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = (in_addr_t)host; + sin.sin_port = htons( port ); - sockaddr_in sin; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = (in_addr_t)host; - sin.sin_port = htons( port ); + int res = connect(sckt,(struct sockaddr *)&sin, sizeof(sockaddr_in) ); - int res = connect(sckt,(struct sockaddr *)&sin, sizeof(sockaddr_in) ); - - if (res == SOCKET_ERROR) { - #ifdef WIN_OS - int err = WSAGetLastError(); - #else - int err = errno; - #endif - if (err != EINPROGRESS && - err != EWOULDBLOCK) { - jsError("Connect failed (err %d)", err); - closesocket(sckt); - return -1; - } + if (res == SOCKET_ERROR) { + #ifdef WIN_OS + int err = WSAGetLastError(); + #else + int err = errno; + #endif + if (err != EINPROGRESS && + err != EWOULDBLOCK) { + jsError("Connect failed (err %d)", err); + closesocket(sckt); + return -1; + } + } } } else { // ------------------------------------------------- no host (=server) @@ -127,13 +125,15 @@ int net_esp32_createsocket(JsNetwork *net, SocketType socketType, uint32_t host, return 0; } - if (jsvGetBoolAndUnLock(jsvObjectGetChild(options, "reuseAddr", 0))) { + if (scktType != SOCK_DGRAM || + jsvGetBoolAndUnLock(jsvObjectGetChild(options, "reuseAddr", 0))) { int optval = 1; if (setsockopt(sckt,SOL_SOCKET,SO_REUSEADDR,(const char *)&optval,sizeof(optval)) < 0) jsWarn("setsockopt(SO_REUSADDR) failed\n"); #ifdef SO_REUSEPORT -// if (setsockopt(sckt,SOL_SOCKET,SO_REUSEPORT,(const char *)&optval,sizeof(optval)) < 0) -// jsWarn("setsockopt(SO_REUSPORT) failed\n"); + // not supported by esp-idf 3.1 + //if (setsockopt(sckt,SOL_SOCKET,SO_REUSEPORT,(const char *)&optval,sizeof(optval)) < 0) + //jsWarn("setsockopt(SO_REUSPORT) failed\n"); #endif } @@ -185,6 +185,13 @@ int net_esp32_createsocket(JsNetwork *net, SocketType socketType, uint32_t host, } } +#ifdef SO_RCVBUF + int rcvBufSize = net->data.recvBufferSize; + if (rcvBufSize > 0) { + if (setsockopt(sckt,SOL_SOCKET,SO_RCVBUF,(const char *)&rcvBufSize,sizeof(rcvBufSize))<0) + jsWarn("setsockopt(SO_RCVBUF) failed\n"); + } +#endif #ifdef SO_NOSIGPIPE // disable SIGPIPE int optval = 1; @@ -241,18 +248,16 @@ int net_esp32_recv(JsNetwork *net, SocketType socketType, int sckt, void *buf, s } else if (n>0) { // receive data if (socketType & ST_UDP) { - size_t delta = sizeof(uint32_t) + sizeof(unsigned short) + sizeof(uint16_t); - uint32_t *host = (uint32_t*)buf; - unsigned short *port = (unsigned short*)&host[1]; - uint16_t *size = (unsigned short*)&port[1]; - num = (int)recvfrom(sckt,buf+delta,len-delta,0,&fromAddr,&fromAddrLen); - *host = fromAddr.sin_addr.s_addr; - *port = ntohs(fromAddr.sin_port); - *size = num; + num = (int)recvfrom(sckt,buf+sizeof(JsNetUDPPacketHeader),len-sizeof(JsNetUDPPacketHeader),0,&fromAddr,&fromAddrLen); - DBG("Recv %d %x:%d", num, *host, *port); + JsNetUDPPacketHeader *header = (JsNetUDPPacketHeader*)buf; + *(in_addr_t*)&header->host = fromAddr.sin_addr.s_addr; + header->port = ntohs(fromAddr.sin_port); + header->length = num; + + DBG("Recv %d %x:%d", num, *(uint32_t*)&header->host, header->port); if (num==0) return -1; // select says data, but recv says 0 means connection is closed - num += delta; + num += sizeof(JsNetUDPPacketHeader); } else { num = (int)recvfrom(sckt,buf,len,0,&fromAddr,&fromAddrLen); if (num==0) return -1; // select says data, but recv says 0 means connection is closed @@ -281,20 +286,17 @@ int net_esp32_send(JsNetwork *net, SocketType socketType, int sckt, const void * flags |= MSG_NOSIGNAL; #endif if (socketType & ST_UDP) { - sockaddr_in sin; - size_t delta = sizeof(uint32_t) + sizeof(unsigned short) + sizeof(uint16_t); - uint32_t *host = (uint32_t*)buf; - unsigned short *port = (unsigned short*)&host[1]; - uint16_t *size = (uint16_t*)&port[1]; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = *(in_addr_t*)host; - sin.sin_port = htons(*port); + JsNetUDPPacketHeader *header = (JsNetUDPPacketHeader*)buf; + sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = *(in_addr_t*)&header->host; + sin.sin_port = htons(header->port); - DBG("Send %d %x:%d", len - delta, *host, *port); - n = (int)sendto(sckt, buf + delta, *size, flags, (struct sockaddr *)&sin, sizeof(sockaddr_in)) + delta; - DBG("Send bytes %d", n); + DBG("Send %d %x:%d", len - sizeof(JsNetUDPPacketHeader), *(uint32_t*)&header->host, header->port); + n = (int)sendto(sckt, buf + sizeof(JsNetUDPPacketHeader), header->length, flags, (struct sockaddr *)&sin, sizeof(sockaddr_in)) + sizeof(JsNetUDPPacketHeader); + DBG("Send bytes %d", n); } else { - n = (int)send(sckt, buf, len, flags); + n = (int)send(sckt, buf, len, flags); } return n; } else diff --git a/libs/network/esp8266/jswrap_esp8266_network.c b/libs/network/esp8266/jswrap_esp8266_network.c index 65595b9eb..bb04e575e 100644 --- a/libs/network/esp8266/jswrap_esp8266_network.c +++ b/libs/network/esp8266/jswrap_esp8266_network.c @@ -69,6 +69,7 @@ typedef long long int64_t; #include "network.h" #include "network_esp8266.h" #include "jswrap_net.h" +#include "jswrap_storage.h" //#define jsvUnLock(v) do { os_printf("Unlock %s @%d\n", __STRING(v), __LINE__); jsvUnLock(v); } while(0) @@ -469,7 +470,9 @@ void jswrap_wifi_scan(JsVar *jsCallback) { wifi_set_opmode_current(wifi_get_opmode() | STATION_MODE); // Request a scan of the network calling "scanCB" on completion - wifi_station_scan(NULL, scanCB); + struct scan_config config = {0}; + config.show_hidden = true; + wifi_station_scan(&config, scanCB); DBG("Wifi.scan starting: mode=%s\n", wifiMode[wifi_get_opmode()]); DBGV("< Wifi.scan\n"); @@ -513,6 +516,14 @@ void jswrap_wifi_startAP( // Handle any options that may have been supplied. if (jsvIsObject(jsOptions)) { + // Handle hidden + JsVar *jsHidden = jsvObjectGetChild(jsOptions, "hidden", 0); + if (jsvIsInt(jsHidden)) { + int hidden = jsvGetInteger(jsHidden); + if (hidden >= 0 && hidden <= 1) softApConfig.ssid_hidden = hidden; + } + jsvUnLock(jsHidden); + // Handle channel JsVar *jsChan = jsvObjectGetChild(jsOptions, "channel", 0); if (jsvIsInt(jsChan)) { @@ -808,123 +819,136 @@ JsVar *jswrap_wifi_getAPDetails(JsVar *jsCallback) { } void jswrap_wifi_save(JsVar *what) { - DBGV("> Wifi.save\n"); - uint32_t flashBlock[256]; - Esp8266_config *conf=(Esp8266_config *)flashBlock; - os_memset(flashBlock, 0, sizeof(flashBlock)); - uint32_t map = system_get_flash_size_map(); - - conf->length = 1024; - conf->version = 24; + DBGV("> Wifi.save\n"); + JsVar *o = jsvNewObject(); + if (!o) return; if (jsvIsString(what) && jsvIsStringEqual(what, "clear")) { - conf->mode = 0; // disable - savedMode = conf->mode; - conf->phyMode = PHY_MODE_11N; - conf->sleepType = MODEM_SLEEP_T; - // ssids, passwords, and hostname are set to zero thanks to memset above + JsVar *name = jsvNewFromString(WIFI_CONFIG_STORAGE_NAME); + jswrap_storage_erase(name); + jsvUnLock(name); DBG("Wifi.save(clear)\n"); - - } else { - conf->mode = wifi_get_opmode(); - conf->phyMode = wifi_get_phy_mode(); - conf->sleepType = wifi_get_sleep_type(); - DBG("Wifi.save: len=%d phy=%d sleep=%d opmode=%d\n", - sizeof(*conf), conf->phyMode, conf->sleepType, conf->mode); - - struct station_config sta_config; - wifi_station_get_config(&sta_config); - os_strncpy(conf->staSsid, (char *)sta_config.ssid, 32); - os_strncpy(conf->staPass, (char *)sta_config.password, 64); - - struct softap_config ap_config; - wifi_softap_get_config(&ap_config); - conf->authMode = ap_config.authmode; - conf->hidden = ap_config.ssid_hidden; - conf->ssidLen = ap_config.ssid_len; - os_strncpy(conf->apSsid, (char *)ap_config.ssid, 32); - os_strncpy(conf->apPass, (char *)ap_config.password, 64); - DBG("Wifi.save: AP=%s STA=%s\n", ap_config.ssid, sta_config.ssid); - - char *hostname = wifi_station_get_hostname(); - if (hostname) os_strncpy(conf->dhcpHostname, hostname, 64); - } - savedMode = conf->mode; - conf->crc = crc32((uint8_t*)flashBlock, sizeof(flashBlock)); - DBG("Wifi.save: len=%d vers=%d crc=0x%08lx\n", conf->length, conf->version, (long unsigned int) conf->crc); - if (map == 6 ) { - jshFlashErasePage( 0x0FB000); - jshFlashWrite(conf,0x0FB000, sizeof(flashBlock)); - } else { - jshFlashErasePage(0x7B000); - jshFlashWrite(conf, 0x7B000, sizeof(flashBlock)); + return; } + + // station stuff + struct station_config sta_config; + wifi_station_get_config(&sta_config); + jsvObjectSetChildAndUnLock(o, "ssid", jsvNewFromString((char *)sta_config.ssid)); + jsvObjectSetChildAndUnLock(o, "password", jsvNewFromString((char *)sta_config.password)); + jsvObjectSetChildAndUnLock(o, "mode", jsvNewFromInteger(wifi_get_opmode())); + jsvObjectSetChildAndUnLock(o, "phyMode", jsvNewFromInteger(wifi_get_phy_mode())); + jsvObjectSetChildAndUnLock(o, "sleepType", jsvNewFromInteger(wifi_get_sleep_type())); + + char *hostname = wifi_station_get_hostname(); + if (hostname) jsvObjectSetChildAndUnLock(o, "hostname", jsvNewFromString((char *) hostname)); + + // softap stuff + struct softap_config ap_config; + wifi_softap_get_config(&ap_config); + jsvObjectSetChildAndUnLock(o, "ssidAP", jsvNewFromString((char *)ap_config.ssid)); + jsvObjectSetChildAndUnLock(o, "passwordAP", jsvNewFromString((char *) ap_config.password)); + jsvObjectSetChildAndUnLock(o, "authmodeAP", jsvNewFromInteger(ap_config.authmode)); + jsvObjectSetChildAndUnLock(o, "hiddenAP", jsvNewFromInteger(ap_config.ssid_hidden)); + jsvObjectSetChildAndUnLock(o, "channelAP", jsvNewFromInteger(ap_config.channel)); + + savedMode = wifi_get_opmode(); + + // save object + JsVar *name = jsvNewFromString(WIFI_CONFIG_STORAGE_NAME); + //JsVar *arr = jsvNewArray(&o,1); + jswrap_storage_erase(name); + jswrap_storage_write(name,o,0,0); + //jsvUnLock3(arr,name,o); + jsvUnLock2(name,o); + DBGV("< Wifi.save: write completed\n"); } void jswrap_wifi_restore(void) { DBG("Wifi.restore\n"); - uint32_t flashBlock[256]; - Esp8266_config *conf=(Esp8266_config *)flashBlock; - os_memset(flashBlock, 0, sizeof(flashBlock)); - uint32_t map = system_get_flash_size_map(); - if (map == 6 ) { - jshFlashRead(flashBlock, 0x0FB000, sizeof(flashBlock)); - } else { - jshFlashRead(flashBlock, 0x7B000, sizeof(flashBlock)); - } - DBG("Wifi.restore: len=%d vers=%d crc=0x%08lx\n", conf->length, conf->version, (long unsigned int) conf->crc); - uint32_t crcRd = conf->crc; - conf->crc = 0; - uint32_t crcCalc = crc32((uint8_t*)flashBlock, sizeof(flashBlock)); - - wifi_set_opmode(0); - - // check that we have a good flash config - if (conf->length != 1024 || conf->version != 24 || crcRd != crcCalc || - conf->phyMode > PHY_MODE_11N || conf->sleepType > MODEM_SLEEP_T || - conf->mode > STATIONAP_MODE) { - DBG("Wifi.restore cannot restore: version read=%d exp=%d, crc read=0x%08lx cacl=0x%08lx\n", - conf->version, 24, (long unsigned int) crcRd, (long unsigned int) crcCalc); - wifi_set_phy_mode(PHY_MODE_11N); - wifi_set_opmode_current(SOFTAP_MODE); - return; + JsVar *name = jsvNewFromString(WIFI_CONFIG_STORAGE_NAME); + JsVar *o = jswrap_storage_readJSON(name); + if (!o) { // no data + jsvUnLock2(name,o); + return; } - DBG("Wifi.restore: phy=%d sleep=%d opmode=%d\n", conf->phyMode, conf->sleepType, conf->mode); + JsVar *v; + v = jsvObjectGetChild(o,"mode",0); + savedMode = jsvGetInteger(v); + jsvUnLock(v); + wifi_set_opmode_current(savedMode); - wifi_set_phy_mode(conf->phyMode); - wifi_set_sleep_type(conf->sleepType); - wifi_set_opmode_current(conf->mode); - savedMode = conf->mode; + v = jsvObjectGetChild(o,"phyMode",0); + wifi_set_phy_mode(jsvGetInteger(v)); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"sleepType",0); + wifi_set_sleep_type(jsvGetInteger(v)); + jsvUnLock(v); + + if (savedMode & SOFTAP_MODE) { - if (conf->mode & SOFTAP_MODE) { struct softap_config ap_config; os_memset(&ap_config, 0, sizeof(ap_config)); - ap_config.authmode = conf->authMode; - ap_config.ssid_hidden = conf->hidden; - ap_config.ssid_len = conf->ssidLen; - os_strncpy((char *)ap_config.ssid, conf->apSsid, 32); - os_strncpy((char *)ap_config.password, conf->apPass, 64); - ap_config.channel = 1; + + v = jsvObjectGetChild(o,"authmodeAP",0); + ap_config.authmode =jsvGetInteger(v); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"hiddenAP",0); + ap_config.ssid_hidden = jsvGetInteger(v); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"ssidAP",0); + jsvGetString(v, (char *)ap_config.ssid, sizeof(ap_config.ssid)); + + ap_config.ssid_len = jsvGetStringLength(v); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"passwordAP",0); + jsvGetString(v, (char *)ap_config.password, sizeof(ap_config.password)); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"channelAP",0); + ap_config.channel = jsvGetInteger(v); + jsvUnLock(v); + ap_config.max_connection = 4; ap_config.beacon_interval = 100; wifi_softap_set_config_current(&ap_config); DBG("Wifi.restore: AP=%s\n", ap_config.ssid); } - if (conf->mode & STATION_MODE) { - if (conf->dhcpHostname[0] != 0 && os_strlen(conf->dhcpHostname) < 64) { - DBG("Wifi.restore: hostname=%s\n", conf->dhcpHostname); - wifi_station_set_hostname(conf->dhcpHostname); + if (savedMode & STATION_MODE) { + + v = jsvObjectGetChild(o,"hostname",0); + + if (v) { + char hostname[64]; + jsvGetString(v, hostname, sizeof(hostname)); + DBG("Wifi.restore: hostname=%s\n", hostname); + wifi_station_set_hostname(hostname); } + jsvUnLock(v); struct station_config sta_config; os_memset(&sta_config, 0, sizeof(sta_config)); - os_strncpy((char *)sta_config.ssid, conf->staSsid, 32); - os_strncpy((char *)sta_config.password, conf->staPass, 64); + + v = jsvObjectGetChild(o,"ssid",0); + jsvGetString(v, (char *)sta_config.ssid, sizeof(sta_config.ssid)); + jsvUnLock(v); + + v = jsvObjectGetChild(o,"password",0); + jsvGetString(v, (char *)sta_config.password, sizeof(sta_config.password)); + jsvUnLock(v); + wifi_station_set_config_current(&sta_config); DBG("Wifi.restore: STA=%s\n", sta_config.ssid); + + //jsWarn("Station SSID '%s', password '%s'\n",sta_config.ssid,sta_config.password); + wifi_station_connect(); // we're not supposed to call this from user_init but it doesn't harm // and we need it when invoked from JS } @@ -966,9 +990,11 @@ static JsVar *getIPInfo(JsVar *jsCallback, int interface) { // Schedule callback if a function was provided if (jsvIsFunction(jsCallback)) { - JsVar *params[1]; - params[0] = jsIpInfo; - jsiQueueEvents(NULL, jsCallback, params, 1); + JsVar *params[2]; + params[0] = jsvNewWithFlags(JSV_NULL); + params[1] = jsIpInfo; + jsiQueueEvents(NULL, jsCallback, params, 2); + jsvUnLock(params[0]); } return jsIpInfo; @@ -1181,8 +1207,6 @@ void jswrap_ESP8266_wifi_reset() { void jswrap_ESP8266_wifi_init1() { DBGV("> Wifi.init1\n"); - jswrap_wifi_restore(); - // register the state change handler so we get debug printout for sure wifi_set_event_handler_cb(wifiEventHandler); @@ -1307,6 +1331,7 @@ static void pingRecvCB(void *pingOpt, void *pingResponse) { JsVar *params[1]; params[0] = jsPingResponse; jsiQueueEvents(NULL, g_jsPingCallback, params, 1); + jsvUnLock(jsPingResponse); } } @@ -1390,15 +1415,12 @@ static void setIP(JsVar *jsSettings, JsVar *jsCallback, int interface) { DBG(">> rc: %s\n", rc ? "true" : "false"); -// Schedule callback + // Schedule callback if (jsvIsFunction(jsCallback)) { - JsVar *jsRC = jsvNewObject(); - jsvObjectSetChildAndUnLock(jsRC, "success",jsvNewFromBool(rc)); JsVar *params[1]; - params[0] = jsRC; + params[0] = rc ? jsvNewWithFlags(JSV_NULL) : jsvNewFromString("Failure"); jsiQueueEvents(NULL, jsCallback, params, 1); jsvUnLock(params[0]); - jsvUnLock(jsRC); } else { jsExceptionHere(JSET_ERROR, "Callback is not a function."); @@ -1455,7 +1477,7 @@ static void scanCB(void *arg, STATUS status) { if (bssInfo->rssi > 0) bssInfo->rssi = 0; jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "rssi", jsvNewFromInteger(bssInfo->rssi)); jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "channel", jsvNewFromInteger(bssInfo->channel)); - jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "authMode", jsvNewFromInteger(bssInfo->authmode)); + jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "authMode", jsvNewFromString(wifiAuth[bssInfo->authmode])); jsvObjectSetChildAndUnLock(jsCurrentAccessPoint, "isHidden", jsvNewFromBool(bssInfo->is_hidden)); // The SSID may **NOT** be NULL terminated ... so handle that. diff --git a/libs/network/esp8266/network_esp8266.c b/libs/network/esp8266/network_esp8266.c index 407d9aabb..0cccfa4a9 100644 --- a/libs/network/esp8266/network_esp8266.c +++ b/libs/network/esp8266/network_esp8266.c @@ -160,12 +160,15 @@ static bool g_socketsInitialized = false; * socket structures and dumps their state to the debug log. */ void esp8266_dumpAllSocketData() { +#ifndef RELEASE for (int i=0; ihost; - *port = pSocketData->port; - *size = rxBuf->filled; + *(uint32_t*)&header->host = pSocketData->host; + header->port = pSocketData->port; + header->length = rxBuf->filled; } // If the receive buffer is able to completely fit in the buffer @@ -921,18 +925,16 @@ int net_ESP8266_BOARD_send( //os_printf("\n"); size_t delta = 0; if (socketType & ST_UDP) { - delta = sizeof(uint32_t) + sizeof(unsigned short) + sizeof(uint16_t); - uint32_t *host = (uint32_t*)buf; - unsigned short *port = (unsigned short*)&host[1]; - uint16_t *size = (uint16_t*)&port[1]; + JsNetUDPPacketHeader *header = (JsNetUDPPacketHeader*)buf; // UDP remote IP/port need to be set everytime we call espconn_send - *(uint32_t *)&pSocketData->pEspconn->proto.tcp->remote_ip = *host; - pSocketData->pEspconn->proto.tcp->remote_port = *port; + *(uint32_t *)&pSocketData->pEspconn->proto.tcp->remote_ip = *(uint32_t *)&header->host; + pSocketData->pEspconn->proto.tcp->remote_port = header->port; + delta = sizeof(JsNetUDPPacketHeader); buf += delta; - len = *size; - DBG("%s: Sendto %d to %x:%d\n", DBG_LIB, len, *host, *port); + len = header->length; + DBG("%s: Sendto %d to %x:%d\n", DBG_LIB, len, *(uint32_t *)&header->host, header->port); } // Copy the data to be sent into a transmit buffer we hand off to espconn diff --git a/libs/network/http/jswrap_http.c b/libs/network/http/jswrap_http.c index 0e39aa84d..cabc46254 100644 --- a/libs/network/http/jswrap_http.c +++ b/libs/network/http/jswrap_http.c @@ -64,6 +64,39 @@ The 'data' event is called when data is received. If a handler is defined with ` } Called when the connection closes. */ + + +/*JSON{ + "type" : "property", + "class" : "httpSRq", + "name" : "headers", + "generate" : false, + "return" : ["JsVar", "An object mapping header name to value" ] +} +The headers to sent to the server with this HTTP request. +*//*Documentation only*/ +/*JSON{ + "type" : "property", + "class" : "httpSRq", + "name" : "method", + "generate" : false, + "return" : ["JsVar", "A string" ] +} +The HTTP method used with this request. Often `"GET"`. +*//*Documentation only*/ +/*JSON{ + "type" : "property", + "class" : "httpSRq", + "name" : "url", + "generate" : false, + "return" : ["JsVar", "A string representing the URL" ] +} +The URL requested in this HTTP request, for instance: + +* `"/"` - the main page +* `"/favicon.ico"` - the web page's icon +*//*Documentation only*/ + /*JSON{ "type" : "method", "class" : "httpSRq", @@ -174,6 +207,42 @@ Called when the connection closes with one `hadError` boolean parameter, which i } An event that is fired if there is an error receiving the response. The error event function receives an error object as parameter with a `code` field and a `message` field. After the error event the close even will also be triggered to conclude the HTTP request/response. */ +/*JSON{ + "type" : "property", + "class" : "httpCRs", + "name" : "headers", + "generate" : false, + "return" : ["JsVar", "An object mapping header name to value" ] +} +The headers received along with the HTTP response +*//*Documentation only*/ +/*JSON{ + "type" : "property", + "class" : "httpCRs", + "name" : "statusCode", + "generate" : false, + "return" : ["JsVar", "The status code as a String" ] +} +The HTTP response's status code - usually `"200"` if all went well +*//*Documentation only*/ +/*JSON{ + "type" : "property", + "class" : "httpCRs", + "name" : "statusMessage", + "generate" : false, + "return" : ["JsVar", "An String Status Message" ] +} +The HTTP response's status message - Usually `"OK"` if all went well +*//*Documentation only*/ +/*JSON{ + "type" : "property", + "class" : "httpCRs", + "name" : "httpVersion", + "generate" : false, + "return" : ["JsVar", "Th" ] +} +The HTTP version reported back by the server - usually `"1.1"` +*//*Documentation only*/ /*JSON{ "type" : "method", "class" : "httpCRs", @@ -210,6 +279,10 @@ Pipe this to a stream (an object with a 'write' method) */ + + + + // --------------------------------------------------------------------------------- // --------------------------------------------------------------------------------- // --------------------------------------------------------------------------------- @@ -227,7 +300,7 @@ Pipe this to a stream (an object with a 'write' method) } Create an HTTP Server -When a request to the server is made, the callback is called. In the callback you can use the methods on the response (httpSRs) to send data. You can also add `request.on('data',function() { ... })` to listen for POSTed data +When a request to the server is made, the callback is called. In the callback you can use the methods on the response (`httpSRs`) to send data. You can also add `request.on('data',function() { ... })` to listen for POSTed data */ JsVar *jswrap_http_createServer(JsVar *callback) { @@ -363,6 +436,24 @@ Stop listening for new HTTP connections // --------------------------------------------------------------------------------- // --------------------------------------------------------------------------------- // --------------------------------------------------------------------------------- +/*JSON{ + "type" : "property", + "class" : "httpSRs", + "name" : "headers", + "generate" : false, + "return" : ["JsVar", "An object mapping header name to value" ] +} +The headers to send back along with the HTTP response. + +The default contents are: + +``` +{ + "Connection": "close" + } +``` +*//*Documentation only*/ + /*JSON{ "type" : "method", "class" : "httpSRs", @@ -408,11 +499,36 @@ void jswrap_httpSRs_end(JsVar *parent, JsVar *data) { ["statusCode","int32","The HTTP status code"], ["headers","JsVar","An object containing the headers"] ] -}*/ +} +Send the given status code and headers. If not explicitly called +this will be done automatically the first time data is written +to the response. + +This cannot be called twice, or after data has already been sent +in the response. +*/ void jswrap_httpSRs_writeHead(JsVar *parent, int statusCode, JsVar *headers) { serverResponseWriteHead(parent, statusCode, headers); } +/*JSON{ + "type" : "method", + "class" : "httpSRs", + "name" : "setHeader", + "generate" : "jswrap_httpSRs_setHeader", + "params" : [ + ["name","JsVar","The name of the header as a String"], + ["value","JsVar","The value of the header as a String"] + ] +} +Set a value to send in the header of this HTTP response. This updates the `httpSRs.headers` property. + +Any headers supplied to `writeHead` will overwrite any headers with the same name. +*/ +void jswrap_httpSRs_setHeader(JsVar *parent, JsVar *name, JsVar *value) { + serverResponseSetHeader(parent, name, value); +} + // --------------------------------------------------------------------------------- // --------------------------------------------------------------------------------- // --------------------------------------------------------------------------------- diff --git a/libs/network/http/jswrap_http.h b/libs/network/http/jswrap_http.h index 38ae70db4..2e883fd60 100644 --- a/libs/network/http/jswrap_http.h +++ b/libs/network/http/jswrap_http.h @@ -18,6 +18,8 @@ JsVar *jswrap_http_createServer(JsVar *callback); JsVar *jswrap_http_request(JsVar *options, JsVar *callback); JsVar *jswrap_http_get(JsVar *options, JsVar *callback); +// for HTTP +void jswrap_httpSRs_setHeader(JsVar *parent, JsVar *name, JsVar *value); void jswrap_httpSRs_writeHead(JsVar *parent, int statusCode, JsVar *headers); bool jswrap_httpSRs_write(JsVar *parent, JsVar *data); void jswrap_httpSRs_end(JsVar *parent, JsVar *data); diff --git a/libs/network/jswrap_net.c b/libs/network/jswrap_net.c index 1f3853519..a7dd069b9 100644 --- a/libs/network/jswrap_net.c +++ b/libs/network/jswrap_net.c @@ -404,6 +404,18 @@ JsVar *jswrap_net_connect(JsVar *options, JsVar *callback, SocketType socketType return 0; } + if ((socketType&ST_TYPE_MASK) == ST_UDP) { + JsNetwork net; + if (networkGetFromVar(&net)) { + int recvBufferSize = jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "recvBufferSize", 0)); + if (recvBufferSize > net.data.recvBufferSize) { + net.data.recvBufferSize = recvBufferSize; + networkSet(&net); + } + networkFree(&net); + } + } + JsVar *rq = clientRequestNew(socketType, options, callback); if (unlockOptions) jsvUnLock(options); @@ -435,7 +447,7 @@ This is designed to be a cut-down version of the [node.js library](http://nodejs "name" : "createSocket", "generate_full" : "jswrap_dgram_createSocket(type, callback)", "params" : [ - ["type","JsVar","Socket type to create e.g. 'udp4'"], + ["type","JsVar","Socket type to create e.g. 'udp4'. Or options object { type: 'udp4', reuseAddr: true, recvBufferSize: 1024 }"], ["callback","JsVar","A `function(sckt)` that will be called with the socket when a connection is made. You can then call `sckt.send(...)` to send data, and `sckt.on('message', function(data) { ... })` and `sckt.on('close', function() { ... })` to deal with the response."] ], "return" : ["JsVar","Returns a new dgram.Socket object"], @@ -518,22 +530,6 @@ void jswrap_dgram_socket_send(JsVar *parent, JsVar *buffer, JsVar *offset, JsVar } The 'message' event is called when a datagram message is received. If a handler is defined with `X.on('message', function(msg) { ... })` then it will be called` */ -void jswrap_dgram_messageCallback(JsVar *parent, JsVar *msg, JsVar *rinfo) { - assert(jsvIsObject(parent)); - assert(jsvIsString(msg)); - assert(jsvIsObject(rinfo)); - - JsVar *callback = jsvFindChildFromString(parent, DGRAM_MESSAGE_CALLBACK_NAME, false); - if (callback) { - JsVar *args[] = { msg, rinfo }; - if (!jsiExecuteEventCallback(parent, callback, 2, args)) { - jsError("Error processing Datagram message handler - removing it."); - jsErrorFlags |= JSERR_CALLBACK; - jsvObjectRemoveChild(parent, DGRAM_MESSAGE_CALLBACK_NAME); - } - jsvUnLock(callback); - } -} /*JSON{ "type" : "method", diff --git a/libs/network/jswrap_net.h b/libs/network/jswrap_net.h index fb587049a..e8dce8aae 100644 --- a/libs/network/jswrap_net.h +++ b/libs/network/jswrap_net.h @@ -15,7 +15,6 @@ #include "socketserver.h" #define DGRAM_ON_BIND_NAME JS_EVENT_PREFIX"bind" -#define DGRAM_MESSAGE_CALLBACK_NAME JS_EVENT_PREFIX"message" bool jswrap_net_idle(); void jswrap_net_init(); @@ -34,7 +33,6 @@ void jswrap_net_socket_end(JsVar *parent, JsVar *data); JsVar *jswrap_dgram_createSocket(JsVar *type, JsVar *callback); JsVar *jswrap_dgramSocket_bind(JsVar *parent, unsigned short port, JsVar *callback); -void jswrap_dgram_messageCallback(JsVar *parent, JsVar *dataString, JsVar *dataInfo); void jswrap_dgram_close(JsVar *parent); void jswrap_dgram_addMembership(JsVar *parent, JsVar *group, JsVar *ip); void jswrap_dgram_socket_send(JsVar *parent, JsVar *buffer, JsVar *offset, JsVar *length, JsVar *args); diff --git a/libs/network/jswrap_wifi.c b/libs/network/jswrap_wifi.c index cb84a4b90..f5b10cddc 100644 --- a/libs/network/jswrap_wifi.c +++ b/libs/network/jswrap_wifi.c @@ -186,7 +186,7 @@ The details include: "name" : "disconnect", "generate" : "jswrap_wifi_disconnect", "params" : [ - ["callback", "JsVar", "An optional function to be called back on disconnection. The callback function receives no argument."] + ["callback", "JsVar", "An optional `callback()` function to be called back on disconnection. The callback function receives no argument."] ] } Disconnect the wifi station from an access point and disable the station mode. It is OK to call `disconnect` to turn off station mode even if no connection exists (for example, connection attempts may be failing). Station mode can be re-enabled by calling `connect` or `scan`. @@ -198,10 +198,10 @@ Disconnect the wifi station from an access point and disable the station mode. I "name" : "stopAP", "generate" : "jswrap_wifi_stopAP", "params" : [ - ["callback", "JsVar", "An optional function to be called back on successful stop. The callback function receives no argument."] + ["callback", "JsVar", "An optional `callback()` function to be called back on successful stop. The callback function receives no argument."] ] } -Stop being an access point and disable the AP operation mode. Ap mode can be re-enabled by calling `startAP`. +Stop being an access point and disable the AP operation mode. AP mode can be re-enabled by calling `startAP`. */ /*JSON{ @@ -212,7 +212,7 @@ Stop being an access point and disable the AP operation mode. Ap mode can be re- "params" : [ ["ssid", "JsVar", "The access point network id."], ["options", "JsVar", "Connection options (optional)."], - ["callback", "JsVar", "A function to be called back on completion (optional)."] + ["callback", "JsVar", "A `callback(err)` function to be called back on completion. `err` is null on success, or contains an error string on failure."] ] } Connect to an access point as a station. If there is an existing connection to an AP it is first disconnected if the SSID or password are different from those passed as parameters. Put differently, if the passed SSID and password are identical to the currently connected AP then nothing is changed. @@ -237,7 +237,7 @@ Notes: "name" : "scan", "generate" : "jswrap_wifi_scan", "params" : [ - ["callback", "JsVar", "A function to be called back on completion."] + ["callback", "JsVar", "A `callback(err, ap_list)` function to be called back on completion. `err==null` and `ap_list` is an array on success, or `err` is an error string and `ap_list` is undefined on failure."] ] } Perform a scan for access points. This will enable the station mode if it is not currently enabled. Once the scan is complete the callback function is called with an array of APs found, each AP is an object with: @@ -263,7 +263,7 @@ Notes: "params" : [ ["ssid", "JsVar", "The network id."], ["options", "JsVar", "Configuration options (optional)."], - ["callback", "JsVar", "Optional function to be called when the AP is successfully started."] + ["callback", "JsVar", "Optional `callback(err)` function to be called when the AP is successfully started. `err==null` on success, or an error string on failure."] ] } Create a WiFi access point allowing stations to connect. If the password is NULL or an empty string the access point is open, otherwise it is encrypted. @@ -274,6 +274,7 @@ The `options` object can contain the following properties. * `authMode` - The authentication mode to use. Can be one of "open", "wpa2", "wpa", "wpa_wpa2". The default is open (but open access points are not recommended). * `password` - The password for connecting stations if authMode is not open. * `channel` - The channel to be used for the access point in the range 1..13. If the device is also connected to an access point as a station then that access point determines the channel. +* `hidden` - The flag if visible or not (0:visible, 1:hidden), default is visible. Notes: @@ -291,7 +292,7 @@ Notes: "#if" : "defined(ESP32) || defined(ESP8266)", "return" : ["JsVar", "An object representing the current WiFi status, if available immediately."], "params" : [ - ["callback", "JsVar", "An optional function to be called back with the current Wifi status, i.e. the same object as returned directly. The callback function is more portable than the direct return value."] + ["callback", "JsVar", "Optional `callback(status)` function to be called back with the current Wifi status, i.e. the same object as returned directly."] ] } Retrieve the current overall WiFi configuration. This call provides general information that pertains to both station and access point modes. The getDetails and getAPDetails calls provide more in-depth information about the station and access point configurations, respectively. The status object has the following properties: @@ -311,7 +312,7 @@ Retrieve the current overall WiFi configuration. This call provides general info "class" : "Wifi", "name" : "setConfig", "generate" : "jswrap_wifi_setConfig", - "#if" : "defined(ESP8266)", + "#if" : "defined(ESP32) || defined(ESP8266)", "params" : [ ["settings", "JsVar", "An object with the configuration settings to change."] ] @@ -333,7 +334,7 @@ Note: esp8266 SDK programmers may be missing an "opmode" option to set the sta/a "#if" : "defined(ESP32) || defined(ESP8266)", "return" : ["JsVar", "An object representing the wifi station details, if available immediately."], "params" : [ - ["callback", "JsVar", "An optional function to be called back with the wifi details, i.e. the same object as returned directly. The callback function is more portable than the direct return value."] + ["callback", "JsVar", "An optional `callback(details)` function to be called back with the wifi details, i.e. the same object as returned directly."] ] } Retrieve the wifi station configuration and status details. The details object has the following properties: @@ -355,7 +356,7 @@ Retrieve the wifi station configuration and status details. The details object h "#if" : "defined(ESP32) || defined(ESP8266)", "return" : ["JsVar", "An object representing the current access point details, if available immediately."], "params" : [ - ["callback", "JsVar", "An optional function to be called back with the current access point details, i.e. the same object as returned directly. The callback function is more portable than the direct return value."] + ["callback", "JsVar", "An optional `callback(details)` function to be called back with the current access point details, i.e. the same object as returned directly."] ] } Retrieve the current access point configuration and status. The details object has the following properties: @@ -410,7 +411,7 @@ Restores the saved Wifi configuration from flash. See `Wifi.save()`. "generate" : "jswrap_wifi_getIP", "return" : ["JsVar", "An object representing the station IP information, if available immediately (**ONLY** on ESP8266/ESP32)."], "params" : [ - ["callback", "JsVar", "An optional function to be called back with the IP information, i.e. the same object as returned directly. The callback function is more portable than the direct return value."] + ["callback", "JsVar", "An optional `callback(err, ipinfo)` function to be called back with the IP information."] ] } Return the station IP information in an object as follows: @@ -430,7 +431,7 @@ Note that the `ip`, `netmask`, and `gw` fields are omitted if no connection is e "generate" : "jswrap_wifi_getAPIP", "return" : ["JsVar", "An object representing the esp8266's Access Point IP information, if available immediately (**ONLY** on ESP8266/ESP32)."], "params" : [ - ["callback", "JsVar", "An optional function to be called back with the the IP information, i.e. the same object as returned directly. The callback function is more portable than the direct return value."] + ["callback", "JsVar", "An optional `callback(err, ipinfo)` function to be called back with the the IP information."] ] } Return the access point IP information in an object which contains: @@ -447,10 +448,10 @@ Return the access point IP information in an object which contains: "class" : "Wifi", "name" : "getHostByName", "generate" : "jswrap_wifi_getHostByName", - "#if" : "defined(ESP8266)", + "#if" : "defined(ESP8266) || defined(ESP32)", "params" : [ ["hostname", "JsVar", "The hostname to lookup."], - ["callback", "JsVar", "The callback to invoke when the hostname is returned."] + ["callback", "JsVar", "The `callback(ip)` to invoke when the IP is returned. `ip==null` on failure."] ] } Lookup the hostname and invoke a callback with the IP address as integer argument. If the lookup fails, the callback is invoked with a null argument. @@ -463,10 +464,10 @@ Lookup the hostname and invoke a callback with the IP address as integer argumen "class" : "Wifi", "name" : "getHostname", "generate" : "jswrap_wifi_getHostname", - "#if" : "defined(ESP8266)", + "#if" : "defined(ESP8266) || defined(ESP32)", "return" : ["JsVar", "The currently configured hostname, if available immediately."], "params" : [ - ["callback", "JsVar", "An optional function to be called back with the hostname, i.e. the same string as returned directly. The callback function is more portable than the direct return value."] + ["callback", "JsVar", "An optional `callback(hostname)` function to be called back with the hostname."] ] } Returns the hostname announced to the DHCP server and broadcast via mDNS when connecting to an access point. @@ -477,10 +478,10 @@ Returns the hostname announced to the DHCP server and broadcast via mDNS when co "class" : "Wifi", "name" : "setHostname", "generate" : "jswrap_wifi_setHostname", - "#if" : "defined(ESP8266) || defined(ESPRUINOWIFI)", + "#if" : "defined(ESP8266) || defined(ESPRUINOWIFI) || defined(ESP32)", "params" : [ ["hostname", "JsVar", "The new hostname."], - ["callback", "JsVar", "An optional function to be called back when the hostname is set"] + ["callback", "JsVar", "An optional `callback()` function to be called back when the hostname is set"] ] } Set the hostname. Depending on implemenation, the hostname is sent with every DHCP request and is broadcast via mDNS. The DHCP hostname may be visible in the access point and may be forwarded into DNS as hostname.local. @@ -493,7 +494,7 @@ The mDNS announcement also includes an announcement for the "espruino" service. "class" : "Wifi", "name" : "setSNTP", "generate" : "jswrap_wifi_setSNTP", - "ifdef" : "ESP8266", + "#if" : "defined(ESP8266) || defined(ESP32)", "params" : [ ["server", "JsVar", "The NTP server to query, for example, `us.pool.ntp.org`"], ["tz_offset", "JsVar", "Local time zone offset in the range -11..13."] @@ -513,7 +514,7 @@ The interval determines how often the time server is queried and Espruino's time "#if" : "defined(ESP8266) || defined(ESPRUINOWIFI)", "params" : [ ["settings", "JsVar", "Configuration settings"], - ["callback", "JsVar", "The callback to invoke when ip is set"] + ["callback", "JsVar", "A `callback(err)` function to invoke when ip is set. `err==null` on success, or a string on failure."] ] } The `settings` object must contain the following properties. @@ -532,7 +533,7 @@ The `settings` object must contain the following properties. "generate" : "jswrap_wifi_setAPIP", "params" : [ ["settings", "JsVar", "Configuration settings"], - ["callback", "JsVar", "The callback to invoke when ip is set"] + ["callback", "JsVar", "A `callback(err)` function to invoke when ip is set. `err==null` on success, or a string on failure."] ] } The `settings` object must contain the following properties. @@ -549,11 +550,11 @@ The `settings` object must contain the following properties. "type" : "staticmethod", "class" : "Wifi", "name" : "ping", - "#if" : "defined(ESPRUINOWIFI) || defined(ESP8266)", + "#if" : "defined(ESPRUINOWIFI) || defined(ESP8266) || defined(ESP32)", "generate" : "jswrap_wifi_ping", "params" : [ ["hostname", "JsVar", "The host to ping"], - ["callback", "JsVar", "A callback(time) function to invoke when a ping is received"] + ["callback", "JsVar", "A `callback(time)` function to invoke when a ping is received"] ] } Issues a ping to the given host, and calls a callback with the time when the ping is received. @@ -567,7 +568,7 @@ Issues a ping to the given host, and calls a callback with the time when the pin "generate_full" : "", "params" : [ ["enable", "JsVar", "true (or a baud rate as a number) to enable, false to disable"], - ["callback", "JsVar", "A callback(time) function to invoke when a ping is received"] + ["callback", "JsVar", "A `callback()` function to invoke when turbo mode has been set"] ] } Switch to using a higher communication speed with the WiFi module. diff --git a/libs/network/linux/network_linux.c b/libs/network/linux/network_linux.c index c2961bff7..1a55a6309 100644 --- a/libs/network/linux/network_linux.c +++ b/libs/network/linux/network_linux.c @@ -72,7 +72,6 @@ bool net_linux_checkError(JsNetwork *net) { /// if host=0, creates a server otherwise creates a client (and automatically connects). Returns >=0 on success int net_linux_createsocket(JsNetwork *net, SocketType socketType, uint32_t host, unsigned short port, JsVar *options) { - NOT_USED(net); int ippProto = socketType & ST_UDP ? IPPROTO_UDP : IPPROTO_TCP; int scktType = socketType & ST_UDP ? SOCK_DGRAM : SOCK_STREAM; int sckt = -1; @@ -96,28 +95,27 @@ int net_linux_createsocket(JsNetwork *net, SocketType socketType, uint32_t host, int optval = 1; if (setsockopt(sckt,SOL_SOCKET,SO_BROADCAST,(const char *)&optval,sizeof(optval))<0) jsWarn("setsockopt(SO_BROADCAST) failed\n"); - return sckt; - } + } else { + sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = (in_addr_t)host; + sin.sin_port = htons( port ); - sockaddr_in sin; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = (in_addr_t)host; - sin.sin_port = htons( port ); + int res = connect(sckt,(struct sockaddr *)&sin, sizeof(sockaddr_in) ); - int res = connect(sckt,(struct sockaddr *)&sin, sizeof(sockaddr_in) ); - - if (res == SOCKET_ERROR) { - #ifdef WIN_OS - int err = WSAGetLastError(); - #else - int err = errno; - #endif - if (err != EINPROGRESS && - err != EWOULDBLOCK) { - jsError("Connect failed (err %d)", err); - closesocket(sckt); - return -1; - } + if (res == SOCKET_ERROR) { + #ifdef WIN_OS + int err = WSAGetLastError(); + #else + int err = errno; + #endif + if (err != EINPROGRESS && + err != EWOULDBLOCK) { + jsError("Connect failed (err %d)", err); + closesocket(sckt); + return -1; + } + } } } else { // ------------------------------------------------- no host (=server) @@ -128,7 +126,8 @@ int net_linux_createsocket(JsNetwork *net, SocketType socketType, uint32_t host, return 0; } - if (jsvGetBoolAndUnLock(jsvObjectGetChild(options, "reuseAddr", 0))) { + if (scktType != SOCK_DGRAM || + jsvGetBoolAndUnLock(jsvObjectGetChild(options, "reuseAddr", 0))) { int optval = 1; if (setsockopt(sckt,SOL_SOCKET,SO_REUSEADDR,(const char *)&optval,sizeof(optval)) < 0) jsWarn("setsockopt(SO_REUSADDR) failed\n"); @@ -186,6 +185,13 @@ int net_linux_createsocket(JsNetwork *net, SocketType socketType, uint32_t host, } } +#ifdef SO_RCVBUF + int rcvBufSize = net->data.recvBufferSize; + if (rcvBufSize > 0) { + if (setsockopt(sckt,SOL_SOCKET,SO_RCVBUF,(const char *)&rcvBufSize,sizeof(rcvBufSize))<0) + jsWarn("setsockopt(SO_RCVBUF) failed\n"); + } +#endif #ifdef SO_NOSIGPIPE // disable SIGPIPE int optval = 1; @@ -242,18 +248,15 @@ int net_linux_recv(JsNetwork *net, SocketType socketType, int sckt, void *buf, s } else if (n>0) { // receive data if (socketType & ST_UDP) { - size_t delta = sizeof(uint32_t) + sizeof(unsigned short) + sizeof(uint16_t); - uint32_t *host = (uint32_t*)buf; - unsigned short *port = (unsigned short*)&host[1]; - uint16_t *size = (unsigned short*)&port[1]; - num = (int)recvfrom(sckt,buf+delta,len-delta,0,(struct sockaddr *)&fromAddr,(socklen_t*)&fromAddrLen); - *host = fromAddr.sin_addr.s_addr; - *port = ntohs(fromAddr.sin_port); - *size = (uint16_t)num; + JsNetUDPPacketHeader *header = (JsNetUDPPacketHeader*)buf; + num = (int)recvfrom(sckt,buf+sizeof(JsNetUDPPacketHeader),len-sizeof(JsNetUDPPacketHeader),0,(struct sockaddr *)&fromAddr,(socklen_t*)&fromAddrLen); + *(in_addr_t*)&header->host = fromAddr.sin_addr.s_addr; + header->port = ntohs(fromAddr.sin_port); + header->length = (uint16_t)num; - DBG("Recv %d %x:%d", num, *host, *port); + DBG("Recv %d %x:%d", num, *(uint32_t*)&header->host, header->port); if (num==0) return -1; // select says data, but recv says 0 means connection is closed - num += (int)delta; + num += sizeof(JsNetUDPPacketHeader); } else { num = (int)recvfrom(sckt,buf,len,0,(struct sockaddr *)&fromAddr,(socklen_t*)&fromAddrLen); if (num==0) return -1; // select says data, but recv says 0 means connection is closed @@ -282,19 +285,17 @@ int net_linux_send(JsNetwork *net, SocketType socketType, int sckt, const void * flags |= MSG_NOSIGNAL; #endif if (socketType & ST_UDP) { - sockaddr_in sin; - size_t delta = sizeof(uint32_t) + sizeof(unsigned short) + sizeof(uint16_t); - uint32_t *host = (uint32_t*)buf; - unsigned short *port = (unsigned short*)&host[1]; - uint16_t *size = (uint16_t*)&port[1]; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = *(in_addr_t*)host; - sin.sin_port = htons(*port); + JsNetUDPPacketHeader *header = (JsNetUDPPacketHeader*)buf; + sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = *(in_addr_t*)&header->host; + sin.sin_port = htons(header->port); - DBG("Send %d %x:%d", len - delta, *host, *port); - n = (int)sendto(sckt, buf + delta, *size, flags, (struct sockaddr *)&sin, sizeof(sockaddr_in)) + (int)delta; + DBG("Send %d %x:%d", len - sizeof(JsNetUDPPacketHeader), header->host, header->port); + n = (int)sendto(sckt, buf + sizeof(JsNetUDPPacketHeader), header->length, flags, (struct sockaddr *)&sin, sizeof(sockaddr_in)); + n += sizeof(JsNetUDPPacketHeader); } else { - n = (int)send(sckt, buf, len, flags); + n = (int)send(sckt, buf, len, flags); } return n; } else diff --git a/libs/network/network.c b/libs/network/network.c index 7cf59d26a..c4e875c78 100644 --- a/libs/network/network.c +++ b/libs/network/network.c @@ -195,6 +195,7 @@ void networkCreate(JsNetwork *net, JsNetworkType type) { net->data.pinCS = PIN_UNDEFINED; net->data.pinIRQ = PIN_UNDEFINED; net->data.pinEN = PIN_UNDEFINED; + net->data.recvBufferSize = 0; // use net->chunkLen jsvObjectSetChildAndUnLock(execInfo.hiddenRoot, NETWORK_VAR_NAME, net->networkVar); networkSet(net); networkGetFromVar(net); @@ -254,6 +255,11 @@ bool networkGetFromVar(JsNetwork *net) { return false; } + // Adjust for SO_RCVBUF + if (net->data.recvBufferSize > net->chunkSize) { + net->chunkSize = net->data.recvBufferSize; + } + // Save the current network as a global. networkCurrentStruct = net; return true; @@ -325,7 +331,7 @@ int ssl_recv(void *ctx, unsigned char *buf, size_t len) { int ssl_entropy(void *data, unsigned char *output, size_t len ) { NOT_USED(data); size_t i; - unsigned int r; + unsigned int r = 0; for (i=0;i=0 && sckt<32); // Create a new socketData using the variable JsVar *ssl = jsvObjectGetChild(execInfo.root, "ssl", JSV_OBJECT); if (!ssl) return false; // out of memory? @@ -672,7 +677,6 @@ int netCreateSocket(JsNetwork *net, SocketType socketType, uint32_t host, unsign if (sckt<0) return sckt; #ifdef USE_TLS - assert(sckt>=0 && sckt<32); if (socketType & ST_TLS) { if (ssl_newSocketData(sckt, options)) { } else { diff --git a/libs/network/network.h b/libs/network/network.h index 68405e4e3..5acd6b49e 100644 --- a/libs/network/network.h +++ b/libs/network/network.h @@ -55,6 +55,7 @@ typedef struct { // Info for accessing specific devices IOEventFlags device; Pin pinCS, pinIRQ, pinEN; + int recvBufferSize; ///< Amount of memory to allocate for recv (sockopt SO_RCVBUF), by default net->chunkSize } PACKED_FLAGS JsNetworkData; @@ -85,6 +86,13 @@ typedef struct JsNetwork { int (*send)(struct JsNetwork *net, SocketType socketType, int sckt, const void *buf, size_t len); } PACKED_FLAGS JsNetwork; +/// Header applied to all UDP packets when they are received +typedef struct { + uint8_t host[4]; //< host data sent from + uint16_t port; //< port data sent from + uint16_t length; //< length in bytes of the data +} PACKED_FLAGS JsNetUDPPacketHeader; + // ---------------------------------- these are in network.c // Get the relevant info for JsNetwork (done from a var in root scope) void networkCreate(JsNetwork *net, JsNetworkType type); // create the network object (ONLY to be used by network drivers) diff --git a/libs/network/socketserver.c b/libs/network/socketserver.c index 2b611cb88..ef62378bf 100644 --- a/libs/network/socketserver.c +++ b/libs/network/socketserver.c @@ -18,11 +18,14 @@ #include "jshardware.h" #include "jswrap_net.h" #include "jswrap_stream.h" +#include "jswrap_string.h" +#include "jswrap_functions.h" #define HTTP_NAME_SOCKETTYPE "type" // normal socket or HTTP #define HTTP_NAME_PORT "port" #define HTTP_NAME_SOCKET "sckt" #define HTTP_NAME_HAD_HEADERS "hdrs" +#define HTTP_NAME_ENDED "endd" #define HTTP_NAME_RECEIVE_DATA "dRcv" #define HTTP_NAME_RECEIVE_COUNT "cRcv" #define HTTP_NAME_SEND_DATA "dSnd" @@ -30,6 +33,7 @@ #define HTTP_NAME_OPTIONS_VAR "opt" #define HTTP_NAME_SERVER_VAR "svr" #define HTTP_NAME_CHUNKED "chunked" +#define HTTP_NAME_HEADERS "headers" #define HTTP_NAME_CLOSENOW "clsNow" // boolean: gotta close #define HTTP_NAME_CONNECTED "conn" // boolean: we are connected #define HTTP_NAME_CLOSE "cls" // close after sending @@ -39,6 +43,8 @@ #define HTTP_NAME_ON_DRAIN JS_EVENT_PREFIX"drain" #define HTTP_NAME_ON_ERROR JS_EVENT_PREFIX"error" +#define DGRAM_NAME_ON_MESSAGE JS_EVENT_PREFIX"message" + #define HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS "HttpCC" #define HTTP_ARRAY_HTTP_SERVERS "HttpS" #define HTTP_ARRAY_HTTP_SERVER_CONNECTIONS "HttpSC" @@ -60,13 +66,18 @@ static char DBG_LIB[] = "socketserver"; // library name // ----------------------------- +static ALWAYS_INLINE bool compareTransferEncodingAndUnlock(JsVar *encoding, char *value) { + // RFC 2616: All transfer-coding values are case-insensitive. + return jsvIsStringIEqualAndUnLock(encoding, value); +} + static void httpAppendHeaders(JsVar *string, JsVar *headerObject) { // append headers JsvObjectIterator it; jsvObjectIteratorNew(&it, headerObject); while (jsvObjectIteratorHasValue(&it)) { - JsVar *k = jsvAsString(jsvObjectIteratorGetKey(&it), true); - JsVar *v = jsvAsString(jsvObjectIteratorGetValue(&it), true); + JsVar *k = jsvAsStringAndUnLock(jsvObjectIteratorGetKey(&it)); + JsVar *v = jsvAsStringAndUnLock(jsvObjectIteratorGetValue(&it)); jsvAppendStringVarComplete(string, k); jsvAppendString(string, ": "); jsvAppendStringVarComplete(string, v); @@ -109,7 +120,7 @@ bool httpParseHeaders(JsVar **receiveData, JsVar *objectForData, bool isServer) // Now parse the header JsVar *vHeaders = jsvNewObject(); if (!vHeaders) return true; - jsvUnLock(jsvAddNamedChild(objectForData, vHeaders, "headers")); + jsvUnLock(jsvAddNamedChild(objectForData, vHeaders, HTTP_NAME_HEADERS)); strIdx = 0; int firstSpace = -1; int secondSpace = -1; @@ -152,6 +163,15 @@ bool httpParseHeaders(JsVar **receiveData, JsVar *objectForData, bool isServer) strIdx++; } jsvStringIteratorFree(&it); + // flag the req/response if Transfer-Encoding:chunked was set + JsVarInt contentToReceive; + if (compareTransferEncodingAndUnlock(jsvObjectGetChildI(vHeaders, "Transfer-Encoding"), "chunked")) { + jsvObjectSetChildAndUnLock(objectForData, HTTP_NAME_CHUNKED, jsvNewFromBool(true)); + contentToReceive = 1; + } else { + contentToReceive = jsvGetIntegerAndUnLock(jsvObjectGetChildI(vHeaders,"Content-Length")); + } + jsvObjectSetChildAndUnLock(objectForData, HTTP_NAME_RECEIVE_COUNT, jsvNewFromInteger(contentToReceive)); jsvUnLock(vHeaders); // try and pull out methods/etc if (isServer) { @@ -285,23 +305,144 @@ int socketSendData(JsNetwork *net, JsVar *connection, int sckt, JsVar **sendData return 0; } -void socketReceivedUDP(JsVar *connection, JsVar *receiveData, char *buf, int num) { - JsVar *address = jsvNewFromEmptyString(); // inet_ntoa replacement - JsVar *receiveInfo = jsvNewObject(); - if (receiveInfo && address) { // could be out of memory - uint32_t delta = sizeof(uint32_t) + sizeof(unsigned short) + sizeof(uint16_t); - uint32_t host = *(uint32_t*)buf; - unsigned short port = *(unsigned short*)(buf + sizeof(uint32_t)); - buf += delta; - num -= (int)delta; - jsvAppendStringBuf(receiveData, buf, (size_t)num); - jsvAppendPrintf(address, "%d.%d.%d.%d", ((uint8_t*)&host)[0], ((uint8_t*)&host)[1], ((uint8_t*)&host)[2], ((uint8_t*)&host)[3]); - jsvObjectSetChildAndUnLock(receiveInfo, "address", address); - jsvObjectSetChildAndUnLock(receiveInfo, "port", jsvNewFromInteger(port)); - jsvObjectSetChildAndUnLock(receiveInfo, "size", jsvNewFromInteger(num)); - jswrap_dgram_messageCallback(connection, receiveData, receiveInfo); - jsvUnLock(receiveInfo); +void socketPushReceiveData(JsVar *reader, JsVar **receiveData, bool isHttp, bool force) { + if (!*receiveData || jsvIsEmptyString(*receiveData)) { + // no data available (after headers) + return; } + + JsVar *nextChunk = 0; + JsVar *partialChunk = 0; + + // Keep track of how much we received (so we can close once we have it) + if (isHttp) { + size_t len = (size_t)jsvGetStringLength(*receiveData); + if (jsvGetBoolAndUnLock(jsvObjectGetChild(reader, HTTP_NAME_CHUNKED, 0))) { + // check for incomplete chunk, at least "0\r\n\r\n" + if (len < 5) return; // incomplete, wait for more data + + JsVar *crlf = jsvNewFromString("\r\n"); + JsVar *zero = jsvNewFromInteger(0); + size_t startIdx = (size_t)jswrap_string_indexOf(*receiveData, crlf, zero, false); + jsvUnLock2(crlf, zero); + + JsVar *sixteen = jsvNewFromInteger(16); + int chunkLen = jsvGetIntegerAndUnLock(jswrap_parseInt(*receiveData, sixteen)); + jsvUnLock(sixteen); + DBG("D:%d\n", chunkLen); + + // for 'chunked' set the counter to 1 to read on or 0 if at last chunk + jsvObjectSetChildAndUnLock(reader, HTTP_NAME_RECEIVE_COUNT, jsvNewFromInteger(chunkLen ? 1 : 0)); + if (!chunkLen) { // no 'data' callback + // clear received data + jsvUnLock(*receiveData); + *receiveData = 0; + return; + } + + startIdx += 2; // skip the CRLF + size_t nextIdx = startIdx + (size_t)chunkLen + 2; // CRLF at the end + if (nextIdx < len) { // there is another chunk in the buffer + DBG("D:nextIdx %d %d\n", nextIdx, len); + nextChunk = jsvNewFromEmptyString(); + if (!nextChunk) return; // out of memory + jsvAppendStringVar(nextChunk, *receiveData, nextIdx, len-nextIdx); + } else if (nextIdx > len) { // chunk not complete + DBG("D:partialIdx %d %d %d\n", len - startIdx, nextIdx - len - 2, len); + if (nextIdx - len < 3) return; // just CRLF missing, wait + // use the data available, write remaining length, wait + partialChunk = jsvNewFromEmptyString(); + if (!partialChunk) return; // out of memory + jsvAppendPrintf(partialChunk, "%x\r\n", nextIdx - len - 2); + } + + JsVar *chunkData = jsvNewFromEmptyString(); + if (!chunkData) return; // out of memory + jsvAppendStringVar(chunkData, *receiveData, startIdx, (size_t)chunkLen); + jsvUnLock(*receiveData); + *receiveData = chunkData; + } else { + jsvObjectSetChildAndUnLock(reader, HTTP_NAME_RECEIVE_COUNT, + jsvNewFromInteger( + jsvGetIntegerAndUnLock(jsvObjectGetChild(reader, HTTP_NAME_RECEIVE_COUNT, JSV_INTEGER)) - (JsVarInt)len) + ); + } + } + + // execute 'data' callback or save data + if (!jswrap_stream_pushData(reader, *receiveData, force)) { + jsvUnLock2(nextChunk, partialChunk); + return; + } + + // clear received data + jsvUnLock(*receiveData); + *receiveData = nextChunk ? nextChunk : partialChunk; + + if (nextChunk) { // process following chunks if any + socketPushReceiveData(reader, receiveData, isHttp, force); + } +} + +void socketReceivedUDP(JsVar *connection, JsVar **receiveData) { + // Get the header + size_t len = jsvGetStringLength(*receiveData); + if (len < sizeof(JsNetUDPPacketHeader)) return; // not enough data for header! + char buf[sizeof(JsNetUDPPacketHeader)+1]; // trailing 0 from jsvGetStringChars + jsvGetStringChars(*receiveData, 0, buf, sizeof(JsNetUDPPacketHeader)+1); + JsNetUDPPacketHeader *header = (JsNetUDPPacketHeader*)buf; + if (sizeof(JsNetUDPPacketHeader)+header->length < len) return; // not enough data yet + + JsVar *rinfo = jsvNewObject(); + if (rinfo) { + // split the received data string to get the data we need + JsVar *data = jsvNewFromStringVar(*receiveData, sizeof(JsNetUDPPacketHeader), header->length); + JsVar *newReceiveData = 0; + if (len > sizeof(JsNetUDPPacketHeader)+header->length) + newReceiveData = jsvNewFromStringVar(*receiveData, sizeof(JsNetUDPPacketHeader)+header->length, JSVAPPENDSTRINGVAR_MAXLENGTH); + jsvUnLock(*receiveData); + *receiveData = newReceiveData; + // fire the received data event + jsvObjectSetChildAndUnLock(rinfo, "address", jsvVarPrintf("%d.%d.%d.%d", header->host[0], header->host[1], header->host[2], header->host[3])); + jsvObjectSetChildAndUnLock(rinfo, "port", jsvNewFromInteger(header->port)); + jsvObjectSetChildAndUnLock(rinfo, "size", jsvNewFromInteger(header->length)); + JsVar *args[2] = { data, rinfo }; + jsiQueueObjectCallbacks(connection, DGRAM_NAME_ON_MESSAGE, args, 2); + jsvUnLock2(data,rinfo); + } +} + +void socketReceived(JsVar *connection, JsVar *socket, SocketType socketType, JsVar **receiveData, bool isServer) { + if ((socketType&ST_TYPE_MASK)==ST_UDP) { + socketReceivedUDP(connection, receiveData); + return; + } + JsVar *reader = isServer ? connection : socket; + bool isHttp = (socketType&ST_TYPE_MASK)==ST_HTTP; + bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(reader,HTTP_NAME_HAD_HEADERS,0)); + if (!hadHeaders) { + if (!isHttp) { + hadHeaders = true; + } else if (httpParseHeaders(receiveData, reader, isServer)) { + hadHeaders = true; + + // on connect only when just parsed the HTTP headers + if (isServer) { + JsVar *server = jsvObjectGetChild(connection,HTTP_NAME_SERVER_VAR,0); + JsVar *args[2] = { connection, socket }; + jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, args, isHttp ? 2 : 1); + jsvUnLock(server); + } else { + jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &socket, 1); + } + } + jsvObjectSetChildAndUnLock(reader, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders)); + } + if (!hadHeaders) { + // no headers yet, no 'data' callback + return; + } + socketPushReceiveData(reader, receiveData, isHttp, false); } @@ -361,7 +502,8 @@ bool socketServerConnectionsIdle(JsNetwork *net) { // For normal sockets, socket==connection, but for HTTP we split it into a request and a response JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); - JsVar *socket = ((socketType&ST_TYPE_MASK)==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); + bool isHttp = (socketType&ST_TYPE_MASK) == ST_HTTP; + JsVar *socket = isHttp ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); @@ -376,42 +518,11 @@ bool socketServerConnectionsIdle(JsNetwork *net) { } else { if (num>0) { JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); - JsVar *oldReceiveData = receiveData; if (!receiveData) receiveData = jsvNewFromEmptyString(); if (receiveData) { - if ((socketType&ST_TYPE_MASK)==ST_UDP) { - socketReceivedUDP(connection, receiveData, buf, num); - } else { - jsvAppendStringBuf(receiveData, buf, (size_t)num); - bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); - if (!hadHeaders && httpParseHeaders(&receiveData, connection, true)) { - hadHeaders = true; - jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders)); - JsVar *server = jsvObjectGetChild(connection,HTTP_NAME_SERVER_VAR,0); - JsVar *args[2] = { connection, socket }; - jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, args, ((socketType&ST_TYPE_MASK)==ST_HTTP) ? 2 : 1); - jsvUnLock(server); - } - if (hadHeaders && !jsvIsEmptyString(receiveData)) { - // Keep track of how much we received (so we can close once we have it) - if ((socketType&ST_TYPE_MASK)==ST_HTTP) { - jsvObjectSetChildAndUnLock(connection, HTTP_NAME_RECEIVE_COUNT, - jsvNewFromInteger( - jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, JSV_INTEGER)) + - (JsVarInt)jsvGetStringLength(receiveData) - )); - } - // execute 'data' callback or save data - if (jswrap_stream_pushData(connection, receiveData, false)) { - // clear received data - jsvUnLock(receiveData); - receiveData = 0; - } - } - // if received data changed, update it - if (receiveData != oldReceiveData) - jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,receiveData); - } + jsvAppendStringBuf(receiveData, buf, (size_t)num); + socketReceived(connection, socket, socketType, &receiveData, true); + jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,receiveData); jsvUnLock(receiveData); } } @@ -433,19 +544,17 @@ bool socketServerConnectionsIdle(JsNetwork *net) { jsvObjectSetChild(socket, HTTP_NAME_SEND_DATA, sendData); // socketSendData updated sendData } // only close if we want to close, have no data to send, and aren't receiving data - bool wantClose = jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_CLOSE,0)); - if (wantClose && (!sendData || jsvIsEmptyString(sendData)) && num<=0) { - bool reallyCloseNow = true; - if ((socketType&ST_TYPE_MASK)==ST_HTTP) { - // Check if we had a Content-Length header - if so, we need to wait until we have received that amount - JsVar *headers = jsvObjectGetChild(connection,"headers",0); - if (headers) { - JsVarInt contentLength = jsvGetIntegerAndUnLock(jsvObjectGetChild(headers,"Content-Length",0)); - JsVarInt contentReceived = jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, 0)); - if (contentLength > contentReceived) { - reallyCloseNow = false; - } - jsvUnLock(headers); + if ((!sendData || jsvIsEmptyString(sendData)) && num<=0) { + bool reallyCloseNow = jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_CLOSE,0)); + if (isHttp) { + bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); + JsVarInt contentToReceive = jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, 0)); + if (contentToReceive > 0 || !hadHeaders) { + reallyCloseNow = false; + } else if (!jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_ENDED,0))) { + jsvObjectSetChildAndUnLock(connection, HTTP_NAME_ENDED, jsvNewFromBool(true)); + jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_END, NULL, 0); + DBG("ONEND %d (%d)\n", contentToReceive, reallyCloseNow); } } closeConnectionNow = reallyCloseNow; @@ -457,21 +566,21 @@ bool socketServerConnectionsIdle(JsNetwork *net) { DBG("CLOSE NOW\n"); // send out any data that we were POSTed - JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); - if (hadHeaders && !jsvIsEmptyString(receiveData)) { + if (hadHeaders) { // execute 'data' callback or save data - jswrap_stream_pushData(connection, receiveData, true); + JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); + socketPushReceiveData(connection, &receiveData, isHttp, true); + jsvUnLock(receiveData); } - jsvUnLock(receiveData); // fire error events bool hadError = fireErrorEvent(error, connection, socket); - // fire the close listeners - if (connection!=socket) - jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_END, NULL, 0); + // fire end listeners jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, NULL, 0); + + // fire the close listeners JsVar *params[1] = { jsvNewFromBool(hadError) }; if (connection!=socket) jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CLOSE, params, 1); @@ -494,18 +603,6 @@ bool socketServerConnectionsIdle(JsNetwork *net) { } -void socketClientPushReceiveData(JsVar *connection, JsVar *socket, JsVar **receiveData) { - if (*receiveData) { - if (jsvIsEmptyString(*receiveData) || - jswrap_stream_pushData(socket, *receiveData, false)) { - // clear - because we have issued a callback - jsvObjectRemoveChild(connection,HTTP_NAME_RECEIVE_DATA); - jsvUnLock(*receiveData); - *receiveData = 0; - } - } -} - bool socketClientConnectionsIdle(JsNetwork *net) { char *buf = alloca((size_t)net->chunkSize); // allocate on stack @@ -521,19 +618,19 @@ bool socketClientConnectionsIdle(JsNetwork *net) { // For normal sockets, socket==connection, but for HTTP connection is httpCRq and socket is httpCRs JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); - JsVar *socket = ((socketType&ST_TYPE_MASK)==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); + bool isHttp = (socketType&ST_TYPE_MASK) == ST_HTTP; + JsVar *socket = isHttp ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); bool socketClosed = false; JsVar *receiveData = 0; bool hadHeaders = false; int error = 0; // error code received from netXxxx functions - bool isHttp = (socketType&ST_TYPE_MASK) == ST_HTTP; bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); bool alreadyConnected = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CONNECTED, false)); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined if (sckt>=0) { if (isHttp) - hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); + hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_HAD_HEADERS,0)); else hadHeaders = true; receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); @@ -541,7 +638,7 @@ bool socketClientConnectionsIdle(JsNetwork *net) { /* We do this up here because we want to wait until we have been once * around the idle loop (=callbacks have been executed) before we run this */ if (hadHeaders) - socketClientPushReceiveData(connection, socket, &receiveData); + socketPushReceiveData(socket, &receiveData, isHttp, false); if (!closeConnectionNow) { JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); @@ -566,52 +663,52 @@ bool socketClientConnectionsIdle(JsNetwork *net) { // no data to send, do we want to close? do so. if (jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSE, false))) closeConnectionNow = true; + if (isHttp) { + JsVarInt contentToReceive = jsvGetIntegerAndUnLock(jsvObjectGetChild(socket, HTTP_NAME_RECEIVE_COUNT, 0)); + if (contentToReceive > 0 || !hadHeaders) { + closeConnectionNow = false; + } else if (!jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_ENDED,0))) { + jsvObjectSetChildAndUnLock(socket, HTTP_NAME_ENDED, jsvNewFromBool(true)); + jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, NULL, 0); + DBG("onEnd %d (%d) %d\n", contentToReceive, closeConnectionNow, hadHeaders); + } + } } // Now read data if possible (and we have space for it) - if (!receiveData || !hadHeaders) { - int num = netRecv(net, socketType, sckt, buf, (size_t)net->chunkSize); - if (!alreadyConnected && num == SOCKET_ERR_NO_CONN) { - ; // ignore... it's just telling us we're not connected yet - } else if (num < 0) { - closeConnectionNow = true; - error = num; - // disconnected without headers? error. - if (!hadHeaders && error == SOCKET_ERR_CLOSED) error = SOCKET_ERR_NO_RESP; - } else { - // did we just get connected? - if (!alreadyConnected && !isHttp) { - jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &connection, 1); - jsvObjectSetChildAndUnLock(connection, HTTP_NAME_CONNECTED, jsvNewFromBool(true)); - alreadyConnected = true; - // if we do not have any data to send, issue a drain event - if (!sendData || (int)jsvGetStringLength(sendData) == 0) - jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_DRAIN, &connection, 1); + int num = netRecv(net, socketType, sckt, buf, (size_t)net->chunkSize); + if (!alreadyConnected && num == SOCKET_ERR_NO_CONN) { + ; // ignore... it's just telling us we're not connected yet + } else if (num < 0) { + closeConnectionNow = true; + // only error out when the response was not completely received + if (num == SOCKET_ERR_CLOSED) { + JsVarInt contentToReceive = jsvGetIntegerAndUnLock(jsvObjectGetChild(socket, HTTP_NAME_RECEIVE_COUNT, 0)); + if (!isHttp || contentToReceive > 0 || !hadHeaders) { + error = num; + // disconnected without headers? error. + if (!hadHeaders) error = SOCKET_ERR_NO_RESP; } - // got data add it to our receive buffer - if (num > 0) { - if (!receiveData) { - receiveData = jsvNewFromEmptyString(); - } - if (receiveData) { // could be out of memory - if ((socketType&ST_TYPE_MASK)==ST_UDP) { - socketReceivedUDP(connection, receiveData, buf, num); - } else { - jsvAppendStringBuf(receiveData, buf, (size_t)num); - jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); - - if ((socketType&ST_TYPE_MASK)==ST_HTTP && !hadHeaders) { - // for HTTP see whether we now have full response headers - JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); - if (httpParseHeaders(&receiveData, resVar, false)) { - hadHeaders = true; - jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders)); - jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &resVar, 1); - } - jsvUnLock(resVar); - jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); - } - } - } + } else { + error = num; + } + } else { + // did we just get connected? + if (!alreadyConnected && !isHttp) { + jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &connection, 1); + jsvObjectSetChildAndUnLock(connection, HTTP_NAME_CONNECTED, jsvNewFromBool(true)); + alreadyConnected = true; + // if we do not have any data to send, issue a drain event + if (!sendData || (int)jsvGetStringLength(sendData) == 0) + jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_DRAIN, &connection, 1); + } + // got data add it to our receive buffer + if (num > 0) { + if (!receiveData) + receiveData = jsvNewFromEmptyString(); + if (receiveData) { // could be out of memory + jsvAppendStringBuf(receiveData, buf, (size_t)num); + socketReceived(connection, socket, socketType, &receiveData, false); + jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } } } @@ -622,8 +719,8 @@ bool socketClientConnectionsIdle(JsNetwork *net) { if (closeConnectionNow) { DBG("close now\n"); - socketClientPushReceiveData(connection, socket, &receiveData); - if (!receiveData) { + socketPushReceiveData(socket, &receiveData, isHttp, true); + if (!receiveData || jsvIsEmptyString(receiveData)) { // If we had data to send but the socket closed, this is an error JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); if (sendData && jsvGetStringLength(sendData) > 0 && error == SOCKET_ERR_CLOSED) @@ -640,8 +737,13 @@ bool socketClientConnectionsIdle(JsNetwork *net) { // fire error event, if there is an error bool hadError = fireErrorEvent(error, connection, NULL); + if (!jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_ENDED,0))) { + jsvObjectSetChildAndUnLock(socket, HTTP_NAME_ENDED, jsvNewFromBool(true)); + jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, NULL, 0); + DBG("onEnd:force\n"); + } + // close callback must happen after error callback - jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, NULL, 0); JsVar *params[1] = { jsvNewFromBool(hadError) }; jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, params, 1); jsvUnLock(params[0]); @@ -675,16 +777,16 @@ bool socketIdle(JsNetwork *net) { while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; - JsVar *server = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(server); int theClient = -1; + // Check for new connections if ((socketType&ST_TYPE_MASK)!=ST_UDP) { int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(server,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined theClient = netAccept(net, sckt); } - if (theClient >= 0) { + if (theClient >= 0) { // We have a new connection if ((socketType&ST_TYPE_MASK) == ST_HTTP) { JsVar *req = jspNewObject(0, "httpSRq"); JsVar *res = jspNewObject(0, "httpSRs"); @@ -698,6 +800,13 @@ bool socketIdle(JsNetwork *net) { jsvObjectSetChild(req, HTTP_NAME_RESPONSE_VAR, res); jsvObjectSetChild(req, HTTP_NAME_SERVER_VAR, server); jsvObjectSetChildAndUnLock(req, HTTP_NAME_SOCKET, jsvNewFromInteger(theClient+1)); + jsvObjectSetChildAndUnLock(res, HTTP_NAME_SOCKET, jsvNewFromInteger(theClient+1)); + // Auto-add connection close header (in HTTP/1.0 this seemed implicit, now it must be explicit) + // This can always be overwritten with setHeader or writeHead + JsVar *name = jsvNewFromString("Connection"); + JsVar *value = jsvNewFromString("close"); + serverResponseSetHeader(res, name, value); + jsvUnLock2(name, value); } jsvUnLock2(req, res); } else { @@ -846,15 +955,15 @@ void clientRequestWrite(JsNetwork *net, JsVar *httpClientReqVar, JsVar *data, Js JsVar *path = jsvObjectGetChild(options, "path", 0); sendData = jsvVarPrintf("%v %v HTTP/1.1\r\nUser-Agent: Espruino "JS_VERSION"\r\nConnection: close\r\n", method, path); jsvUnLock2(method, path); - JsVar *headers = jsvObjectGetChild(options, "headers", 0); + JsVar *headers = jsvObjectGetChild(options, HTTP_NAME_HEADERS, 0); bool hasHostHeader = false; if (jsvIsObject(headers)) { - JsVar *hostHeader = jsvObjectGetChild(headers, "Host", 0); + JsVar *hostHeader = jsvObjectGetChildI(headers, "Host"); hasHostHeader = hostHeader!=0; jsvUnLock(hostHeader); httpAppendHeaders(sendData, headers); // if Transfer-Encoding:chunked was set, subsequent writes need to 'chunk' the data that is sent - if (jsvIsStringEqualAndUnLock(jsvObjectGetChild(headers, "Transfer-Encoding", 0), "chunked")) { + if (compareTransferEncodingAndUnlock(jsvObjectGetChild(headers, "Transfer-Encoding", 0), "chunked")) { jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CHUNKED, jsvNewFromBool(true)); } } @@ -880,10 +989,9 @@ void clientRequestWrite(JsNetwork *net, JsVar *httpClientReqVar, JsVar *data, Js // We have data and aren't out of memory... if (data && sendData) { // append the data to what we want to send - JsVar *s = jsvAsString(data, false); + JsVar *s = jsvAsString(data); if (s) { - if ((socketType&ST_TYPE_MASK) == ST_HTTP && - jsvGetBoolAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_CHUNKED, 0))) { + if (jsvGetBoolAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_CHUNKED, 0))) { // If we asked to send 'chunked' data, we need to wrap it up, // prefixed with the length jsvAppendPrintf(sendData, "%x\r\n%v\r\n", jsvGetStringLength(s), s); @@ -891,14 +999,12 @@ void clientRequestWrite(JsNetwork *net, JsVar *httpClientReqVar, JsVar *data, Js if ((socketType&ST_TYPE_MASK) == ST_UDP) { char hostName[128]; jsvGetString(host, hostName, sizeof(hostName)); - uint32_t host_addr = 0; - uint16_t size = (uint16_t)jsvGetStringLength(s); - networkGetHostByName(net, hostName, &host_addr); - jsvAppendStringBuf(sendData, (const char*)&host_addr, sizeof(host_addr)); - jsvAppendStringBuf(sendData, (const char*)&portNumber, sizeof(portNumber)); - jsvAppendStringBuf(sendData, (const char*)&size, sizeof(size)); + JsNetUDPPacketHeader header; + networkGetHostByName(net, hostName, (uint32_t*)&header.host); + header.port = portNumber; + header.length = (uint16_t)jsvGetStringLength(s); + jsvAppendStringBuf(sendData, (const char*)&header, sizeof(header)); } - jsvAppendStringVarComplete(sendData,s); } jsvUnLock(s); @@ -920,7 +1026,7 @@ void clientRequestConnect(JsNetwork *net, JsVar *httpClientReqVar) { SocketType socketType = socketGetType(httpClientReqVar); - JsVar *options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, false); + JsVar *options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, 0); unsigned short port = (unsigned short)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "port", 0)); uint32_t host_addr = 0; @@ -983,19 +1089,30 @@ void clientRequestEnd(JsNetwork *net, JsVar *httpClientReqVar) { clientRequestWrite(net, httpClientReqVar, finalData, NULL, 0); jsvUnLock(finalData); } else { - // on normal sockets, we actually request close after all data sent - jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSE, jsvNewFromBool(true)); // if we never sent any data, make sure we close 'now' JsVar *sendData = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, 0); if (!sendData || jsvIsEmptyString(sendData)) jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSENOW, jsvNewFromBool(true)); jsvUnLock(sendData); } + + // request close after all data sent + jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSE, jsvNewFromBool(true)); +} + +void serverResponseSetHeader(JsVar *httpServerResponseVar, JsVar *name, JsVar *value) { + name = jsvAsString(name); + value = jsvAsString(value); + JsVar *headers = jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_HEADERS, JSV_OBJECT); + if (jsvIsObject(headers)) + jsvObjectSetChildVar(headers, name, value); + jsvUnLock3(headers,name,value); } -void serverResponseWriteHead(JsVar *httpServerResponseVar, int statusCode, JsVar *headers) { - if (!jsvIsUndefined(headers) && !jsvIsObject(headers)) { +void serverResponseWriteHead(JsVar *httpServerResponseVar, int statusCode, JsVar *explicitHeaders) { + DBG("serverResponseWriteHead %d\n", statusCode); + if (!jsvIsUndefined(explicitHeaders) && !jsvIsObject(explicitHeaders)) { jsError("Headers sent to writeHead should be an object"); return; } @@ -1008,8 +1125,22 @@ void serverResponseWriteHead(JsVar *httpServerResponseVar, int statusCode, JsVar return; } + JsVar *headers = jsvNewObject(); + JsVar *implicitHeaders = jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_HEADERS, 0); + if (jsvIsObject(implicitHeaders)) jsvObjectAppendAll(headers, implicitHeaders); + jsvUnLock(implicitHeaders); + if (jsvIsObject(explicitHeaders)) jsvObjectAppendAll(headers, explicitHeaders); + + sendData = jsvVarPrintf("HTTP/1.1 %d OK\r\nServer: Espruino "JS_VERSION"\r\n", statusCode); - if (headers) httpAppendHeaders(sendData, headers); + if (headers) { + httpAppendHeaders(sendData, headers); + // if Transfer-Encoding:chunked was set, subsequent writes need to 'chunk' the data that is sent + if (compareTransferEncodingAndUnlock(jsvObjectGetChildI(headers, "Transfer-Encoding"), "chunked")) { + jsvObjectSetChildAndUnLock(httpServerResponseVar, HTTP_NAME_CHUNKED, jsvNewFromBool(true)); + } + } + jsvUnLock(headers); // finally add ending newline jsvAppendString(sendData, "\r\n"); jsvObjectSetChildAndUnLock(httpServerResponseVar, HTTP_NAME_SEND_DATA, sendData); @@ -1032,16 +1163,31 @@ void serverResponseWrite(JsVar *httpServerResponseVar, JsVar *data) { } // check, just in case! if (sendData && !jsvIsUndefined(data)) { - JsVar *s = jsvAsString(data, false); - if (s) jsvAppendStringVarComplete(sendData,s); + JsVar *s = jsvAsString(data); + if (s) { + if (jsvGetBoolAndUnLock(jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_CHUNKED, 0))) { + // If we asked to send 'chunked' data, we need to wrap it up, + // prefixed with the length + jsvAppendPrintf(sendData, "%x\r\n%v\r\n", jsvGetStringLength(s), s); + } else { + jsvAppendStringVarComplete(sendData,s); + } + } jsvUnLock(s); } + DBG("serverResponseWrite %v\n", sendData); jsvUnLock(sendData); } void serverResponseEnd(JsVar *httpServerResponseVar) { - serverResponseWrite(httpServerResponseVar, 0); // force connection->sendData to be created even if data not called - // TODO: This should only close the connection once the received data length == contentLength header + JsVar *finalData = 0; + if (jsvGetBoolAndUnLock(jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_CHUNKED, 0))) { + // If we were asked to send 'chunked' data, we need to finish up + finalData = jsvNewFromString(""); + } + serverResponseWrite(httpServerResponseVar, finalData); // force connection->sendData to be created even if data not called + jsvUnLock(finalData); + jsvObjectSetChildAndUnLock(httpServerResponseVar, HTTP_NAME_CLOSE, jsvNewFromBool(true)); } diff --git a/libs/network/socketserver.h b/libs/network/socketserver.h index 33621b6a7..94d17f5dc 100644 --- a/libs/network/socketserver.h +++ b/libs/network/socketserver.h @@ -35,7 +35,8 @@ void clientRequestWrite(JsNetwork *net, JsVar *httpClientReqVar, JsVar *data, Js void clientRequestConnect(JsNetwork *net, JsVar *httpClientReqVar); void clientRequestEnd(JsNetwork *net, JsVar *httpClientReqVar); -void serverResponseWriteHead(JsVar *httpServerResponseVar, int statusCode, JsVar *headers); +void serverResponseSetHeader(JsVar *parent, JsVar *name, JsVar *value); // for HTTP +void serverResponseWriteHead(JsVar *httpServerResponseVar, int statusCode, JsVar *headers); // for HTTP void serverResponseWrite(JsVar *httpServerResponseVar, JsVar *data); void serverResponseEnd(JsVar *httpServerResponseVar); diff --git a/libs/network/telnet/jswrap_telnet.c b/libs/network/telnet/jswrap_telnet.c index 8bc99176d..6cdb8ceae 100644 --- a/libs/network/telnet/jswrap_telnet.c +++ b/libs/network/telnet/jswrap_telnet.c @@ -286,8 +286,9 @@ void telnetSendChar(char ch) { bool telnetRecv(JsNetwork *net) { if (tnSrv.sock == 0 || tnSrv.cliSock == 0) return false; - char buff[256]; - int r = netRecv(net, ST_NORMAL, tnSrv.cliSock-1, buff, 256); + char buff[64]; + if (!jshHasEventSpaceForChars(sizeof(buff))) return false; + int r = netRecv(net, ST_NORMAL, tnSrv.cliSock-1, buff, sizeof(buff)); if (r > 0) { jshPushIOCharEvents(EV_TELNET, buff, (unsigned int)r); } else if (r < 0) { diff --git a/libs/network/wiznet/jswrap_wiznet.c b/libs/network/wiznet/jswrap_wiznet.c index a19bd868b..b7a5e2c00 100644 --- a/libs/network/wiznet/jswrap_wiznet.c +++ b/libs/network/wiznet/jswrap_wiznet.c @@ -25,11 +25,13 @@ #include "DHCP/dhcp.h" // -------------------- defaults... +#ifdef STM32 #define ETH_SPI EV_SPI3 #define ETH_CS_PIN (Pin)(JSH_PORTB_OFFSET + 2) // active low #define ETH_CLK_PIN (Pin)(JSH_PORTB_OFFSET + 3) #define ETH_MISO_PIN (Pin)(JSH_PORTB_OFFSET + 4) #define ETH_MOSI_PIN (Pin)(JSH_PORTB_OFFSET + 5) +#endif // ------------------------------- void wizchip_select(void) { @@ -88,6 +90,7 @@ JsVar *jswrap_wiznet_connect(JsVar *spi, Pin cs) { return 0; } } else { +#ifdef ETH_SPI // SPI config JshSPIInfo inf; jshSPIInitInfo(&inf); @@ -98,9 +101,15 @@ JsVar *jswrap_wiznet_connect(JsVar *spi, Pin cs) { inf.spiMode = SPIF_SPI_MODE_0; jshSPISetup(ETH_SPI, &inf); spiDevice = ETH_SPI; +#else + jsExceptionHere(JSET_ERROR, "No default SPI on this platform - you must specify one."); + return 0; +#endif } +#ifdef ETH_CS_PIN if (!jshIsPinValid(cs)) cs = ETH_CS_PIN; +#endif JsNetwork net; networkCreate(&net, JSNETWORKTYPE_W5500); @@ -159,15 +168,18 @@ An instantiation of an Ethernet network adaptor "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 *jswrap_ethernet_getIP(JsVar *wlanObj, JsVar *callback) { NOT_USED(wlanObj); if (networkState != NETWORKSTATE_ONLINE) { - jsError("Not connected to the internet"); + jsExceptionHere(JSET_ERROR, "Not connected to the internet"); return 0; } @@ -187,6 +199,16 @@ JsVar *jswrap_ethernet_getIP(JsVar *wlanObj) { 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; } @@ -207,7 +229,8 @@ static void _eth_getIP_set_address(JsVar *options, char *name, unsigned char *pt "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","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"] } @@ -215,18 +238,18 @@ Set the current IP address or get an IP from DHCP (if no options object is speci 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) { +bool jswrap_ethernet_setIP(JsVar *wlanObj, JsVar *options, JsVar *callback) { NOT_USED(wlanObj); if (networkState != NETWORKSTATE_ONLINE) { - jsError("Not connected to the internet"); + jsExceptionHere(JSET_ERROR, "Not connected to the internet"); return false; } JsNetwork net; if (!networkGetFromVar(&net)) return false; - bool success = false; + const char *errorMessage = 0; wiz_NetInfo gWIZNETINFO; ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO); @@ -259,16 +282,16 @@ bool jswrap_ethernet_setIP(JsVar *wlanObj, JsVar *options) { } gWIZNETINFO.dhcp = NETINFO_STATIC; - success = true; + errorMessage = 0; // all ok } else { // DHCP uint8_t DHCPisSuccess = getIP_DHCPS(net_wiznet_getFreeSocket(), &gWIZNETINFO); if (DHCPisSuccess == 1) { // info in lease_time.lVal - success = true; + errorMessage = 0; // all ok } else { - jsWarn("DHCP failed"); - success = false; + errorMessage = "DHCP failed"; + jsWarn(errorMessage); } } @@ -276,6 +299,14 @@ bool jswrap_ethernet_setIP(JsVar *wlanObj, JsVar *options) { networkFree(&net); - return success; + // 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; } diff --git a/libs/network/wiznet/jswrap_wiznet.h b/libs/network/wiznet/jswrap_wiznet.h index 81214a265..72620319d 100644 --- a/libs/network/wiznet/jswrap_wiznet.h +++ b/libs/network/wiznet/jswrap_wiznet.h @@ -16,5 +16,5 @@ JsVar *jswrap_wiznet_connect(JsVar *spi, Pin cs); -JsVar *jswrap_ethernet_getIP(JsVar *wlanObj); -bool jswrap_ethernet_setIP(JsVar *wlanObj, JsVar *options); +JsVar *jswrap_ethernet_getIP(JsVar *wlanObj, JsVar *callback); +bool jswrap_ethernet_setIP(JsVar *wlanObj, JsVar *options, JsVar *callback); diff --git a/libs/network/wiznet/network_wiznet.c b/libs/network/wiznet/network_wiznet.c index 9a901ea0d..0f441f1a1 100644 --- a/libs/network/wiznet/network_wiznet.c +++ b/libs/network/wiznet/network_wiznet.c @@ -105,12 +105,16 @@ int net_wiznet_createsocket(JsNetwork *net, SocketType socketType, uint32_t host /// destroys the given socket void net_wiznet_closesocket(JsNetwork *net, int sckt) { + uint8_t status; +#if _WIZCHIP_ == 5500 // try and close gracefully disconnect((uint8_t)sckt); JsSysTime timeout = jshGetSystemTime()+jshGetTimeFromMilliseconds(1000); - uint8_t status; while ((status=getSn_SR((uint8_t)sckt)) != SOCK_CLOSED && jshGetSystemTime()host,(uint16_t*)&header->port); + header->length = num; + if (num) num += sizeof(JsNetUDPPacketHeader); } else if (getSn_SR((uint8_t)sckt) == SOCK_LISTEN) { // socket is operating as a TCP server - something has gone wrong. // just return -1 to close this connection immediately @@ -186,12 +187,8 @@ int net_wiznet_recv(JsNetwork *net, SocketType socketType, int sckt, void *buf, int net_wiznet_send(JsNetwork *net, SocketType socketType, int sckt, const void *buf, size_t len) { int r; if (socketType & ST_UDP) { - size_t delta = sizeof(uint32_t) + sizeof(unsigned short) + sizeof(uint16_t); - uint32_t *host = (uint32_t*)buf; - unsigned short *port = (unsigned short*)&host[1]; - uint16_t *size = (uint16_t*)&port[1]; - - r = (int)sendto((uint8_t)sckt, buf + delta, *size, (uint8_t*)host, *port) + (int)delta; + JsNetUDPPacketHeader *header = (JsNetUDPPacketHeader*)buf; + r = (int)sendto((uint8_t)sckt, buf + sizeof(JsNetUDPPacketHeader), header->length, (uint8_t*)&header->host, header->port) + sizeof(JsNetUDPPacketHeader); } else { r = (int)send((uint8_t)sckt, buf, (uint16_t)len, MSG_NOSIGNAL); } diff --git a/libs/nordic_thingy/Thingy.js b/libs/nordic_thingy/Thingy.js deleted file mode 100644 index d03e70b60..000000000 --- a/libs/nordic_thingy/Thingy.js +++ /dev/null @@ -1,244 +0,0 @@ - -/* Copyright (c) 2018 Gordon Williams, Pur3 Ltd. See the file LICENSE for copying permission. */ -/* -Nordic Thingy Support Library - -TODO: - -Beep -MPU9250 support - the library at the moment only works for the magnetometer -Microphone support - will most likely need native support - -// Button -BTN - -// R/G/B leds -LED1/2/3 - -// MOSFET outputs -MOS1/2/3/4 - -// External IO outputs -IOEXT0/1/2/3 - -// Get repeated callbacks with {x,y,z}. Call with no argument to disable -Thingy.onAcceleration = function(callback) - -// Get one callback with a new acceleration value -Thingy.getAcceleration = function(callback) - -// Get repeated callbacks with {pressure,temperature}. Call with no argument to disable -Thingy.onPressure = function(callback) - -// Get one callback with a new {pressure,temperature} value -Thingy.getPressure = function(callback) - -// Get repeated callbacks with {humidity,temperature}. Call with no argument to disable -Thingy.onHumidity = function(callback) - -// Get one callback with a new {humidity,temperature} value -Thingy.getHumidity = function(callback) - -// Get repeated callbacks with air quality `{eC02,TVOC}`. Call with no argument to disable -Thingy.onGas = function(callback) - -//Get one callback with a new air quality value `{eC02,TVOC}`. This may not be useful as the sensor takes a while to warm up and produce useful values -Thingy.getGas = function(callback) - -// Get repeated callbacks with color `{r,g,b,c}`. Call with no argument to disable -Thingy.onColor = function(callback) - -// Get one callback with a new color value `{r,g,b,c}` -Thingy.getColor = function(callback) - -// Play a sound, supply a string/uint8array/arraybuffer, samples per second, and a callback to use when done -Thingy.sound = function(waveform, pitch, callback) - -*/ - - -var MPU_PWR_CTRL = V8; -var MPU_INT = D6; -var MIC_PWR_CTRL = V9; -var MIC_DOUT = D25; -var MIC_CLK = D26; -var CCS_PWR_CTRL = V10; -var CCS_RST = V11; -var CCS_WAKE = V12; -var CCS_INT = D22; -var LIS_INT = D12; -var SENSE_LEDR = V13; -var SENSE_LEDG = V14; -var SENSE_LEDB = V15; -var SENSE_LEDS = [SENSE_LEDR,SENSE_LEDG,SENSE_LEDB]; -var LPS_INT = D23; -var HTS_INT = D24; -var BH_INT = D31; -var BATTERY = D28; -var SPEAKER = D27; -var SPK_PWR_CTRL= D29; - -var i2c = new I2C(); -i2c.setup({sda:7,scl:8,bitrate:400000}); -var i2ce = new I2C(); -i2ce.setup({sda:14,scl:15,bitrate:400000}); - - -// ------------------------------------------------------------------------------------------- LIS2DH12 -// Get repeated callbacks with {x,y,z}. Call with no argument to disable -exports.onAcceleration = function(callback) { - if (callback) { - if (!this.accel) this.accel = require("LIS2DH12").connectI2C(i2ce/*, { int:LIS_INT } - not used */); - this.accel.callback = callback; - this.accel.setPowerMode("low"); - } else { - if (this.accel) this.accel.setPowerMode("powerdown"); - this.accel = undefined; - } -}; -// Get one callback with a new acceleration value -exports.getAcceleration = function(callback) { - if (!this.accel) { - require("LIS2DH12").connectI2C(i2ce/*, { int:LIS_INT } - not used */).readXYZ(callback); - } else { - this.accel.readXYZ(callback); - } -} - -// ------------------------------------------------------------------------------------------- LPS22HB -// Get repeated callbacks with {pressure,temperature}. Call with no argument to disable -exports.onPressure = function(callback) { - if (callback) { - if (!this.pressure) { - this.pressure = require("LPS22HB").connectI2C(i2c, {int:LPS_INT}); - this.pressure.on('data',function(d) { this.pressureCallback(d); }.bind(this)); - } - this.pressureCallback = callback; - } else { - if (this.pressure) this.pressure.stop(); - this.pressure = undefined; - this.pressureCallback = undefined; - } -}; -// Get one callback with a new {pressure,temperature} value -exports.getPressure = function(callback) { - if (!this.pressure) { - var p = require("LPS22HB").connectI2C(i2c, {int:LPS_INT}); - p.read(function(d) { - p.stop(); - callback(d); - }); - } else { - this.pressure.read(callback); - } -} -// ------------------------------------------------------------------------------------------- HTS221 -// Get repeated callbacks with {humidity,temperature}. Call with no argument to disable -exports.onHumidity = function(callback) { - if (callback) { - if (!this.humidity) { - this.humidity = require("HTS221").connect(i2c, {int:HTS_INT}); - this.humidity.on('data',function(d) { this.humidityCallback(d); }.bind(this)); - } - this.humidityCallback = callback; - } else { - if (this.humidity) this.humidity.stop(); - this.humidity = undefined; - this.humidityCallback = undefined; - } -}; -// Get one callback with a new {humidity,temperature} value -exports.getHumidity = function(callback) { - if (!this.humidity) { - this.onHumidity(function(d) { - this.onHumidity(); - callback(d); - }.bind(this)); - } else { - this.humidity.read(callback); - } -} -// ------------------------------------------------------------------------------------------- HTS221 -// Get repeated callbacks with air quality `{eC02,TVOC}`. Call with no argument to disable -exports.onGas = function(callback) { - if (callback) { - if (!this.gas) { - CCS_PWR_CTRL.set(); // CCS on - CCS_RST.set(); // no reset - CCS_WAKE.reset(); // wake - this.gas = require("CCS811").connectI2C(i2c, { int : CCS_INT }); - this.gas.on('data',function(d) { this.gasCallback(d); }.bind(this)); - } - this.gasCallback = callback; - } else { - if (this.gas) { - this.gas.stop(); - CCS_RST.reset(); // reset (so it doesn't power it via RST pin) - CCS_PWR_CTRL.reset(); // CCS off - } - this.gas = undefined; - this.gasCallback = undefined; - } -}; -/* Get one callback with a new air quality value `{eC02,TVOC}`. This may not be useful -as the sensor takes a while to warm up and produce useful values */ -exports.getGas = function(callback) { - if (!this.gas) { - this.onGas(function(d) { - this.onGas(); - callback(d); - }.bind(this)); - } else { - callback(this.gas.get()); - } -} -// ------------------------------------------------------------------------------------------- HTS221 -// Get repeated callbacks with color `{r,g,b,c}`. Call with no argument to disable -exports.onColor = function(callback) { - if (callback) { - if (!this.color) { - digitalWrite(SENSE_LEDS,0); // all LEDs on - this.color = require("BH1745").connectI2C(i2c/*, {int : BH_INT}*/); - this.colorInt = setInterval(function() { - this.colorCallback(this.color.read()); - }.bind(this), 200); - } - this.colorCallback = callback; - } else { - if (this.color) { - clearInterval(this.colorInt); - this.color.stop(); - digitalWrite(SENSE_LEDS,7); // all LEDs off - } - this.color = undefined; - this.colorInt = undefined; - this.colorCallback = undefined; - } -}; -// Get one callback with a new color value `{r,g,b,c}` -exports.getColor = function(callback) { - if (!this.color) { - this.onColor(function(d) { - this.onColor(); - callback(d); - }.bind(this)); - } else { - callback(this.color.read()); - } -} -// ------------------------------------------------------------------------------------------- Speaker -// Play a sound, supply a string/uint8array/arraybuffer, samples per second, and a callback to use when done -exports.sound = function(waveform, pitch, callback) { - if (exports.waveform) exports.waveform.stop(); - exports.waveform = new Waveform(waveform.length); - exports.waveform.buffer.set(waveform); - exports.waveform.on("finish", function(buf) { - SPK_PWR_CTRL.reset(); - digitalWrite(SPEAKER,0); - exports.waveform = undefined; - if (callback) callback(); - }); - analogWrite(SPEAKER, 0.5, {freq:40000}); - exports.waveform.startOutput(SPEAKER, pitch); - SPK_PWR_CTRL.set(); -}; diff --git a/libs/nordic_thingy/Thingy.min.js b/libs/nordic_thingy/Thingy.min.js deleted file mode 100644 index 88ac102d3..000000000 --- a/libs/nordic_thingy/Thingy.min.js +++ /dev/null @@ -1,6 +0,0 @@ -var f=V10,g=V11,m=V12,n=D22,h=[V13,V14,V15],k=D23,p=D24,d=D27,l=D29,b=new I2C;b.setup({sda:7,scl:8,bitrate:4E5});var e=new I2C;e.setup({sda:14,scl:15,bitrate:4E5});exports.onAcceleration=function(a){a?(this.accel||(this.accel=require("LIS2DH12").connectI2C(e)),this.accel.callback=a,this.accel.setPowerMode("low")):(this.accel&&this.accel.setPowerMode("powerdown"),this.accel=void 0)};exports.getAcceleration=function(a){this.accel?this.accel.readXYZ(a):require("LIS2DH12").connectI2C(e).readXYZ(a)}; -exports.onPressure=function(a){a?(this.pressure||(this.pressure=require("LPS22HB").connectI2C(b,{"int":k}),this.pressure.on("data",function(a){this.pressureCallback(a)}.bind(this))),this.pressureCallback=a):(this.pressure&&this.pressure.stop(),this.pressureCallback=this.pressure=void 0)};exports.getPressure=function(a){if(this.pressure)this.pressure.read(a);else{var c=require("LPS22HB").connectI2C(b,{"int":k});c.read(function(b){c.stop();a(b)})}};exports.onHumidity=function(a){a?(this.humidity||(this.humidity= -require("HTS221").connect(b,{"int":p}),this.humidity.on("data",function(a){this.humidityCallback(a)}.bind(this))),this.humidityCallback=a):(this.humidity&&this.humidity.stop(),this.humidityCallback=this.humidity=void 0)};exports.getHumidity=function(a){if(this.humidity)this.humidity.read(a);else this.onHumidity(function(c){this.onHumidity();a(c)}.bind(this))};exports.onGas=function(a){a?(this.gas||(f.set(),g.set(),m.reset(),this.gas=require("CCS811").connectI2C(b,{"int":n}),this.gas.on("data",function(a){this.gasCallback(a)}.bind(this))), -this.gasCallback=a):(this.gas&&(this.gas.stop(),g.reset(),f.reset()),this.gasCallback=this.gas=void 0)};exports.getGas=function(a){if(this.gas)a(this.gas.get());else this.onGas(function(c){this.onGas();a(c)}.bind(this))};exports.onColor=function(a){a?(this.color||(digitalWrite(h,0),this.color=require("BH1745").connectI2C(b),this.colorInt=setInterval(function(){this.colorCallback(this.color.read())}.bind(this),200)),this.colorCallback=a):(this.color&&(clearInterval(this.colorInt),this.color.stop(), -digitalWrite(h,7)),this.colorCallback=this.colorInt=this.color=void 0)};exports.getColor=function(a){if(this.color)a(this.color.read());else this.onColor(function(c){this.onColor();a(c)}.bind(this))};exports.sound=function(a,c,b){exports.waveform&&exports.waveform.stop();exports.waveform=new Waveform(a.length);exports.waveform.buffer.set(a);exports.waveform.on("finish",function(a){l.reset();digitalWrite(d,0);exports.waveform=void 0;b&&b()});analogWrite(d,.5,{freq:4E4});exports.waveform.startOutput(d, -c);l.set()} \ No newline at end of file diff --git a/libs/nordic_thingy/jswrap_thingy.c b/libs/nordic_thingy/jswrap_thingy.c index f64c02b9a..a71f6dda7 100644 --- a/libs/nordic_thingy/jswrap_thingy.c +++ b/libs/nordic_thingy/jswrap_thingy.c @@ -26,7 +26,7 @@ #define I2C_SDA 7 #define I2C_SCL 8 -// SX1509 IO Expader +// SX1509 IO Expander #define SX_I2C_ADDR 0x3e #define SX_RESET 16 #define SX_POWER 30 @@ -60,7 +60,7 @@ unsigned char sxReadReg(unsigned reg) { void jshVirtualPinInitialise() { jshPinOutput(SX_POWER, 1); // enable IO expander power - jshPinSetValue(i2cInfo.pinSDA, 1); + jshPinSetValue(i2cInfo.pinSCL, 1); jshPinSetState(i2cInfo.pinSCL, JSHPINSTATE_GPIO_OUT_OPENDRAIN_PULLUP); jshPinSetValue(i2cInfo.pinSDA, 1); jshPinSetState(i2cInfo.pinSDA, JSHPINSTATE_GPIO_OUT_OPENDRAIN_PULLUP); diff --git a/libs/nordic_thingy/update_js.sh b/libs/nordic_thingy/update_js.sh deleted file mode 100755 index aa9a318a2..000000000 --- a/libs/nordic_thingy/update_js.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -node ../../../EspruinoDocs/bin/minify.js Thingy.js Thingy.min.js -wget https://www.espruino.com/modules/LIS2DH12.min.js -O LIS2DH12.min.js -wget https://www.espruino.com/modules/LPS22HB.min.js -O LPS22HB.min.js -wget https://www.espruino.com/modules/HTS221.min.js -O HTS221.min.js -wget https://www.espruino.com/modules/CCS811.min.js -O CCS811.min.js -wget https://www.espruino.com/modules/BH1745.min.js -O BH1745.min.js - diff --git a/libs/pixljs/jswrap_pixljs.c b/libs/pixljs/jswrap_pixljs.c index 04ee1eb59..3c6b472e5 100644 --- a/libs/pixljs/jswrap_pixljs.c +++ b/libs/pixljs/jswrap_pixljs.c @@ -27,6 +27,7 @@ #include "nrf_delay.h" #include "nrf5x_utils.h" #include "jsflash.h" // for jsfRemoveCodeFromFlash +#include "bluetooth.h" // for self-test #include "jswrap_graphics.h" #include "lcd_arraybuffer.h" @@ -44,21 +45,161 @@ Class containing utility functions for [Pixl.js](http://www.espruino.com/Pixl.js "type" : "staticmethod", "class" : "Pixl", "name" : "getBatteryPercentage", - "generate" : "jswrap_pixljs_getBatteryPercentage", + "generate" : "jswrap_espruino_getBattery", "return" : ["int", "A percentage between 0 and 100" ] } +DEPRECATED - Please use `E.getBattery()` instead. + Return an approximate battery percentage remaining based on a normal CR2032 battery (2.8 - 2.2v) */ -int jswrap_pixljs_getBatteryPercentage() { - JsVarFloat v = jswrap_nrf_bluetooth_getBattery(); - int pc = (v-2.2)*100/0.6; - if (pc>100) pc=100; - if (pc<0) pc=0; - return pc; + +/*JSON{ + "type" : "staticmethod", + "class" : "Pixl", + "name" : "menu", + "generate" : "jswrap_pixljs_menu", + "params" : [ + ["menu","JsVar","An object containing name->function mappings to to be used in a menu"] + ], + "return" : ["JsVar", "A menu object with `draw`, `move` and `select` functions" ] +} +Display a menu on Pixl.js's screen, and set up the buttons to navigate through it. + +Supply an object containing menu items. When an item is selected, the +function it references will be executed. For example: + +``` +var boolean = false; +var number = 50; +// First menu +var mainmenu = { + "" : { + "title" : "-- Main Menu --" + }, + "Backlight On" : function() { LED1.set(); }, + "Backlight Off" : function() { LED1.reset(); }, + "Submenu" : function() { Pixl.menu(submenu); }, + "A Boolean" : { + value : boolean, + format : v => v?"On":"Off", + onchange : v => { boolean=v; } + }, + "A Number" : { + value : number, + min:0,max:100,step:10, + onchange : v => { number=v; } + } + "Exit" : function() { Pixl.menu(); }, +}; +// Submenu +var submenu = { + "" : { + "title" : "-- SubMenu --" + }, + "One" : undefined, // do nothing + "Two" : undefined, // do nothing + "< Back" : function() { Pixl.menu(mainmenu); }, +}; +// Actually display the menu +Pixl.menu(mainmenu); +``` + +See http://www.espruino.com/graphical_menu for more detailed information. +*/ +JsVar *jswrap_pixljs_menu(JsVar *menu) { + /* Unminified JS code is: + +Pixl.show = function(menudata) { + if (Pixl.btnWatches) { + Pixl.btnWatches.forEach(clearWatch); + Pixl.btnWatches = undefined; + } + g.clear();g.flip(); // clear screen if no menu supplied + if (!menudata) return; + function im(b) { + return { + width:8,height:b.length,bpp:1,buffer:new Uint8Array(b).buffer + }; + } + if (!menudata[""]) menudata[""]={}; + g.setFontBitmap();g.setFontAlign(-1,-1,0); + var w = g.getWidth()-9; + var h = g.getHeight(); + menudata[""].x=9; + menudata[""].x2=w-2; + menudata[""].preflip=function() { + g.drawImage(im([ + 0b00010000, + 0b00111000, + 0b01111100, + 0b11111110, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + ]),0,4); + g.drawImage(im([ + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b11111110, + 0b01111100, + 0b00111000, + 0b00010000, + ]),0,h-12); + g.drawImage(im([ + 0b00000000, + 0b00001000, + 0b00001100, + 0b00001110, + 0b11111111, + 0b00001110, + 0b00001100, + 0b00001000, + ]),w+1,h-12); + //g.drawLine(7,0,7,h); + //g.drawLine(w,0,w,h); + }; + var m = require("graphical_menu").list(g, menudata); + Pixl.btnWatches = [ + setWatch(function() { m.move(-1); }, BTN1, {repeat:1}), + setWatch(function() { m.move(1); }, BTN4, {repeat:1}), + setWatch(function() { m.select(); }, BTN3, {repeat:1}) + ]; + return m; +}; +*/ + + return jspExecuteJSFunction("(function(a){function c(a){return{width:8,height:a.length,bpp:1,buffer:(new Uint8Array(a)).buffer}}Pixl.btnWatches&&(Pixl.btnWatches.forEach(clearWatch),Pixl.btnWatches=void 0);" + "g.clear();g.flip();" + "if(a){a['']||(a['']={});g.setFontBitmap();g.setFontAlign(-1,-1,0);var d=g.getWidth()-9,e=g.getHeight();a[''].x=9;a[''].x2=d-2;a[''].preflip=function(){" + "g.drawImage(c([16,56,124,254,16,16,16,16]),0,4);g.drawImage(c([16,16,16,16,254,124,56,16]),0,e-12);g.drawImage(c([0,8,12,14,255,14,12,8]),d+1,e-12)};" + "var b=require('graphical_menu').list(g,a);Pixl.btnWatches=[setWatch(function(){b.move(-1)},BTN1,{repeat:1}),setWatch(function(){b.move(1)},BTN4,{repeat:1})," + "setWatch(function(){b.select()},BTN3,{repeat:1})];return b}})",0,1,&menu); } +/*JSON{ + "type" : "variable", + "name" : "SDA", + "generate_full" : "4", + "ifdef" : "PIXLJS", + "return" : ["pin",""] +} +The pin marked SDA on the Arduino pin footprint. This is connected directly to pin A4. +*/ +/*JSON{ + "type" : "variable", + "name" : "SCL", + "generate_full" : "5", + "ifdef" : "PIXLJS", + "return" : ["pin",""] +} +The pin marked SDA on the Arduino pin footprint. This is connected directly to pin A5. +*/ + void lcd_wr(int data) { int bit; @@ -119,8 +260,16 @@ void lcd_flip_gfx(JsGraphics *gfx) { } -void lcd_flip(JsVar *parent) { - JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return; +/// Send buffer contents to the screen. Usually only the modified data will be output, but if all=true then the whole screen contents is sent +void lcd_flip(JsVar *parent, bool all) { + JsGraphics gfx; + if (!graphicsGetFromVar(&gfx, parent)) return; + if (all) { + gfx.data.modMinX = 0; + gfx.data.modMinY = 0; + gfx.data.modMaxX = 127; + gfx.data.modMaxY = 63; + } lcd_flip_gfx(&gfx); graphicsSetVar(&gfx); } @@ -147,6 +296,33 @@ void jswrap_pixljs_setContrast(JsVarFloat c) { jshPinSetValue(LCD_SPI_CS,1); } +/*JSON{ + "type" : "staticmethod", + "class" : "Pixl", + "name" : "setLCDPower", + "generate" : "jswrap_pixljs_setLCDPower", + "params" : [ + ["isOn","bool","True if the LCD should be on, false if not"] + ] +} +This function can be used to turn Pixl.js's LCD off or on. + +* With the LCD off, Pixl.js draws around 0.1mA +* With the LCD on, Pixl.js draws around 0.25mA +*/ +void jswrap_pixljs_setLCDPower(bool isOn) { + jshPinSetValue(LCD_SPI_CS,0); + jshPinSetValue(LCD_SPI_DC,0); + if (isOn) { + lcd_wr(0xA4); // cancel pixel on + lcd_wr(0xAF); // display on + } else { + lcd_wr(0xAE); // display off + lcd_wr(0xA5); // all pixels on + } + jshPinSetValue(LCD_SPI_CS,1); +} + /*JSON{ "type" : "staticmethod", "class" : "Pixl", @@ -210,10 +386,19 @@ static bool pixl_selfTest() { jshPinOutput(LED1_PININDEX, LED1_ONSTATE); jshPinSetState(BTN1_PININDEX, BTN1_PINSTATE); + v = jshReadVRef(); + if (v<3.2 || v>3.4) { + jsiConsolePrintf("VCC out of range 3.2-3.4 (%f)\n", v); + ok = false; + } + if (jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE) jsiConsolePrintf("Release BTN1\n"); + if (jshPinGetValue(BTN4_PININDEX)==BTN4_ONSTATE) + jsiConsolePrintf("Release BTN4\n"); timeout = 2000; - while (jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE && timeout--) + while ((jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE || + jshPinGetValue(BTN4_PININDEX)==BTN4_ONSTATE) && timeout--) nrf_delay_ms(1); if (jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE) { jsiConsolePrintf("BTN1 stuck down\n"); @@ -251,6 +436,24 @@ static bool pixl_selfTest() { for (i=0;i1) f=1; // turn the red LED back on if it was on before @@ -536,22 +543,16 @@ JsVarFloat jswrap_puck_light() { /*JSON{ "type" : "staticmethod", "class" : "Puck", + "ifdef" : "PUCKJS", "name" : "getBatteryPercentage", - "generate" : "jswrap_puck_getBatteryPercentage", + "generate" : "jswrap_espruino_getBattery", "return" : ["int", "A percentage between 0 and 100" ] } +DEPRECATED - Please use `E.getBattery()` instead. + Return an approximate battery percentage remaining based on -a normal CR2032 battery (2.8 - 2.2v) +a normal CR2032 battery (2.8 - 2.2v). */ -int jswrap_puck_getBatteryPercentage() { - JsVarFloat v = jswrap_nrf_bluetooth_getBattery(); - int pc = (v-2.2)*100/0.6; - if (pc>100) pc=100; - if (pc<0) pc=0; - return pc; -} - - static bool selftest_check_pin(Pin pin) { @@ -594,6 +595,7 @@ static bool selftest_check_pin(Pin pin) { "type" : "staticmethod", "class" : "Puck", "name" : "selfTest", + "ifdef" : "PUCKJS", "generate" : "jswrap_puck_selfTest", "return" : ["bool", "True if the self-test passed" ] } diff --git a/libs/puckjs/jswrap_puck.h b/libs/puckjs/jswrap_puck.h index 3bfb1881c..2a7ddace8 100644 --- a/libs/puckjs/jswrap_puck.h +++ b/libs/puckjs/jswrap_puck.h @@ -20,7 +20,6 @@ JsVarInt jswrap_puck_magTemp(); void jswrap_puck_IR(JsVar *data, Pin cathode, Pin anode); int jswrap_puck_capSense(Pin tx, Pin rx); JsVarFloat jswrap_puck_light(); -int jswrap_puck_getBatteryPercentage(); bool jswrap_puck_selfTest(); void jswrap_puck_init(); diff --git a/libs/tv/tv.h b/libs/tv/tv.h index 058235c79..0df6993eb 100644 --- a/libs/tv/tv.h +++ b/libs/tv/tv.h @@ -15,17 +15,17 @@ typedef struct { Pin pinVideo; Pin pinSync; - int width; - int height; + int width; // (must be int because of jsvReadConfigObject) + int height; // (must be int because of jsvReadConfigObject) } tv_info_pal; typedef struct { Pin pinVideo; Pin pinSync; Pin pinSyncV; - int width; - int height; - int lineRepeat; + int width; // (must be int because of jsvReadConfigObject) + int height; // (must be int because of jsvReadConfigObject) + int lineRepeat; // (must be int because of jsvReadConfigObject) } tv_info_vga; void tv_info_pal_init(tv_info_pal *inf); diff --git a/make/common/NRF5X.make b/make/common/NRF5X.make index 5c1fa25ed..a20b626c0 100644 --- a/make/common/NRF5X.make +++ b/make/common/NRF5X.make @@ -27,10 +27,15 @@ ARM = 1 ARM_HAS_OWN_CMSIS = 1 # Nordic uses its own CMSIS files in its SDK, these are up-to-date. INCLUDE += -I$(NRF5X_SDK_PATH) +# This is where the common linker for both nRF51 & nRF52 is stored. ifdef NRF5X_SDK_12 -LDFLAGS += -L$(NRF5X_SDK_PATH)/nrf5x_linkers # This is where the common linker for both nRF51 & nRF52 is stored. -else -LDFLAGS += -L$(NRF5X_SDK_PATH)/components/toolchain/gcc # This is where the common linker for both nRF51 & nRF52 is stored. +LDFLAGS += -L$(NRF5X_SDK_PATH)/nrf5x_linkers +endif +ifdef NRF5X_SDK_14 +LDFLAGS += -L$(NRF5X_SDK_PATH)/components/toolchain/gcc +endif +ifdef NRF5X_SDK_15 +LDFLAGS += -L$(NRF5X_SDK_PATH)/modules/nrfx/mdk endif # These files are the Espruino HAL implementation. @@ -103,8 +108,36 @@ INCLUDE += -I$(NRF5X_SDK_PATH)/components/libraries/strerror INCLUDE += -I$(NRF5X_SDK_PATH)/components/libraries/experimental_memobj INCLUDE += -I$(NRF5X_SDK_PATH)/components/libraries/balloc endif +ifdef NRF5X_SDK_15 +INCLUDE += -I$(NRF5X_SDK_PATH)/modules/nrfx +INCLUDE += -I$(NRF5X_SDK_PATH)/modules/nrfx/mdk +INCLUDE += -I$(NRF5X_SDK_PATH)/modules/nrfx/hal +INCLUDE += -I$(NRF5X_SDK_PATH)/modules/nrfx/legacy +INCLUDE += -I$(NRF5X_SDK_PATH)/modules/nrfx/drivers/include +INCLUDE += -I$(NRF5X_SDK_PATH)/integration/nrfx +INCLUDE += -I$(NRF5X_SDK_PATH)/integration/nrfx/legacy +INCLUDE += -I$(NRF5X_SDK_PATH)/components/libraries/delay +INCLUDE += -I$(NRF5X_SDK_PATH)/components/ble/ble_link_ctx_manager +INCLUDE += -I$(NRF5X_SDK_PATH)/components/libraries/atomic_flags +endif +ifdef NRF5X_SDK_15 +TARGETSOURCES += \ +$(NRF5X_SDK_PATH)/modules/nrfx/drivers/src/nrfx_gpiote.c \ +$(NRF5X_SDK_PATH)/modules/nrfx/drivers/src/nrfx_spi.c \ +$(NRF5X_SDK_PATH)/modules/nrfx/drivers/src/nrfx_twi.c \ +$(NRF5X_SDK_PATH)/modules/nrfx/drivers/src/nrfx_uart.c \ +$(NRF5X_SDK_PATH)/integration/nrfx/legacy/nrf_drv_ppi.c \ +$(NRF5X_SDK_PATH)/integration/nrfx/legacy/nrf_drv_rng.c \ +$(NRF5X_SDK_PATH)/integration/nrfx/legacy/nrf_drv_twi.c \ +$(NRF5X_SDK_PATH)/integration/nrfx/legacy/nrf_drv_uart.c \ +$(NRF5X_SDK_PATH)/integration/nrfx/legacy/nrf_drv_spi.c \ +$(NRF5X_SDK_PATH)/components/libraries/atomic/nrf_atomic.c \ +$(NRF5X_SDK_PATH)/components/libraries/atomic_flags/nrf_atflags.c \ +$(NRF5X_SDK_PATH)/components/ble/ble_link_ctx_manager/ble_link_ctx_manager.c \ +$(NRF5X_SDK_PATH)/components/libraries/util/app_error_handler_gcc.c +else TARGETSOURCES += \ $(NRF5X_SDK_PATH)/components/drivers_nrf/common/nrf_drv_common.c \ $(NRF5X_SDK_PATH)/components/drivers_nrf/gpiote/nrf_drv_gpiote.c \ @@ -113,7 +146,10 @@ $(NRF5X_SDK_PATH)/components/drivers_nrf/hal/nrf_nvmc.c \ $(NRF5X_SDK_PATH)/components/drivers_nrf/twi_master/nrf_drv_twi.c \ $(NRF5X_SDK_PATH)/components/drivers_nrf/spi_master/nrf_drv_spi.c \ $(NRF5X_SDK_PATH)/components/drivers_nrf/ppi/nrf_drv_ppi.c \ -$(NRF5X_SDK_PATH)/components/drivers_nrf/clock/nrf_drv_clock.c \ +$(NRF5X_SDK_PATH)/components/drivers_nrf/clock/nrf_drv_clock.c +endif + +TARGETSOURCES += \ $(NRF5X_SDK_PATH)/components/ble/common/ble_advdata.c \ $(NRF5X_SDK_PATH)/components/ble/common/ble_conn_params.c \ $(NRF5X_SDK_PATH)/components/ble/common/ble_srv_common.c \ @@ -148,7 +184,6 @@ else TARGETSOURCES += \ $(NRF5X_SDK_PATH)/components/softdevice/common/nrf_sdh.c \ $(NRF5X_SDK_PATH)/components/softdevice/common/nrf_sdh_ble.c \ -$(NRF5X_SDK_PATH)/components/drivers_nrf/hal/nrf_saadc.c \ $(NRF5X_SDK_PATH)/components/libraries/experimental_section_vars/nrf_section_iter.c \ $(NRF5X_SDK_PATH)/components/libraries/fstorage/nrf_fstorage.c \ $(NRF5X_SDK_PATH)/components/libraries/fstorage/nrf_fstorage_sd.c \ @@ -160,6 +195,7 @@ $(NRF5X_SDK_PATH)/components/libraries/balloc/nrf_balloc.c endif + # $(NRF5X_SDK_PATH)/components/libraries/util/nrf_log.c ifdef USE_BOOTLOADER @@ -221,6 +257,7 @@ endif include make/common/ARM.make $(PROJ_NAME).app_hex: $(PROJ_NAME).elf + python scripts/check_elf_size.py $(BOARD) $(PROJ_NAME).elf @echo $(call $(quiet_)obj_to_bin,ihex,hex) @$(call obj_to_bin,ihex,hex) @mv $(PROJ_NAME).hex $(PROJ_NAME).app_hex diff --git a/make/crypto/ESP32.make b/make/crypto/ESP32.make new file mode 100644 index 000000000..8b3d9f1c4 --- /dev/null +++ b/make/crypto/ESP32.make @@ -0,0 +1,28 @@ +DEFINES += -DUSE_CRYPTO + +INCLUDE += -I$(ROOT)/libs/crypto +INCLUDE += -I$(ESP_IDF_PATH)/components/mbedtls +INCLUDE += -I$(ESP_IDF_PATH)/components/mbedtls/mbedtls/include + +DEFINES += -DMBEDTLS_CIPHER_MODE_CTR \ +-DMBEDTLS_CIPHER_MODE_CBC \ +-DMBEDTLS_CIPHER_MODE_CFB + +WRAPPERSOURCES += libs/crypto/jswrap_crypto.c + +ifdef USE_SHA256 + DEFINES += -DUSE_SHA256 +endif + +ifdef USE_SHA512 + DEFINES += -DUSE_SHA512 +endif + +ifdef USE_TLS + USE_AES=1 + DEFINES += -DUSE_TLS +endif + +ifdef USE_AES + DEFINES += -DUSE_AES +endif diff --git a/make/crypto/default.make b/make/crypto/default.make new file mode 100644 index 000000000..022bf44c9 --- /dev/null +++ b/make/crypto/default.make @@ -0,0 +1,50 @@ + DEFINES += -DUSE_CRYPTO + INCLUDE += -I$(ROOT)/libs/crypto + INCLUDE += -I$(ROOT)/libs/crypto/mbedtls + INCLUDE += -I$(ROOT)/libs/crypto/mbedtls/include + WRAPPERSOURCES += libs/crypto/jswrap_crypto.c + SOURCES += libs/crypto/mbedtls/library/sha1.c +ifdef USE_SHA256 + DEFINES += -DUSE_SHA256 + SOURCES += libs/crypto/mbedtls/library/sha256.c +endif +ifdef USE_SHA512 + DEFINES += -DUSE_SHA512 + SOURCES += libs/crypto/mbedtls/library/sha512.c +endif + +ifdef USE_TLS + USE_AES=1 + DEFINES += -DUSE_TLS + SOURCES += \ +libs/crypto/mbedtls/library/bignum.c \ +libs/crypto/mbedtls/library/ctr_drbg.c \ +libs/crypto/mbedtls/library/debug.c \ +libs/crypto/mbedtls/library/ecp.c \ +libs/crypto/mbedtls/library/ecp_curves.c \ +libs/crypto/mbedtls/library/entropy.c \ +libs/crypto/mbedtls/library/entropy_poll.c \ +libs/crypto/mbedtls/library/md5.c \ +libs/crypto/mbedtls/library/pk.c \ +libs/crypto/mbedtls/library/pkparse.c \ +libs/crypto/mbedtls/library/pk_wrap.c \ +libs/crypto/mbedtls/library/rsa.c \ +libs/crypto/mbedtls/library/ssl_ciphersuites.c \ +libs/crypto/mbedtls/library/ssl_cli.c \ +libs/crypto/mbedtls/library/ssl_tls.c \ +libs/crypto/mbedtls/library/ssl_srv.c \ +libs/crypto/mbedtls/library/x509.c \ +libs/crypto/mbedtls/library/x509_crt.c +endif +ifdef USE_AES + DEFINES += -DUSE_AES + SOURCES += \ +libs/crypto/mbedtls/library/aes.c \ +libs/crypto/mbedtls/library/asn1parse.c \ +libs/crypto/mbedtls/library/cipher.c \ +libs/crypto/mbedtls/library/cipher_wrap.c \ +libs/crypto/mbedtls/library/md.c \ +libs/crypto/mbedtls/library/md_wrap.c \ +libs/crypto/mbedtls/library/oid.c \ +libs/crypto/mbedtls/library/pkcs5.c +endif diff --git a/make/family/ESP32.make b/make/family/ESP32.make index a1f07b03b..26da7fafd 100755 --- a/make/family/ESP32.make +++ b/make/family/ESP32.make @@ -6,7 +6,7 @@ ESP32=1 CFLAGS+=-Og -Wpointer-arith -Wno-error=unused-function -Wno-error=unused-but-set-variable \ -Wno-error=unused-variable -Wall -ffunction-sections -fdata-sections -mlongcalls -nostdlib \ --MMD -MP -std=gnu99 -fstrict-volatile-bitfields -fgnu89-inline +-MMD -MP -std=gnu99 -fstrict-volatile-bitfields -fgnu89-inline -mfix-esp32-psram-cache-issue SOURCES += targets/esp32/jshardware.c SOURCES += targets/esp32/esp32_neopixel.c INCLUDE += -I$(ROOT)/targets/esp32 @@ -23,60 +23,74 @@ SOURCES += targets/esp32/main.c LDFLAGS += -L$(ESP_IDF_PATH)/ld \ -L$(ESP_IDF_PATH)/components/bt/lib \ -L$(ESP_IDF_PATH)/components/esp32/lib \ +-L$(ESP_APP_TEMPLATE_PATH)/build/app_update \ -L$(ESP_APP_TEMPLATE_PATH)/build/bootloader \ -L$(ESP_APP_TEMPLATE_PATH)/build/bt \ -L$(ESP_APP_TEMPLATE_PATH)/build/driver \ -L$(ESP_APP_TEMPLATE_PATH)/build/esp32 \ -L$(ESP_APP_TEMPLATE_PATH)/build/esptool_py \ +-L$(ESP_APP_TEMPLATE_PATH)/build/ethernet \ -L$(ESP_APP_TEMPLATE_PATH)/build/expat \ -L$(ESP_APP_TEMPLATE_PATH)/build/freertos \ +-L$(ESP_APP_TEMPLATE_PATH)/build/heap \ -L$(ESP_APP_TEMPLATE_PATH)/build/json \ -L$(ESP_APP_TEMPLATE_PATH)/build/log \ -L$(ESP_APP_TEMPLATE_PATH)/build/lwip \ -L$(ESP_APP_TEMPLATE_PATH)/build/mbedtls \ +-L$(ESP_APP_TEMPLATE_PATH)/build/mdns \ -L$(ESP_APP_TEMPLATE_PATH)/build/newlib \ -L$(ESP_APP_TEMPLATE_PATH)/build/nghttp \ -L$(ESP_APP_TEMPLATE_PATH)/build/nvs_flash \ -L$(ESP_APP_TEMPLATE_PATH)/build/partition_table \ +-L$(ESP_APP_TEMPLATE_PATH)/build/pthread \ +-L$(ESP_APP_TEMPLATE_PATH)/build/smartconfig_ack \ -L$(ESP_APP_TEMPLATE_PATH)/build/soc \ -L$(ESP_APP_TEMPLATE_PATH)/build/spi_flash \ -L$(ESP_APP_TEMPLATE_PATH)/build/tcpip_adapter \ -L$(ESP_APP_TEMPLATE_PATH)/build/vfs \ --L$(ESP_APP_TEMPLATE_PATH)/build/newlib \ -L$(ESP_APP_TEMPLATE_PATH)/build/wpa_supplicant \ --L$(ESP_APP_TEMPLATE_PATH)/build/ethernet \ --L$(ESP_APP_TEMPLATE_PATH)/build/app_update \ +-L$(ESP_IDF_PATH)/components/esp32/ld \ -lgcc ESPTOOL?= INCLUDE+=\ -I$(ESP_APP_TEMPLATE_PATH)/build/include \ -I$(ESP_IDF_PATH)/components \ -I$(ESP_IDF_PATH)/components/newlib/include \ +-I$(ESP_IDF_PATH)/components/pthread/include \ -I$(ESP_IDF_PATH)/components/bt/include \ -I$(ESP_IDF_PATH)/components/driver/include \ -I$(ESP_IDF_PATH)/components/esp32/include \ -I$(ESP_IDF_PATH)/components/freertos/include \ +-I$(ESP_IDF_PATH)/components/heap/include \ -I$(ESP_IDF_PATH)/components/json/include \ -I$(ESP_IDF_PATH)/components/log/include \ -I$(ESP_IDF_PATH)/components/lwip/include/lwip \ -I$(ESP_IDF_PATH)/components/lwip/include/lwip/port \ -I$(ESP_IDF_PATH)/components/lwip/include/lwip/posix \ -I$(ESP_IDF_PATH)/components/newlib/include \ --I$(ESP_IDF_PATH)/components/spi_flash/include \ +-I$(ESP_IDF_PATH)/components/newlib/platform_include \ -I$(ESP_IDF_PATH)/components/nvs_flash/include \ +-I$(ESP_IDF_PATH)/components/spi_flash/include \ -I$(ESP_IDF_PATH)/components/tcpip_adapter/include \ --I$(ESP_IDF_PATH)/components/vfs/include \ +-I$(ESP_IDF_PATH)/components/soc/include \ -I$(ESP_IDF_PATH)/components/soc/esp32/include \ -I$(ESP_IDF_PATH)/components/soc/esp32/include/soc \ +-I$(ESP_IDF_PATH)/components/vfs/include \ -Itargets/esp32/include -LDFLAGS+=-nostdlib -u call_user_start_cpu0 -Wl,--gc-sections -Wl,-static -Wl,-EL +LDFLAGS+=-nostdlib -u call_user_start_cpu0 -u ld_include_panic_highint_hdl -Wl,--gc-sections -Wl,-static -Wl,-EL LIBS+=-T esp32_out.ld \ -T$(ESP_IDF_PATH)/components/esp32/ld/esp32.common.ld \ -T$(ESP_IDF_PATH)/components/esp32/ld/esp32.rom.ld \ -T$(ESP_IDF_PATH)/components/esp32/ld/esp32.peripherals.ld \ $(ESP_IDF_PATH)/components/esp32/lib/librtc.a \ +$(ESP_IDF_PATH)/components/esp32/lib/libnet80211.a \ $(ESP_IDF_PATH)/components/newlib/lib/libc.a \ $(ESP_IDF_PATH)/components/newlib/lib/libm.a \ +$(ESP_IDF_PATH)/components/esp32/lib/libwpa2.a \ +$(ESP_IDF_PATH)/components/esp32/lib/libwps.a \ +$(ESP_IDF_PATH)/components/newlib/lib/libc-psram-workaround.a \ +$(ESP_IDF_PATH)/components/newlib/lib/libm-psram-workaround.a \ +$(ESP_IDF_PATH)/components/esp32/lib/libcore.a \ -lbt \ -lbtdm_app \ -ldriver \ @@ -95,8 +109,13 @@ $(ESP_IDF_PATH)/components/esp32/libhal.a \ -llog \ -llwip \ -lmbedtls \ +-lmdns \ +-lmesh \ -lnghttp \ -lnvs_flash \ +-lheap \ +-lpthread \ +-lsmartconfig_ack \ -lsoc \ -lspi_flash \ -ltcpip_adapter \ @@ -107,3 +126,25 @@ $(ESP_IDF_PATH)/components/esp32/libhal.a \ -lapp_update \ -lstdc++ \ -lgcc + +#needed for using ifdef in wrapper JSON +DEFINES += -DESP32 + +ifdef USE_BLUETOOTH +SOURCES+= targets/esp32/bluetooth.c \ +targets/esp32/BLE/esp32_bluetooth_utils.c \ +targets/esp32/BLE/esp32_gap_func.c \ +targets/esp32/BLE/esp32_gatts_func.c \ +targets/esp32/BLE/esp32_gattc_func.c \ +targets/esp32/jshardwareESP32.c +INCLUDE+= -I$(ESP_IDF_PATH)/components/bt/bluedroid/include \ +-I$(ESP_IDF_PATH)/components/bt/bluedroid/api/include \ +-I$(ESP_IDF_PATH)/components/bt/bluedroid/bta/include \ +-I$(ESP_IDF_PATH)/components/bt/bluedroid/api/include/api \ +-I$(ESP_IDF_PATH)/components/bt/bluedroid/stack/include \ +-I$(ESP_IDF_PATH)/components/bt/bluedroid/stack/gatt/include \ +-I$(ESP_IDF_PATH)/components/bt/bluedroid/osi/include +LDFLAGS+= -L$(ESP_APP_TEMPLATE_PATH)/build/components/bt/bluedroid/api \ +-L$(ESP_APP_TEMPLATE_PATH)/build/components/bt/bluedroid/bta +endif + diff --git a/make/family/ESP8266.make b/make/family/ESP8266.make index 0a970825e..7465df1d2 100644 --- a/make/family/ESP8266.make +++ b/make/family/ESP8266.make @@ -13,9 +13,9 @@ else OPTIMIZEFLAGS+=-Os -std=gnu11 -fgnu89-inline -Wl,--allow-multiple-definition endif - +ET_FM ?= qio # Valid values are keep, qio, qout, dio, dout ifdef FLASH_4MB -ESP_FLASH_MAX ?= 962560 # max bin file: 940KB +ESP_FLASH_MAX ?= 831488 # max bin file: 940KB ESP_FLASH_SIZE ?= 6 # 6->4MB (1024KB+1024KB) ESP_FLASH_MODE ?= 0 # 0->QIO, 2->DIO ESP_FLASH_FREQ_DIV ?= 15 # 15->80Mhz @@ -63,7 +63,8 @@ CFLAGS+= -fno-builtin \ -Wno-maybe-uninitialized -Wno-old-style-declaration -Wno-conversion -Wno-unused-variable \ -Wno-unused-parameter -Wno-ignored-qualifiers -Wno-discarded-qualifiers -Wno-float-conversion \ -Wno-parentheses -Wno-type-limits -Wno-unused-function -Wno-unused-value \ --Wl,EL -Wl,--gc-sections -nostdlib -mlongcalls -mtext-section-literals +-Wl,EL -Wl,--gc-sections -nostdlib -mlongcalls -mtext-section-literals \ +-fno-guess-branch-probability -freorder-blocks-and-partition -fno-cse-follow-jumps # only use mfore-l32 if 4MB board for now ifdef FLASH_4MB @@ -96,10 +97,13 @@ SOURCES += targets/esp8266/uart.c \ targets/esp8266/jshardware.c \ targets/esp8266/i2c_master.c \ targets/esp8266/esp8266_board_utils.c \ - targets/esp8266/gdbstub.c \ - targets/esp8266/gdbstub-entry.S \ libs/network/esp8266/network_esp8266.c +ifdef DEBUG +SOURCES += targets/esp8266/gdbstub.c \ + targets/esp8266/gdbstub-entry.S +endif + # The tool used for building the firmware and flashing ESPTOOL ?= $(ESP8266_SDK_ROOT)/esptool/esptool.py diff --git a/make/family/LINUX.make b/make/family/LINUX.make index 9da565044..7cb493e48 100644 --- a/make/family/LINUX.make +++ b/make/family/LINUX.make @@ -10,17 +10,22 @@ export CCPREFIX=$(STAGING_DIR)/mips-openwrt-linux- endif ifeq ($(BOARD),RASPBERRYPI) - ifneq ($(shell uname -m),armv6l) + ifeq ($(shell grep Raspbian /etc/os-release),) + # Not on a Pi at the moment $(info *********************************) $(info * CROSS COMPILING *) $(info *********************************) export CCPREFIX=targetlibs/raspberrypi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf- else + $(info *********************************) + $(info * COMPILING ON PI *) + $(info *********************************) # compiling in-place, so give it a normal name PROJ_NAME=espruino endif endif +CFLAGS += -std=gnu99 DEFINES += -DLINUX INCLUDE += -I$(ROOT)/targets/linux SOURCES += \ diff --git a/make/family/NRF52.make b/make/family/NRF52.make index e03518581..9d04d830a 100644 --- a/make/family/NRF52.make +++ b/make/family/NRF52.make @@ -1,30 +1,54 @@ NRF5X=1 +ifdef NRF_SDK15 +# Use SDK15 +NRF5X_SDK=15 +NRF5X_SDK_15=1 +NRF5X_SDK_PATH=$(ROOT)/targetlibs/nrf5x_15 +DEFINES += -DNRF_SD_BLE_API_VERSION=6 +DEFINES += -D__HEAP_SIZE=0 +SOFTDEVICE_PATH = $(NRF5X_SDK_PATH)/components/softdevice/s132 +SOFTDEVICE = $(SOFTDEVICE_PATH)/hex/s132_nrf52_6.0.0_softdevice.hex +DEFINES += -DS132 + +TARGETSOURCES += $(NRF5X_SDK_PATH)/modules/nrfx/mdk/system_nrf52.c +else +TARGETSOURCES += $(NRF5X_SDK_PATH)/components/toolchain/system_nrf52.c +ifdef NRF_SDK14 +# Use SDK14 +NRF5X_SDK=14 +NRF5X_SDK_14=1 +NRF5X_SDK_PATH=$(ROOT)/targetlibs/nrf5x_14 +DEFINES += -DNRF_SD_BLE_API_VERSION=5 +DEFINES += -D__HEAP_SIZE=0 +SOFTDEVICE_PATH = $(NRF5X_SDK_PATH)/components/softdevice/s132 +SOFTDEVICE = $(SOFTDEVICE_PATH)/hex/s132_nrf52_5.0.0_softdevice.hex +DEFINES += -DS132 +else # Use SDK12 NRF5X_SDK=12 NRF5X_SDK_12=1 NRF5X_SDK_PATH=$(ROOT)/targetlibs/nrf5x_12 DEFINES += -DNRF_SD_BLE_API_VERSION=3 -SOFTDEVICE = $(NRF5X_SDK_PATH)/components/softdevice/s132/hex/s132_nrf52_3.0.0_softdevice.hex - -# Use SDK14 -#NRF5X_SDK=14 -#NRF5X_SDK_14=1 -#NRF5X_SDK_PATH=$(ROOT)/targetlibs/nrf5x_14 -#DEFINES += -DNRF_SD_BLE_API_VERSION=5 -#SOFTDEVICE = $(NRF5X_SDK_PATH)/components/softdevice/s132/hex/s132_nrf52_5.0.0_softdevice.hex +SOFTDEVICE_PATH = $(NRF5X_SDK_PATH)/components/softdevice/s132 +SOFTDEVICE = $(SOFTDEVICE_PATH)/hex/s132_nrf52_3.0.0_softdevice.hex +DEFINES += -DS132 +endif +endif # ARCHFLAGS are shared by both CFLAGS and LDFLAGS. ARCHFLAGS = -mcpu=cortex-m4 -mthumb -mabi=aapcs -mfloat-abi=hard -mfpu=fpv4-sp-d16 # nRF52 specific. -INCLUDE += -I$(NRF5X_SDK_PATH)/components/softdevice/s132/headers -INCLUDE += -I$(NRF5X_SDK_PATH)/components/softdevice/s132/headers/nrf52 -TARGETSOURCES += $(NRF5X_SDK_PATH)/components/toolchain/system_nrf52.c \ - $(NRF5X_SDK_PATH)/components/drivers_nrf/hal/nrf_saadc.c +INCLUDE += -I$(SOFTDEVICE_PATH)/headers +INCLUDE += -I$(SOFTDEVICE_PATH)/headers/nrf52 +ifdef NRF5X_SDK_15 +PRECOMPILED_OBJS += $(NRF5X_SDK_PATH)/modules/nrfx/mdk/gcc_startup_nrf52.S # FIXME variants for 52840 as well +else PRECOMPILED_OBJS += $(NRF5X_SDK_PATH)/components/toolchain/gcc/gcc_startup_nrf52.o +endif -DEFINES += -DSWI_DISABLE0 -DSOFTDEVICE_PRESENT -DFLOAT_ABI_HARD -DNRF52 -DNRF52832_XXAA -DNRF52_PAN_74 -DS132 -DBLE_STACK_SUPPORT_REQD +DEFINES += -DSWI_DISABLE0 -DSOFTDEVICE_PRESENT -DFLOAT_ABI_HARD -DNRF52 -DNRF52832_XXAA -DNRF52_PAN_74 -DBLE_STACK_SUPPORT_REQD ifdef USE_BOOTLOADER NRF_BOOTLOADER = $(BOOTLOADER_PROJ_NAME).hex @@ -45,17 +69,30 @@ ifndef BOOTLOADER # BLE HID Support (only NRF52) INCLUDE += -I$(NRF5X_SDK_PATH)/components/ble/ble_services/ble_hids TARGETSOURCES += $(NRF5X_SDK_PATH)/components/ble/ble_services/ble_hids/ble_hids.c -# Neopixel support (only NRF52) +# Random hardware requirements (only NRF52) INCLUDE += -I$(NRF5X_SDK_PATH)/components/drivers_nrf/i2s +ifdef NRF5X_SDK_12 TARGETSOURCES += $(NRF5X_SDK_PATH)/components/drivers_nrf/i2s/nrf_drv_i2s.c -# Secure connection support +TARGETSOURCES += $(NRF5X_SDK_PATH)/components/drivers_nrf/hal/nrf_saadc.c +TARGETSOURCES += $(NRF5X_SDK_PATH)/components/drivers_nrf/rng/nrf_drv_rng.c +endif +ifdef NRF5X_SDK_14 +TARGETSOURCES += $(NRF5X_SDK_PATH)/components/drivers_nrf/i2s/nrf_drv_i2s.c +TARGETSOURCES += $(NRF5X_SDK_PATH)/components/drivers_nrf/hal/nrf_saadc.c +TARGETSOURCES += $(NRF5X_SDK_PATH)/components/drivers_nrf/rng/nrf_drv_rng.c +endif +ifdef NRF5X_SDK_15 +TARGETSOURCES += $(NRF5X_SDK_PATH)/modules/nrfx/drivers/src/nrfx_i2s.c +TARGETSOURCES += $(NRF5X_SDK_PATH)/modules/nrfx/drivers/src/nrfx_saadc.c +TARGETSOURCES += $(NRF5X_SDK_PATH)/modules/nrfx/drivers/src/nrfx_rng.c +endif +# Secure connection support INCLUDE += -I$(NRF5X_SDK_PATH)/components/libraries/ecc TARGETSOURCES += $(NRF5X_SDK_PATH)/components/libraries/ecc/ecc.c INCLUDE += -I$(NRF5X_SDK_PATH)/components/drivers_nrf/rng -TARGETSOURCES += $(NRF5X_SDK_PATH)/components/drivers_nrf/rng/nrf_drv_rng.c INCLUDE += -I$(NRF5X_SDK_PATH)/external/micro-ecc TARGETSOURCES += $(NRF5X_SDK_PATH)/external/micro-ecc/uECC.c -endif +endif # BOOTLOADER include make/common/NRF5X.make diff --git a/make/targets/ESP8266.make b/make/targets/ESP8266.make index e660bb23f..21ceca4b5 100644 --- a/make/targets/ESP8266.make +++ b/make/targets/ESP8266.make @@ -44,8 +44,12 @@ $(PARTIAL): $(OBJS) $(LINKER_FILE) @echo LD $@ ifdef USE_CRYPTO $(Q)$(OBJCOPY) --rename-section .rodata=.irom0.text libs/crypto/mbedtls/library/sha1.o +ifdef USE_SHA256 $(Q)$(OBJCOPY) --rename-section .rodata=.irom0.text libs/crypto/mbedtls/library/sha256.o +endif +ifdef USE_SHA512 $(Q)$(OBJCOPY) --rename-section .rodata=.irom0.text libs/crypto/mbedtls/library/sha512.o +endif endif $(Q)$(LD) $(OPTIMIZEFLAGS) -nostdlib -Wl,--no-check-sections -Wl,-static -r -o $@ $(OBJS) $(Q)$(OBJCOPY) --rename-section .text=.irom0.text --rename-section .literal=.irom0.literal $@ @@ -142,14 +146,14 @@ flash: all $(USER1_BIN) $(USER2_BIN) ifndef COMPORT $(error "In order to flash, we need to have the COMPORT variable defined") endif - -$(ESPTOOL) --port $(COMPORT) --baud $(FLASH_BAUD) write_flash --flash_freq $(ET_FF) --flash_mode qio --flash_size $(ET_FS) 0x0000 $(BOOTLOADER) 0x1000 $(USER1_BIN) $(ET_DEFAULTS) $(INIT_DATA) $(ET_BLANK) $(BLANK) + -$(ESPTOOL) --port $(COMPORT) --baud $(FLASH_BAUD) write_flash --flash_freq $(ET_FF) --flash_mode $(ET_FM) --flash_size $(ET_FS) 0x0000 $(BOOTLOADER) 0x1000 $(USER1_BIN) $(ET_DEFAULTS) $(INIT_DATA) $(ET_BLANK) $(BLANK) flash_combined: $(ESP_COMBINED) ifndef COMPORT $(error "In order to flash, we need to have the COMPORT variable defined") endif - -$(ESPTOOL) --port $(COMPORT) --baud $(FLASH_BAUD) write_flash --flash_freq $(ET_FF) --flash_mode qio --flash_size $(ET_FS) 0x0000 $(ESP_COMBINED) + -$(ESPTOOL) --port $(COMPORT) --baud $(FLASH_BAUD) write_flash --flash_freq $(ET_FF) --flash_mode $(ET_FM) --flash_size $(ET_FS) 0x0000 $(ESP_COMBINED) # erase flash flash_erase: . diff --git a/scripts/build_board_docs.py b/scripts/build_board_docs.py index e85b5d9ca..60905a38d 100755 --- a/scripts/build_board_docs.py +++ b/scripts/build_board_docs.py @@ -33,7 +33,7 @@ print("Script location "+scriptdir) embeddable = False boardname = "" if len(sys.argv)==3 and sys.argv[2]=="pinout": - embeddable = True + embeddable = True boardname = sys.argv[1] if len(sys.argv)==2: boardname = sys.argv[1] @@ -78,7 +78,7 @@ for pin in pins: def has_pinb(brd,pin): for pinstrip in brd: if pinstrip[0]!='_': - for p in brd[pinstrip]: + for p in brd[pinstrip]: if p==pin: return True return False @@ -99,15 +99,15 @@ def dump_pin(brd, pin, pinstrip): pinmap = brd['_pinmap']; if pin in pinmap: - pin = pinmap[pin]; + pin = pinmap[pin]; pininfo = pinutils.findpin(pins, pin, False) not_five_volt = False # print(json.dumps(pininfo)) - if ("csv" in pininfo) and ("IO" in pininfo["csv"]) and ("Type" in pininfo["csv"]) and (pininfo["csv"]["Type"]=="I/O") and (pininfo["csv"]["IO"]!="FT") : + if ("csv" in pininfo) and ("IO" in pininfo["csv"]) and ("Type" in pininfo["csv"]) and (pininfo["csv"]["Type"]=="I/O") and (pininfo["csv"]["IO"]!="FT") : not_five_volt = True - if "3.3" in pininfo["functions"]: + if "3.3" in pininfo["functions"]: not_five_volt = True writeHTML('
'); @@ -127,14 +127,14 @@ def dump_pin(brd, pin, pinstrip): pinfuncs = {} for func in sorted(pininfo["functions"]): -# writeHTML(' '+func) - if func in pinutils.CLASSES: +# writeHTML(' '+func) + if func in pinutils.CLASSES: funcdata = str(pininfo["functions"][func]) cls = pinutils.CLASSES[func] name = cls title = func if cls=="I2C" or cls=="SPI" or cls=="USART": name=func.replace("_"," ") - + if cls=="DEVICE" and funcdata[:4]=="pin_": title = title + " ("+funcdata[4:]+")"; # print title @@ -150,49 +150,54 @@ def dump_pin(brd, pin, pinstrip): url = False if pf["cls"] in pinutils.URLS: url = pinutils.URLS[pf["cls"]] if pf["func"] in pinutils.URLS: url = pinutils.URLS[pf["func"]] - + if url != False: writeHTML(' '); writeHTML(' '+pf["name"]+'') if url != False: writeHTML(' '); - writeHTML(' ') - + writeHTML(' ') + if reverse: writeHTML(pinHTML2+"\n"+pinHTML) - writeHTML('
') + writeHTML(' ') if not embeddable: writeHTML(""" """); writeHTML(""" "+'') +writeHTML(" ") writeHTML(""" -