diff --git a/ChangeLog b/ChangeLog index 2dcc9f12c4..4ae1984967 100644 --- a/ChangeLog +++ b/ChangeLog @@ -62,6 +62,13 @@ Pixl.js: Remove Wiznet W5100 support from default build (there's now a espruino_#v##_pixljs_wiznet.zip without JIT enabled) to ensure we have enough flash to continue builds Enable nostartfiles optimisation for Pixl,MDBT42 and nRF52DK STM32F4: Add SDIO support + STM32: Ensure we kick the WDT if auto kicking is enabled and in deep sleep (avoids having to to it manually and wait 30ms for USB to wake up/shut down) + Allow a 'file receive' packet which can request Espruino sends a file as binary packets (also fix files not being closed if transmission fails) + Fix parsing of semicolons in DO with a statement: `do print(a);while(a--);` + In SAVE_ON_FLASH builds (Microbit 1) remove getSerial, Math.LN*/LOG*SQRT* constants, passwords, Serial/I2C/SPI.find, Date.toUTCString + ESP32: add setIP and setAPIP + Graphics.wrapString fix issue with missing final char if immediately after a '.' or other char we can split after (#2572) + Graphics: g.dump/asBMP can now output 16 bit images Crypto: Add support for AES CCM 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' diff --git a/README_BuildProcess.md b/README_BuildProcess.md index 2c7e55151e..f75ca93f46 100644 --- a/README_BuildProcess.md +++ b/README_BuildProcess.md @@ -179,6 +179,7 @@ These are set automatically when `SAVE_ON_FLASH` is set (see `jsutils.h`) * `ESPR_NO_LET_SCOPING` - don't create scopes for `let` (treat it like `var`, which was the 2v13 and earlier behaviour) * `ESPR_NO_PROMISES` - Don't include promise-handling functions * `ESPR_NO_PRETOKENISE` - Don't include code to pretokenise functions marked with `"ram"` - code pretokenised in the IDE can still be executed +* `ESPR_NO_PASSWORD` - Disable password protection on the console (E.setPassword/E.lockConsole) ### chip diff --git a/libs/bluetooth/bluetooth_utils.c b/libs/bluetooth/bluetooth_utils.c index f15c1a6291..375cd46233 100644 --- a/libs/bluetooth/bluetooth_utils.c +++ b/libs/bluetooth/bluetooth_utils.c @@ -394,14 +394,12 @@ void jsble_queue_pending_buf(BLEPending blep, uint16_t data, char *ptr, size_t l // Push the actual event JsSysTime d = (JsSysTime)((data<<8)|blep); jshPushIOEvent(EV_BLUETOOTH_PENDING, d); - jshHadEvent(); } /// Add a new bluetooth event to the queue with 16 bits of data void jsble_queue_pending(BLEPending blep, uint16_t data) { JsSysTime d = (JsSysTime)((data<<8)|blep); jshPushIOEvent(EV_BLUETOOTH_PENDING, d); - jshHadEvent(); } /* Handler for common event types (between nRF52/ESP32). Called first diff --git a/libs/bluetooth/jswrap_bluetooth.c b/libs/bluetooth/jswrap_bluetooth.c index 8165c3d968..a455c38011 100644 --- a/libs/bluetooth/jswrap_bluetooth.c +++ b/libs/bluetooth/jswrap_bluetooth.c @@ -2365,6 +2365,7 @@ void jswrap_ble_setTxPower(JsVarInt pwr) { "type" : "staticmethod", "class" : "NRF", "name" : "setLowPowerConnection", + "deprecated" : true, "generate" : "jswrap_ble_setLowPowerConnection", "params" : [ ["lowPower","bool","Whether the connection is low power or not"] diff --git a/libs/filesystem/fat_sd/sdio_diskio.c b/libs/filesystem/fat_sd/sdio_diskio.c index bf19c5ad23..8d9d4013c1 100644 --- a/libs/filesystem/fat_sd/sdio_diskio.c +++ b/libs/filesystem/fat_sd/sdio_diskio.c @@ -1,6 +1,6 @@ // USED FOR SDIO-BASED SD CARDS /* - * @author: ickle + * @author: ickle * @source: http://www.61ic.com/code/archiver/?tid-27986.html */ @@ -175,10 +175,12 @@ DRESULT disk_ioctl ( res = RES_OK; break; - case GET_SECTOR_COUNT : // Get number of sectors on the disk (DWORD) - *(DWORD*)buff = 131072; // 4*1024*32 = 131072 + case GET_SECTOR_COUNT : { // Get number of sectors on the disk (DWORD) + SD_CardInfo SDCardInfo; + SD_GetCardInfo(&SDCardInfo); + *(DWORD*)buff = SDCardInfo.CardCapacity>>9; res = RES_OK; - break; + } break; case GET_SECTOR_SIZE : // Get R/W sector size (WORD) *(WORD*)buff = 512; diff --git a/libs/graphics/graphics.c b/libs/graphics/graphics.c index d5ace6863c..da2772dcdf 100644 --- a/libs/graphics/graphics.c +++ b/libs/graphics/graphics.c @@ -366,6 +366,20 @@ JsGraphicsSetPixelFn graphicsGetSetPixelUnclippedFn(JsGraphics *gfx, int x1, int return gfx->setPixel; // fast } +/// Merge one color into another based RGB565(amt is 0..256) +uint16_t graphicsBlendColorRGB565(uint16_t f, uint16_t b, int amt) { + unsigned int br = (b>>11)&0x1F; + unsigned int bg = (b>>5)&0x3F; + unsigned int bb = b&0x1F; + unsigned int fr = (f>>11)&0x1F; + unsigned int fg = (f>>5)&0x3F; + unsigned int fb = f&0x1F; + unsigned int ri = (br*(256-amt) + fr*amt) >> 8; + unsigned int gi = (bg*(256-amt) + fg*amt) >> 8; + unsigned int bi = (bb*(256-amt) + fb*amt) >> 8; + return (bi | gi<<5 | ri<<11); +} + /// Merge one color into another based on current bit depth (amt is 0..256) uint32_t graphicsBlendColor(JsGraphics *gfx, unsigned int fg, unsigned int bg, int iamt) { unsigned int amt = (iamt>0) ? (unsigned)iamt : 0; @@ -374,18 +388,7 @@ uint32_t graphicsBlendColor(JsGraphics *gfx, unsigned int fg, unsigned int bg, i // TODO: if our graphics instance is paletted this isn't correct! return (bg*(256-amt) + fg*amt + 127) >> 8; } else if (gfx->data.bpp==16) { // Blend from bg to fg - unsigned int b = bg; - unsigned int br = (b>>11)&0x1F; - unsigned int bg = (b>>5)&0x3F; - unsigned int bb = b&0x1F; - unsigned int f = fg; - unsigned int fr = (f>>11)&0x1F; - unsigned int fg = (f>>5)&0x3F; - unsigned int fb = f&0x1F; - unsigned int ri = (br*(256-amt) + fr*amt) >> 8; - unsigned int gi = (bg*(256-amt) + fg*amt) >> 8; - unsigned int bi = (bb*(256-amt) + fb*amt) >> 8; - return (bi | gi<<5 | ri<<11); + return graphicsBlendColorRGB565(fg,bg,iamt); #ifdef ESPR_GRAPHICS_12BIT } else if (gfx->data.bpp==12) { // Blend from bg to fg unsigned int b = bg; @@ -911,7 +914,7 @@ void graphicsScroll(JsGraphics *gfx, int xdir, int ydir) { static void graphicsDrawString(JsGraphics *gfx, int x1, int y1, const char *str) { // no need to modify coordinates as setPixel does that while (*str) { -#ifdef USE_FONT_6X8 +#ifdef USE_FONT_6X8 graphicsDrawChar6x8(gfx,x1,y1,*(str++),1,1,false); x1 = (int)(x1 + 6); #else diff --git a/libs/graphics/graphics.h b/libs/graphics/graphics.h index 75c4fa3ae4..189e70690f 100644 --- a/libs/graphics/graphics.h +++ b/libs/graphics/graphics.h @@ -201,6 +201,8 @@ void graphicsSetModified(JsGraphics *gfx, int x1, int y1, int x2, int y2); JsGraphicsSetPixelFn graphicsGetSetPixelFn(JsGraphics *gfx); /// Get a setPixel function and set modified area (assuming no clipping) (inclusive of x2,y2) - if all is ok it can choose a faster draw function JsGraphicsSetPixelFn graphicsGetSetPixelUnclippedFn(JsGraphics *gfx, int x1, int y1, int x2, int y2, bool coordsRotatedAlready); +/// Merge one color into another based RGB565(amt is 0..256) +uint16_t graphicsBlendColorRGB565(uint16_t fg, uint16_t bg, int iamt); /// Merge one color into another based on current bit depth (amt is 0..256) uint32_t graphicsBlendColor(JsGraphics *gfx, unsigned int fg, unsigned int bg, int iamt); /// Merge one color into another based on current bit depth (amt is 0..256) diff --git a/libs/graphics/jswrap_graphics.c b/libs/graphics/jswrap_graphics.c index 5c9245f01a..c32ef7bc5d 100644 --- a/libs/graphics/jswrap_graphics.c +++ b/libs/graphics/jswrap_graphics.c @@ -1720,7 +1720,7 @@ It is recommended that you use `Graphics.setFont("4x6")` for more flexibility. "type" : "method", "class" : "Graphics", "name" : "setFontVector", - "ifndef" : "SAVE_ON_FLASH", + "#if" : "!defined(SAVE_ON_FLASH) && !defined(NO_VECTOR_FONT)", "generate_full" : "jswrap_graphics_setFontSizeX(parent, size, true)", "params" : [ ["size","int32","The height of the font, as an integer"] @@ -2542,7 +2542,10 @@ JsVar *jswrap_graphics_wrapString(JsVar *parent, JsVar *str, int maxWidth) { wasNewLine = ch=='\n'; canSplitAfter = ch==0; // can split after if there is an image next if (endOfText) break; - if (ch!=0) continue; // allow us to handle images next + if (ch!=0) { + if (!jsvStringIteratorHasChar(&it)) endOfText=true; // handle sometimes missed final char: #2572 + continue; // allow us to handle images next + } } canSplitAfter = false; #ifndef SAVE_ON_FLASH @@ -2807,7 +2810,7 @@ void jswrap_graphics_drawCString(JsGraphics *gfx, int x, int y, char *str) { "type" : "method", "class" : "Graphics", "name" : "getVectorFontPolys", - "#if" : "!defined(SAVE_ON_FLASH) || !defined(NO_VECTOR_FONT)", + "#if" : "!defined(SAVE_ON_FLASH) && !defined(NO_VECTOR_FONT)", "generate" : "jswrap_graphics_getVectorFontPolys", "params" : [ ["str","JsVar","The string"], @@ -4142,64 +4145,93 @@ JsVar *jswrap_graphics_asBMP_X(JsVar *parent, bool printBase64) { int rowstride = (((width*bpp)+31) >> 5) << 2; // padded to 32 bits // palette length (byte size is 3x this) int paletteEntries = hasPalette?(1<>8); // plus 2 more bytes for size - imgPtr[10]=(unsigned char)headerLen; - // maybe we want the InfoHeader, not BITMAPCOREHEADER (http://www.ece.ualberta.ca/~elliott/ee552/studentAppNotes/2003_w/misc/bmp_file_format/bmp_file_format.htm) - // Chrome doesn't like 16 bit BMPs in this format - // BITMAPCOREHEADER - imgPtr[14]=12; // sizeof(BITMAPCOREHEADER) + imgPtr[3]=(unsigned char)(fileSize>>8); + imgPtr[4]=(unsigned char)(fileSize>>16); + imgPtr[5]=(unsigned char)(fileSize>>24); + imgPtr[10]=(unsigned char)headerLen; // data offset + // size in here imgPtr[18]=(unsigned char)width; imgPtr[19]=(unsigned char)(width>>8); - imgPtr[20]=(unsigned char)height; - imgPtr[21]=(unsigned char)(height>>8); - imgPtr[22]=1; // color planes, should be 1 - imgPtr[24]=(unsigned char)bpp; // bpp - if (hasPalette) { - // palette starts at 26 - if (bpp==1) { - // first is white(?) - imgPtr[26]=255; - imgPtr[27]=255; - imgPtr[28]=255; - } else { - if (realBPP==3) { - for (int i=0;i>3)&0xFC); - imgPtr[28 + (i*3)] = (unsigned char)((p>>8)&0xF8); - } - } else if (realBPP==8) { - for (int i=0;i<255;i++) { - int p = PALETTE_8BIT[i]; - imgPtr[26 + (i*3)] = (unsigned char)((p<<3)&0xF8); - imgPtr[27 + (i*3)] = (unsigned char)((p>>3)&0xFC); - imgPtr[28 + (i*3)] = (unsigned char)((p>>8)&0xF8); - } -#endif - } else { // otherwise default to greyscale - for (int i=0;i<(1<>8); + imgPtr[h+12]=1; // planes + imgPtr[h+14]=16; // bits + imgPtr[h+16]=3; // compression BI_BITFIELDS + uint32_t size = height*rowstride; + imgPtr[h+20]=(unsigned char)(size); + imgPtr[h+21]=(unsigned char)(size>>8); + imgPtr[h+22]=(unsigned char)(size>>16); + imgPtr[h+23]=(unsigned char)(size>>24); + //imgPtr[h+40]=0x00;//R + imgPtr[h+41]=0xF8; + imgPtr[h+44]=0xE0;//G + imgPtr[h+45]=0x07; + imgPtr[h+48]=0x1F;//B + //imgPtr[h+49]=0x00; + } else { + // BITMAPCOREHEADER + imgPtr[14]=12; // sizeof(BITMAPCOREHEADER) + imgPtr[20]=(unsigned char)height; + imgPtr[21]=(unsigned char)(height>>8); + imgPtr[22]=1; // color planes, should be 1 + imgPtr[24]=(unsigned char)bpp; // bpp + if (hasPalette) { + // palette starts at 26 + if (bpp==1) { + // first is white(?) + imgPtr[26]=255; + imgPtr[27]=255; + imgPtr[28]=255; + } else { + if (realBPP==3) { + for (int i=0;i>3)&0xFC); + imgPtr[28 + (i*3)] = (unsigned char)((p>>8)&0xF8); + } + } else if (realBPP==8) { + for (int i=0;i<255;i++) { + int p = PALETTE_8BIT[i]; + imgPtr[26 + (i*3)] = (unsigned char)((p<<3)&0xF8); + imgPtr[27 + (i*3)] = (unsigned char)((p>>3)&0xFC); + imgPtr[28 + (i*3)] = (unsigned char)((p>>8)&0xF8); + } + #endif + } else { // otherwise default to greyscale + for (int i=0;i<(1<>1)&~31U); for (int j=0;j2) { + jshKickWatchDog(); // uploading can take a while bool isLastRow = y==0; int count = isLastRow ? idx : (idx-(idx%3)); JsVar *view = jsvNewArrayBufferFromString(imgData, (unsigned int)count); // create an arraybuffer - this means we can pass to btoa with zero allocations diff --git a/libs/graphics/lcd_fsmc.c b/libs/graphics/lcd_fsmc.c index 17db175324..d5d0d133d8 100644 --- a/libs/graphics/lcd_fsmc.c +++ b/libs/graphics/lcd_fsmc.c @@ -1772,7 +1772,8 @@ void lcdFillRect_FSMC(JsGraphics *gfx, int x1, int y1, int x2, int y2, unsigned unsigned int lcdGetPixel_FSMC(JsGraphics *gfx, int x, int y) { lcdSetCursor(gfx,x,y); - lcdSetWrite(); // ? + LCD_WR_REG(0x2E); // start read + LCD_RD_Data(); // dummy read return LCD_RD_Data(); } diff --git a/libs/hexbadge/jswrap_hexbadge.c b/libs/hexbadge/jswrap_hexbadge.c index b4d008274a..388627e757 100644 --- a/libs/hexbadge/jswrap_hexbadge.c +++ b/libs/hexbadge/jswrap_hexbadge.c @@ -192,6 +192,7 @@ int jswrap_badge_capSense(int corner) { "type" : "staticmethod", "class" : "Badge", "name" : "getBatteryPercentage", + "deprecated" : true, "generate" : "jswrap_badge_getBatteryPercentage", "return" : ["int", "A percentage between 0 and 100" ] } diff --git a/libs/network/esp32/jswrap_esp32_network.c b/libs/network/esp32/jswrap_esp32_network.c index 00b5e02539..633ba93ff6 100644 --- a/libs/network/esp32/jswrap_esp32_network.c +++ b/libs/network/esp32/jswrap_esp32_network.c @@ -92,7 +92,6 @@ static bool g_isStaConnected = false; #define EXPECT_CB_EXCEPTION(jsCB) jsExceptionHere(JSET_ERROR, "Expecting callback function but got %v", jsCB) #define EXPECT_OPT_EXCEPTION(jsOPT) jsExceptionHere(JSET_ERROR, "Expecting Object, got %t", jsOPT) - //===== mDNS static bool mdns_started = 0; @@ -1775,3 +1774,99 @@ void jswrap_wifi_getHostByName( dnsFoundCallback(hostname, NULL, NULL); } } + +// worker for jswrap_wifi_setIP and jswrap_wifi_setAPIP +static void setIP(JsVar *jsSettings, JsVar *jsCallback, int interface) { + char ipTmp[20]; + int len = 0; + esp_err_t err; + tcpip_adapter_ip_info_t info; + memset( &info, 0, sizeof(info) ); + +// first check parameter + if (!jsvIsObject(jsSettings)) { + EXPECT_OPT_EXCEPTION(jsSettings); + return; + } + +// get,check and store ip + JsVar *jsIP = jsvObjectGetChildIfExists(jsSettings, "ip"); + if (jsIP != NULL && !jsvIsString(jsIP)) { + EXPECT_OPT_EXCEPTION(jsIP); + jsvUnLock(jsIP); + return; + } + jsvGetString(jsIP, ipTmp, sizeof(ipTmp)-1); + info.ip.addr = networkParseIPAddress(ipTmp); + if ( info.ip.addr == 0) { + jsExceptionHere(JSET_ERROR, "Not a valid IP address"); + jsvUnLock(jsIP); + return; + } + jsvUnLock(jsIP); + +// get, check and store gw + JsVar *jsGW = jsvObjectGetChildIfExists(jsSettings, "gw"); + if (jsGW != NULL && !jsvIsString(jsGW)) { + EXPECT_OPT_EXCEPTION(jsGW); + jsvUnLock(jsGW); + return ; + } + jsvGetString(jsGW, ipTmp, sizeof(ipTmp)-1); + info.gw.addr = networkParseIPAddress(ipTmp); + if (info.gw.addr == 0) { + jsExceptionHere(JSET_ERROR, "Not a valid Gateway address"); + jsvUnLock(jsGW); + return; + } + jsvUnLock(jsGW); + +// netmask setting + JsVar *jsNM = jsvObjectGetChildIfExists(jsSettings, "netmask"); + if (jsNM != NULL && !jsvIsString(jsNM)) { + EXPECT_OPT_EXCEPTION(jsNM); + jsvUnLock(jsNM); + return; + } + jsvGetString(jsNM, ipTmp, sizeof(ipTmp)-1); + info.netmask.addr = networkParseIPAddress(ipTmp); + if (info.netmask.addr == 0) { + jsExceptionHere(JSET_ERROR, "Not a valid Netmask"); + jsvUnLock(jsNM); + return; + } + jsvUnLock(jsNM); +// set IP for station + if (interface == WIFI_IF_STA) { + tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_STA); + err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &info); + } +// set IP for access point + else { + tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP); + err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_AP, &info); + tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); + } +// Schedule callback + if (jsvIsFunction(jsCallback)) { + JsVar *params[1]; + params[0] = err ? jsvNewWithFlags(JSV_NULL) : jsvNewFromString("Failure"); + jsiQueueEvents(NULL, jsCallback, params, 1); + jsvUnLock(params[0]); + } + else { + jsExceptionHere(JSET_ERROR, "Callback is not a function"); + } + return ; +}; + + +void jswrap_wifi_setIP(JsVar *jsSettings, JsVar *jsCallback) { + setIP(jsSettings, jsCallback, WIFI_IF_STA); + return ; +} + +void jswrap_wifi_setAPIP(JsVar *jsSettings, JsVar *jsCallback) { + setIP(jsSettings, jsCallback, WIFI_IF_AP); + return ; +} diff --git a/libs/network/esp8266/jswrap_esp8266_network.c b/libs/network/esp8266/jswrap_esp8266_network.c index 83451e132a..4b1e474933 100644 --- a/libs/network/esp8266/jswrap_esp8266_network.c +++ b/libs/network/esp8266/jswrap_esp8266_network.c @@ -1270,6 +1270,7 @@ void jswrap_ESP8266_wifi_soft_init() { "class" : "ESP8266", "ifdef" : "ESP8266", "name" : "ping", + "deprecated" : true, "generate" : "jswrap_wifi_ping", "params" : [ ["ipAddr", "JsVar", "A string representation of an IP address."], diff --git a/libs/network/jswrap_wifi.c b/libs/network/jswrap_wifi.c index d316b22502..36773d98e7 100644 --- a/libs/network/jswrap_wifi.c +++ b/libs/network/jswrap_wifi.c @@ -605,7 +605,7 @@ returns. "class" : "Wifi", "name" : "setIP", "generate" : "jswrap_wifi_setIP", - "#if" : "defined(ESP8266) || defined(ESPRUINOWIFI)", + "#if" : "defined(ESP8266) || defined(ESPRUINOWIFI) || defined(ESP32)", "params" : [ ["settings", "JsVar", "Configuration settings"], ["callback", "JsVar", "A `callback(err)` function to invoke when ip is set. `err==null` on success, or a string on failure."] @@ -623,7 +623,7 @@ The `settings` object must contain the following properties. "type" : "staticmethod", "class" : "Wifi", "name" : "setAPIP", - "#if" : "defined(ESPRUINOWIFI) || defined(ESP8266)", + "#if" : "defined(ESPRUINOWIFI) || defined(ESP8266) || defined(ESP32)", "generate" : "jswrap_wifi_setAPIP", "params" : [ ["settings", "JsVar", "Configuration settings"], diff --git a/libs/pipboy/jswrap_pipboy.c b/libs/pipboy/jswrap_pipboy.c index 3a230411b0..f63e06951d 100644 --- a/libs/pipboy/jswrap_pipboy.c +++ b/libs/pipboy/jswrap_pipboy.c @@ -49,6 +49,14 @@ uint8_t streamBuffer[STREAM_BUFFER_SIZE+4] __attribute__ ((aligned (8))); // we // Can't go in 64k CCM RAM because no DMA is allowed to CCM! // Maybe if we modified DMA to read to a buffer first? +/** Palette for scanlines +0: even (bright line) +1: odd (dark line) +0: even scan effect +1: odd scan effect + */ +uint16_t palette[4][16]; + typedef enum { ST_NONE, ST_AVI, @@ -201,6 +209,19 @@ void jswrap_pb_videoStart(JsVar *fn, JsVar *options) { f_lseek(&streamFile, (uint32_t)(videoInfo.streamOffset+8)); // go back to start of video data videoFrameTime = jshGetTimeFromMilliseconds(videoInfo.usPerFrame/1000.0); videoNextFrameTime = jshGetSystemTime() + videoFrameTime; + // set palette + for (int i=0;i<256;i++) { + uint16_t c = videoInfo.palette[i]; + uint16_t br = (c>>11)&0x1F; + uint16_t bg = (c>>6)&0x1F; + uint16_t bb = c&0x1F; + uint16_t lum = br; // lum = 0..31 + if (bg>lum) lum=bg; + if (bb>lum) lum=bb; + // We use the non-scan-effect palette here (not idx 2) as it's too bright otherwise + videoInfo.palette[i] = palette[0][lum>>1]; // Should blend so we don't lose 1 bit accuracy? + } + #ifndef LINUX // Set up Audio if (videoInfo.audioBufferSize <= I2S_RING_BUFFER_SIZE*2) { // IF we have audio @@ -380,7 +401,7 @@ JsVar *jswrap_pb_audioRead(JsVar *fn) { "name" : "audioBuiltin", "generate" : "jswrap_pb_audioBuiltin", "params" : [ - ["id","JsVar","OK/NEXT/COLUMN"] + ["id","JsVar","OK/OK2/PREV/NEXT/COLUMN/CLICK"] ], "return" : ["JsVar","The sound as a native string"] } @@ -850,7 +871,7 @@ void jswrap_pb_setDACPower(bool isOn) { jswrap_E_unmountSD(); // Close all files and unmount the SD card #ifndef LINUX SDIO_DeInit(); // Properly shut down the SD interface - jshPinSetState(SD_CLK_PIN, JSHPINSTATE_GPIO_IN_PULLDOWN); + jshPinSetState(SD_CLK_PIN, JSHPINSTATE_GPIO_IN_PULLDOWN); // SD card pins have external pullup resistors to 3V3D_M, which is turned off when the SD_POWER_PIN is low, so we should pull them down to help discharge 3V3D_M jshPinSetState(SD_CMD_PIN, JSHPINSTATE_GPIO_IN_PULLDOWN); jshPinSetState(SD_D0_PIN, JSHPINSTATE_GPIO_IN_PULLDOWN); jshPinSetState(SD_D1_PIN, JSHPINSTATE_GPIO_IN_PULLDOWN); @@ -860,9 +881,9 @@ void jswrap_pb_setDACPower(bool isOn) { /*** FIXME *** We should actually put the SPI flash chip into "power down" mode * ...but for now, just set the SPI flash pins to pulled-up inputs, to ensure that the CS pin isn't left low. * - * NOTE that this won't work in STANDBY mode, as the pins go high-impedance (and there's currently no external pullups). + * NOTE that this won't work in STANDBY mode for PCB v0.6 (or earlier), as the pins go high-impedance (and there's currently no external pullups). */ - jshPinSetState(SPIFLASH_PIN_MOSI, JSHPINSTATE_GPIO_IN_PULLUP); + jshPinSetState(SPIFLASH_PIN_MOSI, JSHPINSTATE_GPIO_IN_PULLUP); // From PCB v0.7, SPI flash pins now have external pullup resistors to 3V3D jshPinSetState(SPIFLASH_PIN_MISO, JSHPINSTATE_GPIO_IN_PULLUP); jshPinSetState(SPIFLASH_PIN_SCK, JSHPINSTATE_GPIO_IN_PULLUP); jshPinSetState(SPIFLASH_PIN_CS, JSHPINSTATE_GPIO_IN_PULLUP); @@ -1005,7 +1026,7 @@ static void jswrap_pb_periph_off() { jshPinSetState(DAC_SDA_PIN, JSHPINSTATE_GPIO_IN); jshPinSetState(RADIO_SCL_PIN, JSHPINSTATE_ADC_IN); jshPinSetState(RADIO_SDA_PIN, JSHPINSTATE_ADC_IN); - jshPinSetState(LCD_TEARING, JSHPINSTATE_ADC_IN); + jshPinSetState(LCD_TEARING, JSHPINSTATE_GPIO_IN_PULLDOWN); STM32_I2S_Kill(); } @@ -1049,8 +1070,8 @@ void jswrap_pb_sleep() { #ifndef LINUX jshTransmitClearDevice(DEFAULT_CONSOLE_DEVICE); // let's just remove any waiting characters for now jshUSARTUnSetup(DEFAULT_CONSOLE_DEVICE); - jshPinSetState(DEFAULT_CONSOLE_TX_PIN, JSHPINSTATE_ADC_IN); - jshPinSetState(DEFAULT_CONSOLE_RX_PIN, JSHPINSTATE_ADC_IN); + jshPinSetState(DEFAULT_CONSOLE_TX_PIN, JSHPINSTATE_GPIO_IN_PULLUP); + jshPinSetState(DEFAULT_CONSOLE_RX_PIN, JSHPINSTATE_GPIO_IN_PULLUP); #endif jswrap_interface_setDeepSleep(1); } @@ -1095,27 +1116,36 @@ void jswrap_pb_wake() { If `height` isn't specified the image height is used, otherwise only part of the image can be rendered. */ int scanlinePos = 0; -void getPaletteForLine2bpp(int y, uint16_t *palette) { +void getPaletteForLine2bpp(int y, uint16_t *pal) { int distfromScaline = y-scanlinePos; if (distfromScaline<0) distfromScaline=-distfromScaline; - int brightness = 220 + ((distfromScaline>64)?0:(63-distfromScaline)); - if (brightness>255) brightness=255; - if (y&1) brightness = (brightness*3) >> 2; - palette[3] = (uint16_t)(brightness>>2)<<5; - brightness = (brightness*2)/3; - palette[2] = (uint16_t)(brightness>>2)<<5; - brightness = brightness >> 1; - palette[1] = (uint16_t)(brightness>>2)<<5; -} -void getPaletteForLine4bpp(int y, uint16_t *palette) { + int n = y&1; + if (distfromScaline>64) { + // no overscan effect - too far away + pal[0] = palette[n][0]; + pal[1] = palette[n][5]; + pal[2] = palette[n][10]; + pal[3] = palette[n][15]; + } else { + int a = distfromScaline<<2; // 0..255 + pal[0] = graphicsBlendColorRGB565(palette[n][0],palette[n+2][0],a); + pal[1] = graphicsBlendColorRGB565(palette[n][5],palette[n+2][5],a); + pal[2] = graphicsBlendColorRGB565(palette[n][10],palette[n+2][10],a); + pal[3] = graphicsBlendColorRGB565(palette[n][15],palette[n+2][15],a); + } +} +void getPaletteForLine4bpp(int y, uint16_t *pal) { int distfromScaline = y-scanlinePos; if (distfromScaline<0) distfromScaline=-distfromScaline; - int brightness = 220 + ((distfromScaline>64)?0:(63-distfromScaline)); - if (brightness>255) brightness=255; - if (y&1) brightness = (brightness*3) >> 2; - for (int i=1;i<16;i++) { - int b = (brightness*i)>>4; - palette[i] = (uint16_t)(b>>2)<<5; + int n = y&1; + if (distfromScaline>64) { + // no overscan effect - too far away + for (int i=0;i<16;i++) + pal[i] = palette[n][i]; + } else { + int a = distfromScaline<<2; // 0..255 + for (int i=0;i<16;i++) + pal[i] = graphicsBlendColorRGB565(palette[n][i],palette[n+2][i],a); } } void jswrap_pb_blitImage(JsVar *image, int x, int y, JsVar *options) { @@ -1214,6 +1244,69 @@ JsVar *jswrap_pb_streamPlaying() { return 0; } +/*JSON{ + "type" : "staticmethod", + "class" : "Pip", + "name" : "setPalette", + "generate" : "jswrap_pb_setPalette", + "params" : [ + ["palette","JsVar","A 4 element array of 16 element arrays"] + ] +} +Set the colour palette used for rendering everything on PipBoy + +eg: + +``` +var pal = [ + new Uint16Array(16), + new Uint16Array(16), + new Uint16Array(16), + new Uint16Array(16) +]; +// Orangey +for (var i=0;i<16;i++) { + pal[0][i] = g.toColor(i/15,i/30,0); // 0: even (bright line) + pal[1][i] = g.toColor(i/30,i/60,0); // 1: odd (dark line) + pal[2][i] = g.toColor(i/10,i/20,0); // 0: even scan effect + pal[3][i] = g.toColor(i/20,i/40,0); // 1: odd scan effect +} +Pip.setPalette(pal); +``` +*/ +static void jswrap_pb_updateTheme() { + graphicsTheme.fg = (JsGraphicsThemeColor)palette[0][15]; + graphicsTheme.bg = (JsGraphicsThemeColor)palette[0][0]; + graphicsTheme.fg2 = (JsGraphicsThemeColor)palette[2][15]; + graphicsTheme.bg2 = (JsGraphicsThemeColor)palette[2][0]; + graphicsTheme.fgH = (JsGraphicsThemeColor)palette[0][0]; + graphicsTheme.bgH = (JsGraphicsThemeColor)palette[0][15]; + graphicsTheme.dark = graphicsTheme.bg; +} + +void jswrap_pb_setPalette(JsVar *pal) { + if (!jsvIsArray(pal)) { + jsExceptionHere(JSET_ERROR, "Expecting array of arrays"); + return; + } + for (int i=0;i<4;i++) { + JsVar *a = jsvGetArrayItem(pal, i); + if (!jsvIsIterable(a)) { + jsExceptionHere(JSET_ERROR, "Expecting array of arrays, pal[%d] is %t", i, a); + jsvUnLock(a); + return; + } + JsvIterator it; + jsvIteratorNew(&it, a, JSIF_EVERY_ARRAY_ELEMENT); + for (int c=0;c<16;c++) { + palette[i][c] = (uint16_t)jsvIteratorGetIntegerValue(&it); + jsvIteratorNext(&it); + } + jsvIteratorFree(&it); + jsvUnLock(a); + } + jswrap_pb_updateTheme(); +} /*JSON{ "type" : "init", @@ -1222,12 +1315,23 @@ JsVar *jswrap_pb_streamPlaying() { void jswrap_pb_init() { // Enable watchdog jswrap_espruino_enableWatchdog(15, NULL); // init watchdog, auto mode + // Set up colour palette + for (uint16_t i=0;i<16;i++) { + // normally we're a bit more dim + uint16_t b = (i*220) >> 6; // 0..63 + palette[0][i] = b << 5; // even + palette[1][i] = ((b*3)>>2) << 5; // odd + // overscan - full range + palette[2][i] = i << 7; + palette[3][i] = (i*3) << 5; + } + jswrap_pb_updateTheme(); // splash screen const unsigned char img_raw[] = {199, 17, 2, 0, 0, 31, 255, 255, 255, 255, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 255, 255, 255, 255, 255, 255, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 255, 255, 255, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 91, 255, 213, 85, 85, 111, 255, 192, 11, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 249, 85, 85, 85, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 253, 0, 0, 0, 191, 253, 0, 127, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 128, 0, 0, 7, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 255, 224, 0, 0, 7, 255, 224, 127, 255, 224, 2, 255, 255, 255, 255, 255, 233, 0, 0, 0, 0, 0, 0, 0, 0, 255, 248, 0, 0, 0, 63, 254, 0, 107, 255, 255, 255, 232, 0, 191, 255, 255, 224, 127, 255, 255, 248, 0, 0, 63, 255, 255, 255, 255, 255, 254, 7, 255, 255, 192, 47, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255, 255, 255, 255, 255, 224, 127, 255, 255, 255, 255, 253, 91, 255, 255, 255, 131, 255, 255, 255, 224, 0, 3, 255, 255, 255, 255, 255, 255, 224, 26, 255, 252, 0, 107, 255, 250, 170, 170, 255, 248, 0, 170, 170, 170, 170, 168, 0, 191, 255, 255, 255, 255, 255, 248, 7, 255, 250, 170, 171, 255, 255, 255, 255, 255, 252, 47, 255, 255, 254, 0, 0, 47, 255, 191, 255, 255, 234, 80, 0, 7, 255, 192, 0, 47, 255, 0, 0, 11, 255, 192, 11, 255, 255, 255, 255, 224, 11, 255, 234, 170, 170, 171, 255, 240, 63, 253, 0, 0, 31, 255, 255, 253, 191, 255, 0, 127, 255, 208, 0, 0, 2, 255, 244, 0, 0, 0, 0, 0, 0, 127, 253, 0, 1, 255, 244, 0, 0, 191, 252, 0, 21, 85, 85, 85, 85, 0, 127, 254, 0, 0, 0, 31, 255, 67, 255, 224, 0, 0, 255, 245, 85, 64, 255, 253, 31, 255, 244, 0, 0, 0, 31, 255, 128, 0, 0, 0, 0, 0, 3, 255, 208, 0, 31, 255, 64, 0, 7, 255, 208, 0, 0, 0, 0, 0, 0, 7, 255, 224, 0, 0, 0, 255, 248, 47, 255, 0, 0, 15, 255, 128, 0, 1, 255, 255, 255, 248, 0, 0, 0, 85, 255, 248, 0, 0, 0, 0, 0, 0, 127, 254, 81, 20, 255, 249, 64, 5, 127, 255, 213, 64, 0, 0, 0, 0, 17, 127, 255, 64, 0, 5, 95, 255, 130, 255, 245, 85, 85, 255, 248, 0, 0, 2, 255, 255, 254, 0, 0, 0, 15, 255, 255, 192, 0, 0, 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 0, 0, 0, 3, 255, 255, 255, 255, 255, 255, 255, 248, 15, 255, 255, 255, 255, 255, 128, 0, 0, 3, 255, 255, 128, 0, 0, 0, 127, 255, 252, 0, 0, 0, 0, 0, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 250, 255, 224, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 249, 0, 11, 255, 255, 255, 255, 144, 0, 0, 0, 127, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 255, 254, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 170, 128, 0, 0, 0, 0, 0, 0}; JsVar *img = jsvNewNativeString((char*)&img_raw[0], sizeof(img_raw)); JsVar *g = jsvNewObject(); // fake object for rendering - graphicsInternal.data.fgColor = 63<<5; - jsvUnLock(jswrap_graphics_clear(g, 0)); + jsvUnLock(jswrap_graphics_clear(g, 1)); + graphicsInternal.data.fgColor = graphicsTheme.fg; jsvUnLock(jswrap_graphics_drawImage(g, img, (LCD_WIDTH-200)/2, LCD_HEIGHT/2-16, NULL)); graphicsInternal.data.fontSize = JSGRAPHICS_FONTSIZE_6X8+1; graphicsInternal.data.fontAlignX = 1; @@ -1248,6 +1352,11 @@ void jswrap_pb_init() { jshPinSetState(SPIFLASH_PIN_MISO, JSHPINSTATE_GPIO_IN_PULLUP); jshPinSetState(SPIFLASH_PIN_SCK, JSHPINSTATE_GPIO_IN_PULLUP); jshPinSetState(SPIFLASH_PIN_CS, JSHPINSTATE_GPIO_IN_PULLUP); + + jshPinOutput(JSH_PORTC_OFFSET+7, 0); // PC7 unused + jshPinOutput(JSH_PORTC_OFFSET+13, 0); // PC13 unused, right next to clock so tie low + jshPinOutput(JSH_PORTD_OFFSET+6, 0); // PD6 unused + jshPinOutput(JSH_PORTD_OFFSET+13, 0); // PD13 unused #endif // turn backlight on after a delay by default jsvUnLock(jsiSetTimeout(jswrap_pb_setLCDBacklightOn, 100)); @@ -1269,8 +1378,8 @@ void jswrap_pb_init() { JsVar *s = jsvVarPrintf("FW %s", version); jsvUnLock2(jswrap_graphics_drawString(g, s, (LCD_WIDTH/2) + 64, 10+LCD_HEIGHT/2, 0), s); // Now run some JS code which will check if what's in Storage is that file, and if not will update it - jsvUnLock(jspEvaluate( -"if (require('Storage').read('VERSION')!==VERSION) {" + jsvUnLock(jspEvaluate( // We can also force an update by holding power+volume up +"if (require('Storage').read('VERSION')!==VERSION || (BTN2.read()&&BTN10.read())) {" " B15.set();" // display on " const FILE = 'FW.JS';" " let stat = require('fs').statSync(FILE);" @@ -1303,7 +1412,7 @@ void jswrap_pb_init() { if (res == FR_NO_FILE) msg = jsvNewFromString("NO VERSION FILE"); else if (res == FR_NOT_ENABLED) msg = jsvNewFromString("NO SD CARD"); else msg = jsvVarPrintf("SD CARD ERROR %d", res); - graphicsInternal.data.fgColor = 63<<5; // green + graphicsInternal.data.fgColor = graphicsTheme.fg; // green graphicsInternal.data.fontSize = JSGRAPHICS_FONTSIZE_6X8+1; graphicsInternal.data.fontAlignX = 0; jsvUnLock(jswrap_graphics_drawString(g, msg, (LCD_WIDTH/2), LCD_HEIGHT/2+14, 0)); @@ -1321,7 +1430,7 @@ void jswrap_pb_init() { jsvUnLock(msg); } // clear up graphics - graphicsInternal.data.fgColor = 65535; + graphicsInternal.data.fgColor = graphicsTheme.fg; graphicsInternal.data.fontAlignX = -1; jsvUnLock(g); } @@ -1490,4 +1599,4 @@ function es8388_output_cfg(o1en, o2en){ es8388_write_reg(0x04, tempreg); } -*/ \ No newline at end of file +*/ diff --git a/libs/pipboy/jswrap_pipboy.h b/libs/pipboy/jswrap_pipboy.h index 9d11b1138e..ae56d556d6 100644 --- a/libs/pipboy/jswrap_pipboy.h +++ b/libs/pipboy/jswrap_pipboy.h @@ -36,6 +36,7 @@ void jswrap_pb_blitImage(JsVar *image, int x, int y, JsVar *options); void jswrap_pb_getAudioWaveform(JsVar *dst, int y1, int y2); bool jswrap_pb_audioIsPlaying(); JsVar *jswrap_pb_streamPlaying(); +void jswrap_pb_setPalette(JsVar *pal); void jswrap_pb_init(); void jswrap_pb_kill(); diff --git a/libs/pipboy/stm32_i2s.c b/libs/pipboy/stm32_i2s.c index 90c0d906fb..5fb7950eeb 100644 --- a/libs/pipboy/stm32_i2s.c +++ b/libs/pipboy/stm32_i2s.c @@ -174,25 +174,25 @@ void STM32_I2S_Init() { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; // PB12=LRCK, PB13=SCLK GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Speed = GPIO_Low_Speed; // SCLK runs at 1.04 MHz - ST datasheet says "Low Speed" is OK at 8 MHz if VDD>2.7V and pin load capacitance < 10pF + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOB, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_6; // PC2=ASDOUT (ouput from IC, so should not be set as STM32 output), PC3=DSDIN, PC6=MCLK GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Speed = GPIO_Low_Speed; // MCLK runs at 4.16 MHz - ST datasheet says "Low Speed" is OK at 8 MHz if VDD>2.7V and pin load capacitance < 10pF + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_SPI2); // PB12,AF5 I2S_LRCK GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); // PB13,AF5 I2S_SCLK - GPIO_PinAFConfig(GPIOC,GPIO_PinSource3,GPIO_AF_SPI2); // PC3 ,AF5 I2S_DACDATA - GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_SPI2); // PC6 ,AF5 I2S_MCK - GPIO_PinAFConfig(GPIOC,GPIO_PinSource2,GPIO_AF_SPI3); // PC2 ,AF6 I2S_ADCDATA (AF6 apparently?) + GPIO_PinAFConfig(GPIOC,GPIO_PinSource3,GPIO_AF_SPI2); // PC3 ,AF5 I2S_DACDATA + GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_SPI2); // PC6 ,AF5 I2S_MCK + GPIO_PinAFConfig(GPIOC,GPIO_PinSource2,GPIO_AF_SPI3); // PC2 ,AF6 I2S_ADCDATA (AF6 apparently?) - RB 2024-11-25: we're not using this, so should we remove it? I2S_InitStructure.I2S_Mode=I2S_Mode_MasterTx; I2S_InitStructure.I2S_Standard=I2S_Standard_Phillips; @@ -231,7 +231,7 @@ void STM32_I2S_Kill() { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6; GPIO_Init(GPIOC, &GPIO_InitStructure); @@ -244,10 +244,13 @@ int STM32_I2S_GetFreeSamples() { // Add samples to the ringbuffer void STM32_I2S_AddSamples(int16_t *data, unsigned int count) { - int timeout = 1000000; - while ((STM32_I2S_GetFreeSamples() < (int)count+32) && --timeout); // wait here for space - + // Try and fill until ringbuffer is full + unsigned int freeSamples = STM32_I2S_GetFreeSamples(); unsigned int c = count; + if (c > freeSamples) { + c = freeSamples; + count -= freeSamples; + } else count = 0; while (c--) { audioRingBuf[audioRingIdxIn] = *(data++) + (audioRingIdxIn&1); // add 1 bit of noise to stop DAC from turning off! @@ -255,8 +258,8 @@ void STM32_I2S_AddSamples(int16_t *data, unsigned int count) { } //jsiConsolePrintf("add %d %d %d %d\n", i2sDMAidx, i2sStatus, count, audioRingBufGetSamples()); - // start playback when we have enough - if (i2sStatus == STM32_I2S_STOPPED && audioRingBufGetSamples()>I2S_DMA_BUFFER_SIZE*3) { + // start playback if we have enough + if (i2sStatus == STM32_I2S_STOPPED && audioRingBufGetSamples()>=I2S_DMA_BUFFER_SIZE*3) { // if audioRingBufGetSamples()>I2S_DMA_BUFFER_SIZE*3 we should have enough here to fill 6 buffers i2sDMAidx = !DMA_GetCurrentMemoryTarget(DMA1_Stream4); fillDMAFromRingBuffer(); // fill the first buffer with what we're currently reading from! @@ -264,6 +267,21 @@ void STM32_I2S_AddSamples(int16_t *data, unsigned int count) { fillDMAFromRingBuffer(); // fill the second buffer STM32_I2S_Start(); } + + if (!count) return; + // otherwise we still have more to put in - try and wait a bit + if (STM32_I2S_GetFreeSamples() < (int)count) { + // wait until we have space + int timeout = 1000000; + while ((STM32_I2S_GetFreeSamples() < (int)count) && --timeout); // wait here for space + } + // now attempt to put in the rest but drop any we don't have space for + c = count; + while (c-- && STM32_I2S_GetFreeSamples()) { + audioRingBuf[audioRingIdxIn] = *(data++) + + (audioRingIdxIn&1); // add 1 bit of noise to stop DAC from turning off! + audioRingIdxIn = (audioRingIdxIn+1) & (I2S_RING_BUFFER_SIZE-1); + } } void STM32_I2S_Start() { diff --git a/libs/pipboy/stm32_i2s.h b/libs/pipboy/stm32_i2s.h index 48b25d073e..a26330e6fe 100644 --- a/libs/pipboy/stm32_i2s.h +++ b/libs/pipboy/stm32_i2s.h @@ -19,9 +19,15 @@ #define I2S_DMA_BUFFER_SIZE 2048 // size of i2sDMAbuf (DMA direct to I2S) in u16 // 16kHz sample rate, 2xu16 = ~16Hz IRQ rate -#define I2S_RING_BUFFER_SIZE 16384 // size of ringbuffer used for audio input in u16 +#define I2S_RING_BUFFER_SIZE 8192 // size of ringbuffer used for audio input in u16 // 8192 seems fine to use - still enough for 8 DMA packets worth/0.5sec... +/* jswrap_pb_audioFrame sends data in 2048 byte chunks and STM32_I2S_AddSamples +starts playback at 3*I2S_DMA_BUFFER_SIZE. So I2S_RING_BUFFER_SIZE=8192 +is the least we can use, since any less and 3*I2S_DMA_BUFFER_SIZE would be +big enough that the next sample from jswrap_pb_audioFrame would fill the buffer */ + + typedef enum { STM32_I2S_STOPPED, STM32_I2S_PLAYING diff --git a/libs/pixljs/jswrap_pixljs.c b/libs/pixljs/jswrap_pixljs.c index 8609cd3a25..8f8bdeff2a 100644 --- a/libs/pixljs/jswrap_pixljs.c +++ b/libs/pixljs/jswrap_pixljs.c @@ -47,6 +47,7 @@ Class containing utility functions for "type" : "staticmethod", "class" : "Pixl", "name" : "getBatteryPercentage", + "deprecated" : true, "generate" : "jswrap_espruino_getBattery", "return" : ["int", "A percentage between 0 and 100" ] } @@ -519,6 +520,7 @@ type MenuNumberItem = { type MenuOptions = { title?: string; back?: () => void; + remove?: () => void; selected?: number; fontHeight?: number; scroll?: number; @@ -733,4 +735,4 @@ To remove the window, call `E.showAlert()` with no arguments. }*/ void jswrap_pixljs_powerusage(JsVar *devices) { jsvObjectSetChildAndUnLock(devices, "LCD", jsvNewFromInteger(lcdIsOn ? 170 : 20)); -} \ No newline at end of file +} diff --git a/libs/puckjs/jswrap_puck.c b/libs/puckjs/jswrap_puck.c index acbcc0085e..b285d04223 100644 --- a/libs/puckjs/jswrap_puck.c +++ b/libs/puckjs/jswrap_puck.c @@ -1349,6 +1349,7 @@ JsVarFloat jswrap_puck_light() { "class" : "Puck", "ifdef" : "PUCKJS", "name" : "getBatteryPercentage", + "deprecated" : true, "generate" : "jswrap_espruino_getBattery", "return" : ["int", "A percentage between 0 and 100" ] } diff --git a/scripts/common.py b/scripts/common.py index 23739b9111..da2d5a80dd 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -71,7 +71,9 @@ def f(*popenargs, **kwargs): # // EV_CUSTOM = Called whenever an event of type EV_CUSTOM is received (jswOnCustomEvent(event)) # // EV_xxx = Something to be called with a character in an IRQ when it is received (eg. EV_SERIAL1) (jswOnCharEvent) # // powerusage = fn(JsVar*) called with an object, and should insert fields for deviec names and estimated power usage in uA (jswGetPowerUsage) -# "class" : "Double", "name" : "doubleToIntBits", +# "class" : "Double", +# "name" : "doubleToIntBits", +# "deprecated" : "2v24", // mark that this may be removed in the future (version=when it was deprecated). Adds a comment to description # "needs_parentName":true, // optional - if for a method, this makes the first 2 args parent+parentName (not just parent) # "generate_full|generate|wrap" : "*(JsVarInt*)&x", // if generate=false, it'll only be used for docs # "generate_js" : "full/file/path.js", // you can supply a JS file instead of 'generate' above. Should be of the form '(function(args) { ... })' @@ -206,11 +208,15 @@ def get_jsondata(is_for_document, parseArgs = True, boardObject = False): try: jsondata = json.loads(jsonstring) if len(description): jsondata["description"] = description; + else: jsondata["description"] = "" jsondata["filename"] = jswrap if jswrap[-2:]==".c": jsondata["include"] = jswrap[:-2]+".h" jsondata["githublink"] = "https://github.com/espruino/Espruino/blob/"+githash+"/"+jswrap+"#L"+str(linenumber) + if "deprecated" in jsondata and not "deprecated" in jsondata["description"].lower(): + jsondata["description"] = "**DEPRECATED** - this will be removed in subsequent versions of Espruino\n\n" + jsondata["description"]; + dropped_prefix = "Dropped " if "name" in jsondata: dropped_prefix += jsondata["name"]+" " elif "class" in jsondata: dropped_prefix += jsondata["class"]+" " diff --git a/scripts/create_zip.sh b/scripts/create_zip.sh index ab188f4769..6c79e97365 100755 --- a/scripts/create_zip.sh +++ b/scripts/create_zip.sh @@ -39,8 +39,9 @@ echo Creating Documentation scripts/build_docs.py || { echo 'Build failed' ; exit 1; } mv $ESPRUINODIR/functions.html $ZIPDIR/functions.html -# Install everything -source scripts/provision.sh ALL +# Install all 'normal boards' +source scripts/provision.sh ESPRUINOBOARD +source scripts/provision.sh PIXLJS echo ------------------------------------------------------ echo Building Version $VERSION @@ -48,11 +49,23 @@ echo ------------------------------------------------------ # The following have been removed because it's too hard to keep the build going: # STM32F3DISCOVERY OLIMEXINO_STM32 HYSTM32_32 HYSTM32_28 HYSTM32_24 RAK8211 RAK8212 RUUVITAG THINGY52 RASPBERRYPI RAK5010 -for BOARDNAME in ESPRUINO_1V3 ESPRUINO_1V3_AT ESPRUINO_1V3_WIZ PICO_1V3 PICO_1V3_CC3000 PICO_1V3_WIZ ESPRUINOWIFI PUCKJS PUCKJS_MINIMAL PUCKJS_NETWORK PIXLJS PIXLJS_WIZ JOLTJS BANGLEJS BANGLEJS2 MDBT42Q NUCLEOF401RE NUCLEOF411RE STM32VLDISCOVERY STM32F4DISCOVERY STM32L496GDISCOVERY MICROBIT1 MICROBIT2 ESP8266_BOARD ESP8266_4MB ESP32 SMARTIBOT +for BOARDNAME in ESPRUINO_1V3 ESPRUINO_1V3_AT ESPRUINO_1V3_WIZ PICO_1V3 PICO_1V3_CC3000 PICO_1V3_WIZ ESPRUINOWIFI PUCKJS PUCKJS_MINIMAL PUCKJS_NETWORK PIXLJS PIXLJS_WIZ JOLTJS BANGLEJS BANGLEJS2 MDBT42Q NUCLEOF401RE NUCLEOF411RE STM32VLDISCOVERY STM32F4DISCOVERY STM32L496GDISCOVERY MICROBIT1 MICROBIT2 SMARTIBOT do scripts/create_zip_board.sh $BOARDNAME done +# Install Espressif stuff as it screws with Python + +source scripts/provision.sh ESP8266_4MB +source scripts/provision.sh ESP32 +source scripts/provision.sh ESP32C3_IDF4 + +for BOARDNAME in ESP8266_BOARD ESP8266_4MB ESP32 +do + scripts/create_zip_board.sh $BOARDNAME +done + + cd $ESPRUINODIR echo Copying README diff --git a/src/jsdevices.c b/src/jsdevices.c index d8db0d1c24..f0c1ca82dc 100644 --- a/src/jsdevices.c +++ b/src/jsdevices.c @@ -477,7 +477,7 @@ void jshPushIOCharEvents(IOEventFlags channel, char *data, unsigned int count) { for (i=0;ibaudRate = DEFAULT_BAUD_RATE; inf->pinRX = PIN_UNDEFINED; @@ -166,6 +169,11 @@ void jshKickSoftWatchDog() { } } +/// Called when we have had an event that means we should execute JS +void jshHadEvent() { + jshHadEventDuringSleep = true; +} + /* Returns the estimated power usage of the microcontroller */ __attribute__((weak)) void jsvGetProcessorPowerUsage(JsVar *devices) { // not implemented by default diff --git a/src/jsinteractive.c b/src/jsinteractive.c index f7a8829328..ea34361309 100644 --- a/src/jsinteractive.c +++ b/src/jsinteractive.c @@ -76,6 +76,7 @@ typedef enum { PT_TYPE_EVENT = 0x4000, // parse as JSON and create `E.on('packet', ...)` event PT_TYPE_FILE_SEND = 0x6000, // called before DATA, with {fn:"filename",s:123} PT_TYPE_DATA = 0x8000, // Sent after FILE_SEND with blocks of data for the file + PT_TYPE_FILE_RECV = 0xA000 // receive a file - returns a series of PT_TYPE_DATA packets, with a final zero length packet to end } PACKED_FLAGS PacketLengthFlags; /* Packets work as follows - introduced 2v25 @@ -137,6 +138,9 @@ JsErrorFlags lastJsErrorFlags = 0; ///< Compare with jsErrorFlags in order to re void jsiDebuggerLine(JsVar *line); #endif void jsiCheckErrors(); + +static void jsiPacketFileEnd(); +static void jsiPacketExit(); // ---------------------------------------------------------------------------- /** @@ -166,7 +170,11 @@ NO_INLINE bool jsiEcho() { } NO_INLINE bool jsiPasswordProtected() { +#ifndef ESPR_NO_PASSWORD return ((jsiStatus&JSIS_PASSWORD_PROTECTED)!=0); +#else + return 0; +#endif } static bool jsiShowInputLine() { @@ -773,6 +781,9 @@ void jsiDumpHardwareInitialisation(vcbprintf_callback user_callback, void *user_ // Used when shutting down before flashing // 'release' anything we are using, but ensure that it doesn't get freed void jsiSoftKill() { + // Close any open file transfers + jsiPacketFileEnd(); + jsiPacketExit(); // Execute `kill` events on `E` jsiExecuteEventCallbackOn("E", KILL_CALLBACK_NAME, 0, 0); jsiCheckErrors(); @@ -885,11 +896,13 @@ void jsiSemiInit(bool autoLoad, JsfFileName *loadedFilename) { jspSoftInit(); } +#ifndef ESPR_NO_PASSWORD // If a password was set, apply the lock JsVar *pwd = jsvObjectGetChildIfExists(execInfo.hiddenRoot, PASSWORD_VARIABLE_NAME); if (pwd) jsiStatus |= JSIS_PASSWORD_PROTECTED; jsvUnLock(pwd); +#endif // Softinit may run initialisation code that will overwrite defaults jsiSoftInit(!autoLoad); @@ -1319,9 +1332,8 @@ void jsiCheckErrors() { } } - -void jsiAppendStringToInputLine(const char *strToAppend) { - // Add the string to our input line +/// Add the given string to our input line +static void jsiAppendStringToInputLine(const char *strToAppend) { jsiIsAboutToEditInputLine(); size_t strSize = 1; @@ -1562,7 +1574,28 @@ void jsiHandleNewLine(bool execute) { } } -void jsiPacketFileEnd() { +/// Called 10s after PT_TYPE_FILE_SEND if no packets received +static void jsiPacketFileTimeoutHandler() { + jsiPacketFileEnd(); +} + +/// Clear and optionally create a new timeout for file reception errors +static void jsiPacketFileSetTimeout(bool createNew) { + // cancel timeout + JsVar *timeout = jsvObjectGetChildIfExists(execInfo.hiddenRoot, "PK_FTIMEOUT"); + if (timeout) { + jsiClearTimeout(timeout); + jsvUnLock(timeout); + } + // add new if needed + if (createNew) + jsvObjectSetChildAndUnLock(execInfo.hiddenRoot, "PK_FTIMEOUT", jsiSetTimeout(jsiPacketFileTimeoutHandler, 10000)); + else + jsvObjectRemoveChild(execInfo.hiddenRoot, "PK_FTIMEOUT"); +} + +/// Called when file transmission has finished (or when there's a timeout) +static void jsiPacketFileEnd() { #ifdef USE_FILESYSTEM JsVar *r = jsvObjectGetChildIfExists(execInfo.hiddenRoot, "PK_FILE"); if (r) { @@ -1577,13 +1610,15 @@ void jsiPacketFileEnd() { #endif // remove stored data jsvObjectRemoveChild(execInfo.hiddenRoot, "PK_FILE"); + // cancel timeout + jsiPacketFileSetTimeout(false); } -void jsiPacketExit() { +/// Called when packet reception is finished (or times out) +static void jsiPacketExit() { inputState = IPS_NONE; inputPacketLength = 0; // cancel timeout - // cancel timeout JsVar *timeout = jsvObjectGetChildIfExists(execInfo.hiddenRoot, "PK_TIMEOUT"); if (timeout) { jsiClearTimeout(timeout); @@ -1597,14 +1632,15 @@ void jsiPacketExit() { jsvObjectRemoveChild(execInfo.hiddenRoot, "PK_IL"); } -// Called 1s after SOH if Packet not complete -void jsiPacketTimeoutHandler() { +/// Called 1s after SOH if Packet not complete +static void jsiPacketTimeoutHandler() { jsiConsolePrintChar(ASCII_NAK); //jshTransmitPrintf(DEFAULT_CONSOLE_DEVICE, "Packet Timeout\n"); jsiPacketExit(); } -void jsiPacketStart() { +/// Called when packet reception starts - allocates data and adds a timeout +static void jsiPacketStart() { inputState = IPS_PACKET_TRANSFER_BYTE0; jsiInputLineCursorMoved(); // unlock iterator jsvObjectSetChildAndUnLock(execInfo.hiddenRoot, "PK_IL", inputLine); // back up old inputline @@ -1612,17 +1648,18 @@ void jsiPacketStart() { inputLine = jsvNewFromEmptyString(); } -void jsiPacketReply(JsVar *data) { // data should be a string - uint16_t len = PT_TYPE_RESPONSE | (uint16_t)jsvGetStringLength(data); // assume not more than 0x1FFF chars +/// Called to send a response packet +static void jsiPacketReply(PacketLengthFlags type, JsVar *data) { // data should be a string + uint16_t len = type | (uint16_t)jsvGetStringLength(data); // assume not more than 0x1FFF chars jsiConsolePrintChar(ASCII_DLE); jsiConsolePrintChar(ASCII_SOH); jsiConsolePrintChar((char)(len>>8)); jsiConsolePrintChar((char)(len&255)); - jsiConsolePrintStringVar(data); + if (data) jsiConsolePrintStringVar(data); } // Called when all data we need is in inputLine, inputPacketLength contains length and flags -void jsiPacketProcess() { +static void jsiPacketProcess() { PacketLengthFlags packetType = inputPacketLength & PT_TYPE_MASK; inputPacketLength &= PT_SIZE_MASK; if (packetType == PT_TYPE_EVAL) { @@ -1633,7 +1670,7 @@ void jsiPacketProcess() { } else { jsiConsolePrintChar(ASCII_ACK); JsVar *v = jswrap_espruino_toJS(result); - jsiPacketReply(v); + jsiPacketReply(PT_TYPE_RESPONSE, v); jsvUnLock(v); } jsvUnLock(result); @@ -1644,7 +1681,47 @@ void jsiPacketProcess() { ok = jsiExecuteEventCallbackOn("E", JS_EVENT_PREFIX"packet", 1, &r); jsvUnLock(r); jsiConsolePrintChar(ok ? ASCII_ACK : ASCII_NAK); + } else if (packetType == PT_TYPE_FILE_RECV) { + JsVar *r = jswrap_json_parse_liberal(inputLine, true/*no exceptions*/); + bool ok = jsvIsObject(r); + if (ok) { + JsVar *fn = jsvObjectGetChildIfExists(r,"fn"); + ok = jsvIsString(fn); +#ifdef USE_FILESYSTEM + if (ok && jsvObjectGetBoolChild(r,"fs")) { // it's a FS file - load and send packets + JsVar *fMode = jsvNewFromString("r"); + JsVar *f = jswrap_E_openFile(fn, fMode); + if (f) { + jsiConsolePrintChar(ASCII_ACK); + JsVar *d = jswrap_file_read(f, 1024); + while (d) { + jsiPacketReply(PT_TYPE_DATA, d); + jsvUnLock(d); + d = jswrap_file_read(f, 1024); + } + jswrap_file_close(f); + } else ok = false; + jsvUnLock2(fMode,f); + } else +#endif + { // it's a file in Storage, load + JsVar *f = jswrap_storage_read(fn, 0, 0); + if (f) { + jsiConsolePrintChar(ASCII_ACK); + size_t len = jsvGetStringLength(f); + for (size_t i=0;i= (inputPacketLength & PT_SIZE_MASK)) jsiPacketProcess(); @@ -1838,7 +1920,7 @@ void jsiHandleChar(char ch) { inputStateNumber = (uint16_t)(10*inputStateNumber + ch - '0'); } else { if (ch=='d') jsiLineNumberOffset = inputStateNumber; - else if (ch=='H' /* 75 */) { + else if (ch=='H' /* 72 */) { if (inputStateNumber==2) jsiClearInputLine(true); // Erase current line } else if (ch==126) { if (inputStateNumber==1) jsiHandleHome(); // Numpad Home diff --git a/src/jsinteractive.h b/src/jsinteractive.h index 4aff5f25d2..6a8db64eae 100644 --- a/src/jsinteractive.h +++ b/src/jsinteractive.h @@ -121,7 +121,7 @@ typedef enum { BUSY_INTERACTIVE = 1, BUSY_TRANSMIT = 2, // ??? = 4 -} JsiBusyDevice; +} PACKED_FLAGS JsiBusyDevice; /// Shows a busy indicator, if one is set up void jsiSetBusy(JsiBusyDevice device, bool isBusy); @@ -130,7 +130,7 @@ typedef enum { JSI_SLEEP_AWAKE = 0, JSI_SLEEP_ASLEEP = 1, JSI_SLEEP_DEEP = 2, -} JsiSleepType; +} PACKED_FLAGS JsiSleepType; /// Shows a sleep indicator, if one is set up void jsiSetSleep(JsiSleepType isSleep); @@ -142,7 +142,9 @@ void jsiSetSleep(JsiSleepType isSleep); #define DEVICE_OPTIONS_NAME "_options" #define INIT_CALLBACK_NAME JS_EVENT_PREFIX"init" ///< Callback for `E.on('init'` #define KILL_CALLBACK_NAME JS_EVENT_PREFIX"kill" ///< Callback for `E.on('kill'` +#ifndef ESPR_NO_PASSWORD #define PASSWORD_VARIABLE_NAME "pwd" +#endif typedef enum { JSIS_NONE, @@ -159,7 +161,7 @@ typedef enum { JSIS_TODO_MASK = JSIS_TODO_FLASH_SAVE|JSIS_TODO_FLASH_LOAD|JSIS_TODO_RESET, JSIS_CONSOLE_FORCED = 1<<8, ///< see jsiSetConsoleDevice JSIS_WATCHDOG_AUTO = 1<<9, ///< Automatically kick the watchdog timer on idle - JSIS_PASSWORD_PROTECTED = 1<<10, ///< Password protected + JSIS_PASSWORD_PROTECTED = 1<<10, ///< Password protected (only ifndef ESPR_NO_PASSWORD) JSIS_COMPLETELY_RESET = 1<<11, ///< Has the board powered on *having not loaded anything from flash* JSIS_FIRST_BOOT = 1<<12, ///< Is this the first time we started, or has load/reset/etc been called? diff --git a/src/jslex.c b/src/jslex.c index 7b63efc08f..181355ea19 100644 --- a/src/jslex.c +++ b/src/jslex.c @@ -41,6 +41,10 @@ void jslCharPosClone(JslCharPos *dstpos, JslCharPos *pos) { dstpos->currCh = pos->currCh; } +void jslCharPosClear(JslCharPos *pos) { + pos->it.var = 0; +} + void jslCharPosFromLex(JslCharPos *dstpos) { jsvStringIteratorClone(&dstpos->it, &lex->it); dstpos->currCh = lex->currCh; diff --git a/src/jslex.h b/src/jslex.h index e12cab1fe7..2ab5e61cdb 100644 --- a/src/jslex.h +++ b/src/jslex.h @@ -137,6 +137,7 @@ typedef struct JslCharPos { void jslCharPosFree(JslCharPos *pos); void jslCharPosClone(JslCharPos *dstpos, JslCharPos *pos); +void jslCharPosClear(JslCharPos *pos); ///< clear charpos (if was an undefined value) void jslCharPosFromLex(JslCharPos *dstpos); void jslCharPosNew(JslCharPos *dstpos, JsVar *src, size_t tokenStart); diff --git a/src/jsparse.c b/src/jsparse.c index a317cd6c65..63dc129943 100644 --- a/src/jsparse.c +++ b/src/jsparse.c @@ -2567,7 +2567,6 @@ NO_INLINE JsVar *jspeStatementDoOrWhile(bool isWhile) { JslCharPos whileCondStart; // We do repetition by pulling out the string representing our statement // there's definitely some opportunity for optimisation here - bool wasInLoop = (execInfo.execute&EXEC_IN_LOOP)!=0; JslCharPos whileBodyStart; if (isWhile) { // while loop @@ -2579,15 +2578,18 @@ NO_INLINE JsVar *jspeStatementDoOrWhile(bool isWhile) { jsvUnLock(cond); jslCharPosFromLex(&whileBodyStart); JSP_MATCH_WITH_CLEANUP_AND_RETURN(')',jslCharPosFree(&whileBodyStart);jslCharPosFree(&whileCondStart);,0); - } else { + } else { // do loop jslCharPosFromLex(&whileBodyStart); JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_R_DO, jslCharPosFree(&whileBodyStart);,0); + jslCharPosClear(&whileCondStart); } JSP_SAVE_EXECUTE(); // actually try and execute first bit of while loop (we'll do the rest in the actual loop later) if (!loopCond) jspSetNoExecute(); execInfo.execute |= EXEC_IN_LOOP; + bool needSemiColon = (!isWhile) && lex->tk!='{'; jsvUnLock(jspeBlockOrStatement()); + if (needSemiColon) JSP_MATCH_WITH_CLEANUP_AND_RETURN(';',jslCharPosFree(&whileBodyStart);jslCharPosFree(&whileCondStart);,0); // do statement; while(a--); if (!wasInLoop) execInfo.execute &= (JsExecFlags)~EXEC_IN_LOOP; hasHadBreak |= jspeCheckBreakContinue(); diff --git a/src/jsutils.h b/src/jsutils.h index 1319113d87..6e0550010c 100755 --- a/src/jsutils.h +++ b/src/jsutils.h @@ -58,6 +58,7 @@ #define ESPR_NO_SOFTWARE_I2C 1 #endif #define ESPR_NO_REGEX_OPTIMISE 1 +#define ESPR_NO_PASSWORD 1 #endif // SAVE_ON_FLASH #ifdef SAVE_ON_FLASH_EXTREME #define ESPR_NO_BLUETOOTH_MESSAGES 1 diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 235757690e..fad337c4ea 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -736,6 +736,7 @@ JsVar *jswrap_date_toString(JsVar *parent) { "type" : "method", "class" : "Date", "name" : "toUTCString", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_date_toUTCString", "return" : ["JsVar","A String"], "typescript" : "toUTCString(): string;" diff --git a/src/jswrap_espruino.c b/src/jswrap_espruino.c index 20fa1d03f7..d7ef62f23c 100644 --- a/src/jswrap_espruino.c +++ b/src/jswrap_espruino.c @@ -2072,6 +2072,7 @@ JsVar *jswrap_espruino_HSBtoRGB(JsVarFloat hue, JsVarFloat sat, JsVarFloat bri, "type" : "staticmethod", "class" : "E", "name" : "setPassword", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_espruino_setPassword", "params" : [ ["password","JsVar","The password - max 20 chars"] @@ -2093,25 +2094,30 @@ from unknown sources) or read the device's firmware then they may be able to obtain it. */ void jswrap_espruino_setPassword(JsVar *pwd) { +#ifndef ESPR_NO_PASSWORD if (pwd) pwd = jsvAsString(pwd); jsvUnLock(jsvObjectSetChild(execInfo.hiddenRoot, PASSWORD_VARIABLE_NAME, pwd)); +#endif } /*JSON{ "type" : "staticmethod", "class" : "E", "name" : "lockConsole", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_espruino_lockConsole" } If a password has been set with `E.setPassword()`, this will lock the console so the password needs to be entered to unlock it. */ void jswrap_espruino_lockConsole() { +#ifndef ESPR_NO_PASSWORD JsVar *pwd = jsvObjectGetChildIfExists(execInfo.hiddenRoot, PASSWORD_VARIABLE_NAME); if (pwd) jsiStatus |= JSIS_PASSWORD_PROTECTED; jsvUnLock(pwd); +#endif } /*JSON{ @@ -2601,6 +2607,7 @@ type PowerUsage = { "type" : "staticmethod", "class" : "E", "name" : "getPowerUsage", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_espruino_getPowerUsage", "return" : ["JsVar","An object detailing power usage in microamps"], "typescript" : "getPowerUsage(): PowerUsage;" diff --git a/src/jswrap_interactive.c b/src/jswrap_interactive.c index dde3b1eb49..c3164ff7c4 100644 --- a/src/jswrap_interactive.c +++ b/src/jswrap_interactive.c @@ -347,6 +347,7 @@ void jswrap_interactive_setTime(JsVarFloat time) { /*JSON{ "type" : "function", "name" : "getSerial", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_interface_getSerial", "return" : ["JsVar","The board's serial number"] } diff --git a/src/jswrap_math.c b/src/jswrap_math.c index 2652e3184f..af13f791ed 100644 --- a/src/jswrap_math.c +++ b/src/jswrap_math.c @@ -101,6 +101,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "LN2", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "0.6931471805599453", "return" : ["float","The natural logarithm of 2 - 0.6931471805599453"] }*/ @@ -108,6 +109,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "LN10", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "2.302585092994046", "return" : ["float","The natural logarithm of 10 - 2.302585092994046"] }*/ @@ -115,6 +117,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "LOG2E", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "1.4426950408889634", "return" : ["float","The base 2 logarithm of e - 1.4426950408889634"] }*/ @@ -122,6 +125,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "LOG10E", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "0.4342944819032518", "return" : ["float","The base 10 logarithm of e - 0.4342944819032518"] }*/ @@ -129,6 +133,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "SQRT2", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "1.4142135623730951", "return" : ["float","The square root of 2 - 1.4142135623730951"] }*/ @@ -136,6 +141,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "SQRT1_2", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "0.7071067811865476", "return" : ["float","The square root of 1/2 - 0.7071067811865476"] }*/ diff --git a/src/jswrap_number.c b/src/jswrap_number.c index f02290c659..106116b8d7 100644 --- a/src/jswrap_number.c +++ b/src/jswrap_number.c @@ -144,6 +144,8 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { /*JSON{ "type" : "variable", "name" : "HIGH", + "ifndef" : "SAVE_ON_FLASH", + "deprecated" : true, "generate_full" : "1", "return" : ["int32","Logic 1 for Arduino compatibility - this is the same as just typing `1`"], "typescript" : "declare const HIGH: true;" @@ -152,6 +154,8 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { /*JSON{ "type" : "variable", "name" : "LOW", + "ifndef" : "SAVE_ON_FLASH", + "deprecated" : true, "generate_full" : "0", "return" : ["int32","Logic 0 for Arduino compatibility - this is the same as just typing `0`"], "typescript" : "declare const LOW: false;" diff --git a/src/jswrap_serial.c b/src/jswrap_serial.c index 29774b5d95..da3690e00c 100644 --- a/src/jswrap_serial.c +++ b/src/jswrap_serial.c @@ -104,6 +104,8 @@ Espruino boards) "type" : "staticmethod", "class" : "Serial", "name" : "find", + "deprecated" : true, + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_USART1, JSH_USARTMAX, pin)", "params" : [ ["pin","pin","A pin to search with"] diff --git a/src/jswrap_spi_i2c.c b/src/jswrap_spi_i2c.c index dc1c417ad4..cc627f939f 100644 --- a/src/jswrap_spi_i2c.c +++ b/src/jswrap_spi_i2c.c @@ -73,6 +73,8 @@ JsVar *jswrap_spi_constructor() { "type" : "staticmethod", "class" : "SPI", "name" : "find", + "deprecated" : true, + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_SPI1, JSH_SPIMAX, pin)", "params" : [ ["pin","pin","A pin to search with"] @@ -532,6 +534,8 @@ JsVar *jswrap_i2c_constructor() { "type" : "staticmethod", "class" : "I2C", "name" : "find", + "deprecated" : true, + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_I2C1, JSH_I2CMAX, pin)", "params" : [ ["pin","pin","A pin to search with"] diff --git a/targetlibs/stm32legacyusb/usb_endp.c b/targetlibs/stm32legacyusb/usb_endp.c index a9752b18d7..149057307d 100644 --- a/targetlibs/stm32legacyusb/usb_endp.c +++ b/targetlibs/stm32legacyusb/usb_endp.c @@ -142,6 +142,7 @@ void EP3_OUT_Callback(void) NAKed till the end of the USART Xfer */ jshPushIOCharEvents(EV_USBSERIAL, USB_Rx_Buffer, USB_Rx_Cnt); + jshHadEvent(); /* Enable the receive of data on EP3 */ SetEPRxStatus(ENDP3, jshHasEventSpaceForChars(VIRTUAL_COM_PORT_DATA_SIZE) ? EP_RX_VALID : EP_RX_NAK); diff --git a/targetlibs/stm32usb/usbd_cdc_hid.c b/targetlibs/stm32usb/usbd_cdc_hid.c index 217ea9c166..0cecab8afa 100644 --- a/targetlibs/stm32usb/usbd_cdc_hid.c +++ b/targetlibs/stm32usb/usbd_cdc_hid.c @@ -633,6 +633,7 @@ static uint8_t USBD_CDC_HID_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum) if (handle) { // Process data jshPushIOCharEvents(EV_USBSERIAL, (char*)handle->cdcRX, rxLength); + jshHadEvent(); // Set CDC_READ_WAIT_EMPTY flag - we'll re-enable USB RX using // USBD_LL_PrepareReceive ONLY when we have enough space diff --git a/targets/nrf5x/jshardware.c b/targets/nrf5x/jshardware.c index e18955b1b9..d605e6ed31 100644 --- a/targets/nrf5x/jshardware.c +++ b/targets/nrf5x/jshardware.c @@ -235,7 +235,7 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst, /*Get amount of data transfered*/ size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm); jshPushIOCharEvents(EV_USBSERIAL, m_rx_buffer, size); - + jshHadEvent(); /*Setup next transfer*/ ret = app_usbd_cdc_acm_read(&m_app_cdc_acm, @@ -309,7 +309,6 @@ static uint8_t pwmClocks[PWM_COUNTERS]; /// For flash - whether it is busy or not... volatile bool flashIsBusy = false; -volatile bool hadEvent = false; // set if we've had an event we need to deal with unsigned int ticksSinceStart = 0; #if GPIO_COUNT>1 @@ -626,12 +625,6 @@ const nrf_drv_twis_t *jshGetTWIS(IOEventFlags device) { } #endif - -/// Called when we have had an event that means we should execute JS -void jshHadEvent() { - hadEvent = true; -} - void TIMER1_IRQHandler(void) { nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_CLEAR); nrf_timer_event_clear(NRF_TIMER1, NRF_TIMER_EVENT_COMPARE0); @@ -1678,7 +1671,6 @@ static void jsvPinWatchHandler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t a lastHandledPinState = !lastHandledPinState; IOEventFlags evt = jshGetEventFlagsForWatchedPin(pin); jshPushIOWatchEvent(evt); - jshHadEvent(); } @@ -2212,7 +2204,6 @@ static void twis_event_handler(nrf_drv_twis_evt_t const * const p_event) break; case TWIS_EVT_READ_DONE: jshPushIOEvent(EV_I2C1, twisAddr|0x80|(p_event->data.tx_amount<<8)); // send event to indicate a read - jshHadEvent(); twisAddr += p_event->data.tx_amount; break; case TWIS_EVT_WRITE_REQ: @@ -2224,7 +2215,6 @@ static void twis_event_handler(nrf_drv_twis_evt_t const * const p_event) twisAddr = twisRxBuf[0]; if (p_event->data.rx_amount>1) { jshPushIOEvent(EV_I2C1, twisAddr|((p_event->data.rx_amount-1)<<8)); // send event to indicate a write - jshHadEvent(); JsVar *i2c = jsvObjectGetChildIfExists(execInfo.root,"I2C1"); if (i2c) { JsVar *buf = jsvObjectGetChildIfExists(i2c,"buffer"); @@ -2746,7 +2736,7 @@ bool jshSleep(JsSysTime timeUntilWake) { #endif } jsiSetSleep(JSI_SLEEP_ASLEEP); - while (!hadEvent) { + while (!jshHadEventDuringSleep) { #ifdef NRF52_SERIES /* * Clear FPU exceptions. @@ -2765,7 +2755,7 @@ bool jshSleep(JsSysTime timeUntilWake) { while (app_usbd_event_queue_process()); /* Nothing to do */ #endif } - hadEvent = false; + jshHadEventDuringSleep = false; jsiSetSleep(JSI_SLEEP_AWAKE); #ifdef BLUETOOTH // we don't care about the return codes... @@ -2928,12 +2918,10 @@ void COMP_LPCOMP_IRQHandler() { if (nrf_lpcomp_event_check(NRF_LPCOMP_EVENT_UP) && nrf_lpcomp_int_enable_check(LPCOMP_INTENSET_UP_Msk)) { nrf_lpcomp_event_clear(NRF_LPCOMP_EVENT_UP); jshPushIOEvent(EV_CUSTOM, EVC_LPCOMP | EVC_DATA_LPCOMP_UP); - jshHadEvent(); } if (nrf_lpcomp_event_check(NRF_LPCOMP_EVENT_DOWN) && nrf_lpcomp_int_enable_check(LPCOMP_INTENSET_DOWN_Msk)) { nrf_lpcomp_event_clear(NRF_LPCOMP_EVENT_DOWN); jshPushIOEvent(EV_CUSTOM, EVC_LPCOMP); - jshHadEvent(); } } diff --git a/targets/stm32/jshardware.c b/targets/stm32/jshardware.c index f06ce8a34e..678b332bc7 100644 --- a/targets/stm32/jshardware.c +++ b/targets/stm32/jshardware.c @@ -81,6 +81,9 @@ JsSysTime jshGetRTCSystemTime(); static JsSysTime jshGetTimeForSecond(); +/// Max time we can sleep in JsSysTime units for the watchdog timer - we need this so we don't get rebooted it auto kicking is enabled +uint32_t watchdogSleepMax; + // The amount of systicks for one second depends on the clock speed #define SYSTICKS_FOR_ONE_SECOND (1+(CLOCK_SPEED_MHZ*1000000/SYSTICK_RANGE)) @@ -1551,7 +1554,7 @@ void jshIdle() { if (wasUSBConnected != USBConnected) { wasUSBConnected = USBConnected; if (USBConnected) - jshClearUSBIdleTimeout(); + jshUSBReceiveLastActive = JSH_USB_MAX_INACTIVITY_TICKS; // set to max so we're not connected until the first data request if (USBConnected && jsiGetConsoleDevice()!=EV_LIMBO) { if (!jsiIsConsoleDeviceForced()) jsiSetConsoleDevice(EV_USBSERIAL, false); @@ -2692,6 +2695,8 @@ void jshClearUSBIdleTimeout() { /// Enter simple sleep mode (can be woken up by interrupts). Returns true on success bool jshSleep(JsSysTime timeUntilWake) { + bool isAutoWDT = jsiStatus & JSIS_WATCHDOG_AUTO; + #ifdef USE_RTC /* TODO: Check jsiGetConsoleDevice to make sure we don't have to wake on USART (we can't do this fast enough) @@ -2706,7 +2711,7 @@ bool jshSleep(JsSysTime timeUntilWake) { #else (timeUntilWake > (jshGetTimeForSecond()*16*2/jshRTCPrescaler)) && // if there's less time that this then we can't go to sleep because we can't be sure we'll wake in time #endif - !jstUtilTimerIsRunning() && // if the utility timer is running (eg. digitalPulse, Waveform output, etc) then that would stop + !jstUtilTimerIsRunning() && // if the utility timer is running (eg. digitalPulse, Waveform output, etc) then that would stop so we can't sleep !jshHasTransmitData() && // if we're transmitting, we don't want USART/etc to get slowed down #ifdef USB !USB_IsConnected() && @@ -2729,93 +2734,106 @@ bool jshSleep(JsSysTime timeUntilWake) { ADC_Cmd(ADC4, DISABLE); // ADC off #endif #ifdef USB - jshSetUSBPower(false); + jshSetUSBPower(false); // WARNING: takes 25ms + bool wokenByUSB = false; #endif // USB + do { // we loop here so we can half-wake to kick the WDT without incurring wait for USB + JsSysTime timeToSleep = timeUntilWake; + // Don't sleep so long the WDT goes off! + if (isAutoWDT && timeToSleep>watchdogSleepMax) + timeToSleep = watchdogSleepMax; + // if JSSYSTIME_MAX we just sleep as long as possible unless woken by something else + if (timeUntilWake!=JSSYSTIME_MAX) + timeUntilWake -= timeToSleep; + if (isAutoWDT) jshKickWatchDog(); /* Add EXTI for Serial port */ //jshPinWatch(JSH_PORTA_OFFSET+10, true); /* add exti for USB */ #ifdef USB #ifdef STM32F1 - // USB has 15k pull-down resistors (and STM32 has 40k pull up) - Pin usbPin = JSH_PORTA_OFFSET+11; - jshPinSetState(usbPin, JSHPINSTATE_GPIO_IN_PULLUP); - Pin oldWatch = watchedPins[pinInfo[usbPin].pin]; - jshPinWatch(usbPin, true, JSPW_NONE); + // USB has 15k pull-down resistors (and STM32 has 40k pull up) + Pin usbPin = JSH_PORTA_OFFSET+11; + jshPinSetState(usbPin, JSHPINSTATE_GPIO_IN_PULLUP); + Pin oldWatch = watchedPins[pinInfo[usbPin].pin]; + jshPinWatch(usbPin, true, JSPW_NONE); #endif #ifdef USB_VSENSE_PIN - // USB_VSENSE_PIN is connected to USB 5v (and pulled down by a 100k resistor) - // ... so wake up if it goes high - Pin oldWatch = watchedPins[pinInfo[USB_VSENSE_PIN].pin]; - jshPinWatch(USB_VSENSE_PIN, true, JSPW_NONE); + // USB_VSENSE_PIN is connected to USB 5v (and pulled down by a 100k resistor) + // ... so wake up if it goes high + Pin oldWatch = watchedPins[pinInfo[USB_VSENSE_PIN].pin]; + jshPinWatch(USB_VSENSE_PIN, true, JSPW_NONE); #endif #endif // USB - if (timeUntilWake!=JSSYSTIME_MAX) { // set alarm - unsigned int ticks = (unsigned int)(timeUntilWake/jshGetTimeForSecond()); // ensure we round down and leave a little time + if (timeToSleep!=JSSYSTIME_MAX) { // set alarm + unsigned int ticks = (unsigned int)(timeToSleep/jshGetTimeForSecond()); // ensure we round down and leave a little time #ifdef STM32F1 - /* If we're going asleep for more than a few seconds, - * add one second to the sleep time so that when we - * wake up, we execute our timer immediately (even if it is a bit late) - * and don't waste power in shallow sleep. This is documented in setInterval */ - if (ticks>3) ticks++; // sleep longer than we need - - RTC_SetAlarm(RTC_GetCounter() + ticks); - RTC_ITConfig(RTC_IT_ALR, ENABLE); - //RTC_AlarmCmd(RTC_Alarm_A, ENABLE); - RTC_WaitForLastTask(); + /* If we're going asleep for more than a few seconds, + * add one second to the sleep time so that when we + * wake up, we execute our timer immediately (even if it is a bit late) + * and don't waste power in shallow sleep. This is documented in setInterval */ + if (ticks>3) ticks++; // sleep longer than we need + + RTC_SetAlarm(RTC_GetCounter() + ticks); + RTC_ITConfig(RTC_IT_ALR, ENABLE); + //RTC_AlarmCmd(RTC_Alarm_A, ENABLE); + RTC_WaitForLastTask(); #else // If available, just use the WakeUp counter - if (ticks < ((65536*16) / jshRTCPrescaler)) { - // if the delay is small enough, clock the WakeUp counter faster so we can sleep more accurately - RTC_WakeUpClockConfig(RTC_WakeUpClock_RTCCLK_Div16); - ticks = (unsigned int)((timeUntilWake*jshRTCPrescaler) / (jshGetTimeForSecond()*16)); - } else { // wakeup in seconds - RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); - if (ticks > 65535) ticks = 65535; - } - RTC_SetWakeUpCounter(ticks - 1); // 0 based - RTC_ITConfig(RTC_IT_WUT, ENABLE); - RTC_WakeUpCmd(ENABLE); - RTC_ClearFlag(RTC_FLAG_WUTF); + if (ticks < ((65536*16) / jshRTCPrescaler)) { + // if the delay is small enough, clock the WakeUp counter faster so we can sleep more accurately + RTC_WakeUpClockConfig(RTC_WakeUpClock_RTCCLK_Div16); + ticks = (unsigned int)((timeToSleep*jshRTCPrescaler) / (jshGetTimeForSecond()*16)); + } else { // wakeup in seconds + RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); + if (ticks > 65535) ticks = 65535; + } + RTC_SetWakeUpCounter(ticks - 1); // 0 based + RTC_ITConfig(RTC_IT_WUT, ENABLE); + RTC_WakeUpCmd(ENABLE); + RTC_ClearFlag(RTC_FLAG_WUTF); #endif - } - // set flag in case there happens to be a SysTick - hasSystemSlept = true; - // ----------------------------------------------- + } + // set flag in case there happens to be a SysTick + hasSystemSlept = true; + // ----------------------------------------------- #ifdef STM32F4 - /* FLASH Deep Power Down Mode enabled */ - PWR_FlashPowerDownCmd(ENABLE); + /* FLASH Deep Power Down Mode enabled */ + PWR_FlashPowerDownCmd(ENABLE); #endif - /* Request to enter STOP mode with regulator in low power mode*/ - PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); - // ----------------------------------------------- - if (timeUntilWake!=JSSYSTIME_MAX) { // disable alarm + /* Request to enter STOP mode with regulator in low power mode*/ + PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); + // ----------------------------------------------- + if (timeToSleep!=JSSYSTIME_MAX) { // disable alarm #ifdef STM32F1 - RTC_ITConfig(RTC_IT_ALR, DISABLE); - //RTC_AlarmCmd(RTC_Alarm_A, DISABLE); + RTC_ITConfig(RTC_IT_ALR, DISABLE); + //RTC_AlarmCmd(RTC_Alarm_A, DISABLE); #else - RTC_ITConfig(RTC_IT_WUT, DISABLE); - RTC_WakeUpCmd(DISABLE); + RTC_ITConfig(RTC_IT_WUT, DISABLE); + RTC_WakeUpCmd(DISABLE); #endif - } + } #ifdef USB - bool wokenByUSB = false; + wokenByUSB = false; #ifdef STM32F1 - wokenByUSB = jshPinGetValue(usbPin)==0; - // remove watches on pins - jshPinWatch(usbPin, false, JSPW_NONE); - if (oldWatch!=PIN_UNDEFINED) jshPinWatch(oldWatch, true, JSPW_NONE); - jshPinSetState(usbPin, JSHPINSTATE_GPIO_IN); + wokenByUSB = jshPinGetValue(usbPin)==0; + // remove watches on pins + jshPinWatch(usbPin, false, JSPW_NONE); + if (oldWatch!=PIN_UNDEFINED) jshPinWatch(oldWatch, true, JSPW_NONE); + jshPinSetState(usbPin, JSHPINSTATE_GPIO_IN); #endif #ifdef USB_VSENSE_PIN - // remove watch and restore old watch if there was one - // setting that we've woken lets the board stay awake - // until a USB connection can be established - if (jshPinGetValue(USB_VSENSE_PIN)) wokenByUSB=true; - jshPinWatch(USB_VSENSE_PIN, false, JSPW_NONE); - if (oldWatch!=PIN_UNDEFINED) jshPinWatch(oldWatch, true, JSPW_NONE); + // remove watch and restore old watch if there was one + // setting that we've woken lets the board stay awake + // until a USB connection can be established + if (jshPinGetValue(USB_VSENSE_PIN)) wokenByUSB=true; + jshPinWatch(USB_VSENSE_PIN, false, JSPW_NONE); + if (oldWatch!=PIN_UNDEFINED) jshPinWatch(oldWatch, true, JSPW_NONE); #endif + } while (timeUntilWake>0 && !wokenByUSB && !jshHadEventDuringSleep); + jshHadEventDuringSleep = false; + if (isAutoWDT) jshKickWatchDog(); #endif // recover oscillator RCC_HSEConfig(RCC_HSE_ON); @@ -2827,7 +2845,7 @@ bool jshSleep(JsSysTime timeUntilWake) { } RTC_WaitForSynchro(); // make sure any RTC reads will be done #ifdef USB - jshSetUSBPower(true); + jshSetUSBPower(true); // WARNING: takes 3ms if (wokenByUSB) jshLastWokenByUSB = jshGetRTCSystemTime(); #endif @@ -2837,6 +2855,11 @@ bool jshSleep(JsSysTime timeUntilWake) { if (timeUntilWake > jshGetTimeFromMilliseconds(ESPR_MIN_WFI_TIME_MS)) { /* don't bother sleeping if the time period is so low we * might miss the timer */ + + // Dont' sleep too long if auto WDT enabled (we'll kick when we go around idle loop) + if (isAutoWDT && timeUntilWake > watchdogSleepMax) + timeUntilWake = watchdogSleepMax; + JsSysTime sysTickTime; #ifdef USE_RTC sysTickTime = expectedSysTickTime*5/4; @@ -2856,6 +2879,7 @@ bool jshSleep(JsSysTime timeUntilWake) { #endif __WFI(); // Wait for Interrupt jsiSetSleep(JSI_SLEEP_AWAKE); + jshHadEventDuringSleep = false; /* We may have woken up before the wakeup event. If so then make sure we clear the event */ @@ -3003,6 +3027,9 @@ void jshEnableWatchDog(JsVarFloat timeout) { /* Enable IWDG (the LSI oscillator will be enabled by hardware) */ IWDG_Enable(); + + // save timeout so when we sleep we don't sleep so long we get rebooted (use wdt time / 2) + watchdogSleepMax = (uint32_t)jshGetTimeFromMilliseconds(timeout*1000 / 2); } // Kick the watchdog diff --git a/targets/stm32/stm32_it.c b/targets/stm32/stm32_it.c index a8fcf6baa6..3143ccbaca 100644 --- a/targets/stm32/stm32_it.c +++ b/targets/stm32/stm32_it.c @@ -304,6 +304,7 @@ NO_INLINE static void USART_IRQHandler(USART_TypeDef *USART, IOEventFlags device if (jshIsSerial7Bit(device)) ch &= 0x7F; /* Put it in our queue */ jshPushIOCharEvent(device, ch); + jshHadEvent(); } /* If overrun condition occurs, clear the ORE flag and recover communication */ if (USART_GetFlagStatus(USART, USART_FLAG_ORE) != RESET) { diff --git a/targets/stm32_boot/utils.c b/targets/stm32_boot/utils.c index ab36a1db80..2cc6add9d9 100644 --- a/targets/stm32_boot/utils.c +++ b/targets/stm32_boot/utils.c @@ -124,6 +124,8 @@ void jshDelayMicroseconds(int c) { void jshClearUSBIdleTimeout() { } +void jshHadEvent() {} + int _getc() { if (rxHead == rxTail) return -1; unsigned char d = (unsigned char)rxBuffer[rxTail]; diff --git a/targets/stm32_ll/stm32_it.c b/targets/stm32_ll/stm32_it.c index eb4916aa2f..3967e8b94c 100644 --- a/targets/stm32_ll/stm32_it.c +++ b/targets/stm32_ll/stm32_it.c @@ -272,6 +272,7 @@ static void USART_IRQHandler(USART_TypeDef *USART, IOEventFlags device) { if (jshIsSerial7Bit(device)) ch &= 0x7F; /* Put it in our queue */ jshPushIOCharEvent(device, ch); + jshHadEvent(); } /* If overrun condition occurs, clear the ORE flag and recover communication */ if (LL_USART_IsActiveFlag_ORE(USART) != RESET) diff --git a/tests/test_graphics_wrapString.js b/tests/test_graphics_wrapString.js index b742e44ebe..7e6765f756 100644 --- a/tests/test_graphics_wrapString.js +++ b/tests/test_graphics_wrapString.js @@ -85,6 +85,11 @@ g.clear().setFont("4x6:2"); lines = g.wrapString("Hello there lots of text here", 64); SHOULD_BE(lines, ["Hello","there","lots of","text","here"]); +// char at end missing: https://github.com/espruino/Espruino/issues/2572 +g.clear().setFont("4x6"); +lines = g.wrapString('test.a', 100); +SHOULD_BE(lines, ["test.a"]); + // wrap string correctly when an image is inline var g = Graphics.createArrayBuffer(32,16,8); g.clear().setFont("4x6");