From 529b396fec1633269144bf92e2e8d3e0d965a30b Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 27 Nov 2024 16:18:40 +0000 Subject: [PATCH 01/24] STM32: Ensure we kick the WDT if auto kicking is enabled and in deep sleep (avoids having to do it manually and wait 30ms for USB to wake up/shut down) --- ChangeLog | 1 + src/jshardware.h | 7 +- src/jshardware_common.c | 8 ++ targets/nrf5x/jshardware.c | 11 +-- targets/stm32/jshardware.c | 150 +++++++++++++++++++++---------------- 5 files changed, 102 insertions(+), 75 deletions(-) diff --git a/ChangeLog b/ChangeLog index 707634df9..b1af537f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -62,6 +62,7 @@ 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) 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/src/jshardware.h b/src/jshardware.h index d20d519e7..186ad9a1b 100644 --- a/src/jshardware.h +++ b/src/jshardware.h @@ -371,13 +371,14 @@ void jshResetRTCTimer(); void jshClearUSBIdleTimeout(); #endif -#if defined(NRF51_SERIES) || defined(NRF52_SERIES) /// Called when we have had an event that means we should execute JS extern void jshHadEvent(); +/// set if we've had an event we need to deal with +extern volatile bool jshHadEventDuringSleep; + +#if defined(NRF51_SERIES) || defined(NRF52_SERIES) /// Enable/disable(if level==NAN) the LPCOMP comparator bool jshSetComparator(Pin pin, JsVarFloat level); -#else -#define jshHadEvent() /* We should ensure we exit idle mode */ #endif /// the temperature from the internal temperature sensor, in degrees C diff --git a/src/jshardware_common.c b/src/jshardware_common.c index aeb3bd302..2856dd576 100644 --- a/src/jshardware_common.c +++ b/src/jshardware_common.c @@ -15,6 +15,9 @@ #include "jsinteractive.h" #include "platform_config.h" +/// set if we've had an event we need to deal with +volatile bool jshHadEventDuringSleep = false; + void jshUSARTInitInfo(JshUSARTInfo *inf) { inf->baudRate = 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/targets/nrf5x/jshardware.c b/targets/nrf5x/jshardware.c index e18955b1b..6f9137e84 100644 --- a/targets/nrf5x/jshardware.c +++ b/targets/nrf5x/jshardware.c @@ -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); @@ -2746,7 +2739,7 @@ bool jshSleep(JsSysTime timeUntilWake) { #endif } jsiSetSleep(JSI_SLEEP_ASLEEP); - while (!hadEvent) { + while (!jshHadEventDuringSleep) { #ifdef NRF52_SERIES /* * Clear FPU exceptions. @@ -2765,7 +2758,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... diff --git a/targets/stm32/jshardware.c b/targets/stm32/jshardware.c index f06ce8a34..0aa429137 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)) @@ -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,104 @@ 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; + if (isAutoWDT && timeToSleep>watchdogSleepMax) + timeToSleep = watchdogSleepMax; + if (timeUntilWake==JSSYSTIME_MAX) timeUntilWake = 0; // if we're just waiting for as long as possible + else 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 +2843,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 +2853,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; @@ -3003,6 +3024,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 From 76e707fed7b9936d950ce7f2718f2e42d34c842f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 27 Nov 2024 16:49:51 +0000 Subject: [PATCH 02/24] Remove jshHadEvent duplication by moving it into jshPushIOEvent and jshPushIOWatchEvent --- libs/bluetooth/bluetooth_utils.c | 2 -- src/jsdevices.c | 5 +++-- src/jsdevices.h | 5 +++-- targetlibs/stm32legacyusb/usb_endp.c | 1 + targetlibs/stm32usb/usbd_cdc_hid.c | 1 + targets/nrf5x/jshardware.c | 7 +------ targets/stm32/jshardware.c | 7 +++++-- targets/stm32/stm32_it.c | 1 + targets/stm32_ll/stm32_it.c | 1 + 9 files changed, 16 insertions(+), 14 deletions(-) diff --git a/libs/bluetooth/bluetooth_utils.c b/libs/bluetooth/bluetooth_utils.c index f15c1a629..375cd4623 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/src/jsdevices.c b/src/jsdevices.c index d8db0d1c2..f0c1ca82d 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;icdcRX, 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 6f9137e84..d605e6ed3 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, @@ -1671,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(); } @@ -2205,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: @@ -2217,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"); @@ -2921,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 0aa429137..33c3fd2e1 100644 --- a/targets/stm32/jshardware.c +++ b/targets/stm32/jshardware.c @@ -2740,10 +2740,12 @@ bool jshSleep(JsSysTime timeUntilWake) { 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 (timeUntilWake==JSSYSTIME_MAX) timeUntilWake = 0; // if we're just waiting for as long as possible - else timeUntilWake -= timeToSleep; + // 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); @@ -2877,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 */ diff --git a/targets/stm32/stm32_it.c b/targets/stm32/stm32_it.c index a8fcf6baa..3143ccbac 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_ll/stm32_it.c b/targets/stm32_ll/stm32_it.c index eb4916aa2..3967e8b94 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) From 46ab342eb4fb652d889236cfe3bab6988b466c9e Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 28 Nov 2024 12:24:13 +0000 Subject: [PATCH 03/24] Allow a 'file receive' packet which can request Espruino sends a file as binary packets (also fix files not being closed if transmission fails) --- ChangeLog | 1 + src/jsinteractive.c | 96 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index b1af537f9..337003bd6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -63,6 +63,7 @@ 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) 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/src/jsinteractive.c b/src/jsinteractive.c index f7a882932..0ad85621d 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(); // ---------------------------------------------------------------------------- /** @@ -773,6 +777,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(); @@ -1562,7 +1569,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 +1605,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 +1627,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 +1643,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 +1665,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 +1676,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 Date: Thu, 28 Nov 2024 20:03:34 +0000 Subject: [PATCH 04/24] Fix for potential 'Execution Interrupted' when uploading binary data containing char code 3 --- src/jsinteractive.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jsinteractive.c b/src/jsinteractive.c index 0ad85621d..f76d21f43 100644 --- a/src/jsinteractive.c +++ b/src/jsinteractive.c @@ -1820,6 +1820,9 @@ void jsiHandleChar(char ch) { return; } + if (ch==3 && IS_PACKET_TRANSFER(inputState)) + execInfo.execute &= ~EXEC_CTRL_C_MASK; // if we got Ctrl-C, ignore it + if (inputState == IPS_PACKET_TRANSFER_BYTE0) { if (jsvGetStringLength(inputLine)==0) jsiStatus &= ~JSIS_ECHO_OFF_FOR_LINE; // turn on echo (because it'd have been turned off by DLE on an empty line) @@ -1832,8 +1835,6 @@ void jsiHandleChar(char ch) { else inputState = IPS_PACKET_TRANSFER_DATA; } else if (inputState == IPS_PACKET_TRANSFER_DATA) { - if (ch==3) - execInfo.execute &= ~EXEC_CTRL_C; // if we got Ctrl-C, ignore it jsiAppendToInputLine(ch); if (inputLineLength >= (inputPacketLength & PT_SIZE_MASK)) jsiPacketProcess(); From d6e543beec98b55cb4d00bed39cb9dab7fd53790 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 28 Nov 2024 20:49:26 +0000 Subject: [PATCH 05/24] fix code comments --- src/jsinteractive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jsinteractive.c b/src/jsinteractive.c index f76d21f43..8f49f8ba9 100644 --- a/src/jsinteractive.c +++ b/src/jsinteractive.c @@ -1791,7 +1791,7 @@ void jsiHandleChar(char ch) { // 27 then 91 then 48-57 (numeric digits) then 'd' - set line number, used for that // inputLine and put into any declared functions // 27 then 91 then 49 ('1') then 126 - numpad home - // 27 then 91 then 50 ('2') then 75 - Erases the entire current line. + // 27 then 91 then 50 ('2') then 72 - Erases the entire current line. // 27 then 91 then 51 ('3') then 126 - backwards delete // 27 then 91 then 52 ('4') then 126 - numpad end // 27 then 91 then 53 ('5') then 126 - pgup @@ -1913,7 +1913,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 From 77110ad1c07612a46d4ed8ace97349fa8b0f88e2 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 2 Dec 2024 16:41:30 +0000 Subject: [PATCH 06/24] Fix parsing of semicolons in DO with a statement: `do print(a);while(a--);` --- ChangeLog | 1 + src/jslex.c | 4 ++++ src/jslex.h | 1 + src/jsparse.c | 6 ++++-- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 337003bd6..b06d35c98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -64,6 +64,7 @@ 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--);` 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/src/jslex.c b/src/jslex.c index 7b63efc08..181355ea1 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 e12cab1fe..2ab5e61cd 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 a317cd6c6..63dc12994 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(); From 71f7ca0e55e661b217dbdd519da0be570fe34f10 Mon Sep 17 00:00:00 2001 From: MaBecker Date: Tue, 3 Dec 2024 08:15:00 +0100 Subject: [PATCH 07/24] ES32: add setIP and setAPIP --- ChangeLog | 1 + libs/network/esp32/jswrap_esp32_network.c | 99 +++++++++++++++++++++++ libs/network/jswrap_wifi.c | 4 +- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 337003bd6..6aebe91ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -64,6 +64,7 @@ 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) + ES32: add setIP and setAPIP 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/libs/network/esp32/jswrap_esp32_network.c b/libs/network/esp32/jswrap_esp32_network.c index 00b5e0253..b00e1a4ac 100644 --- a/libs/network/esp32/jswrap_esp32_network.c +++ b/libs/network/esp32/jswrap_esp32_network.c @@ -92,6 +92,8 @@ 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) +// Global data structure for setIP and setAPIP +tcpip_adapter_ip_info_t info; //===== mDNS static bool mdns_started = 0; @@ -1775,3 +1777,100 @@ 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; + // bool rc = false; + esp_err_t err; + + 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/jswrap_wifi.c b/libs/network/jswrap_wifi.c index d316b2250..36773d98e 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"], From 130666479d9b0065f2f0334b85810476cded8917 Mon Sep 17 00:00:00 2001 From: MaBecker Date: Tue, 3 Dec 2024 08:18:22 +0100 Subject: [PATCH 08/24] ESP32: add setIP and setAPIP fix typo --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 6aebe91ae..80e97c201 100644 --- a/ChangeLog +++ b/ChangeLog @@ -64,7 +64,7 @@ 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) - ES32: add setIP and setAPIP + ESP32: add setIP and setAPIP 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) From 6d6a82856b6c564595d3c7f2c09bbbc459670622 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Tue, 3 Dec 2024 12:09:34 +0000 Subject: [PATCH 09/24] typescript: add `remove` to `MenuOptions` --- libs/pixljs/jswrap_pixljs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/pixljs/jswrap_pixljs.c b/libs/pixljs/jswrap_pixljs.c index 8609cd3a2..cd794be7f 100644 --- a/libs/pixljs/jswrap_pixljs.c +++ b/libs/pixljs/jswrap_pixljs.c @@ -519,6 +519,7 @@ type MenuNumberItem = { type MenuOptions = { title?: string; back?: () => void; + remove?: () => void; selected?: number; fontHeight?: number; scroll?: number; @@ -733,4 +734,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 +} From 1d1707da1ecca8a2a89d0c25aff64b0271543250 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 14:02:41 +0000 Subject: [PATCH 10/24] In SAVE_ON_FLASH builds (Microbit 1) remove getSerial, Math.LN*/LOG*SQRT* constants, passwords, Serial/I2C/SPI.find, Date.toUTCString --- ChangeLog | 1 + README_BuildProcess.md | 1 + libs/graphics/jswrap_graphics.c | 4 ++-- src/jsinteractive.c | 8 ++++++++ src/jsinteractive.h | 4 +++- src/jsutils.h | 1 + src/jswrap_date.c | 1 + src/jswrap_espruino.c | 7 +++++++ src/jswrap_interactive.c | 1 + src/jswrap_math.c | 6 ++++++ src/jswrap_number.c | 2 ++ src/jswrap_serial.c | 1 + src/jswrap_spi_i2c.c | 2 ++ 13 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index b06d35c98..b0c16aa22 100644 --- a/ChangeLog +++ b/ChangeLog @@ -65,6 +65,7 @@ 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 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/README_BuildProcess.md b/README_BuildProcess.md index 2c7e55151..f75ca93f4 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/graphics/jswrap_graphics.c b/libs/graphics/jswrap_graphics.c index 5c9245f01..e230340c4 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"] @@ -2807,7 +2807,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"], diff --git a/src/jsinteractive.c b/src/jsinteractive.c index 8f49f8ba9..c5082ce18 100644 --- a/src/jsinteractive.c +++ b/src/jsinteractive.c @@ -170,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() { @@ -892,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); @@ -1800,6 +1806,7 @@ void jsiHandleChar(char ch) { // 27 then 79 then 72 - end // 27 then 10 - alt enter +#ifndef ESPR_NO_PASSWORD if (jsiPasswordProtected()) { if (ch=='\r' || ch==10) { JsVar *pwd = jsvObjectGetChildIfExists(execInfo.hiddenRoot, PASSWORD_VARIABLE_NAME); @@ -1819,6 +1826,7 @@ void jsiHandleChar(char ch) { jsiAppendToInputLine(ch); return; } +#endif if (ch==3 && IS_PACKET_TRANSFER(inputState)) execInfo.execute &= ~EXEC_CTRL_C_MASK; // if we got Ctrl-C, ignore it diff --git a/src/jsinteractive.h b/src/jsinteractive.h index 4aff5f25d..b1db59399 100644 --- a/src/jsinteractive.h +++ b/src/jsinteractive.h @@ -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/jsutils.h b/src/jsutils.h index 1319113d8..6e0550010 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 235757690..fad337c4e 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 20fa1d03f..d7ef62f23 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 dde3b1eb4..c3164ff7c 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 2652e3184..af13f791e 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 f02290c65..0a03b638e 100644 --- a/src/jswrap_number.c +++ b/src/jswrap_number.c @@ -144,6 +144,7 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { /*JSON{ "type" : "variable", "name" : "HIGH", + "ifndef" : "SAVE_ON_FLASH", "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 +153,7 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { /*JSON{ "type" : "variable", "name" : "LOW", + "ifndef" : "SAVE_ON_FLASH", "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 29774b5d9..788f5b405 100644 --- a/src/jswrap_serial.c +++ b/src/jswrap_serial.c @@ -104,6 +104,7 @@ Espruino boards) "type" : "staticmethod", "class" : "Serial", "name" : "find", + "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 dc1c417ad..5d21538de 100644 --- a/src/jswrap_spi_i2c.c +++ b/src/jswrap_spi_i2c.c @@ -73,6 +73,7 @@ JsVar *jswrap_spi_constructor() { "type" : "staticmethod", "class" : "SPI", "name" : "find", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_SPI1, JSH_SPIMAX, pin)", "params" : [ ["pin","pin","A pin to search with"] @@ -532,6 +533,7 @@ JsVar *jswrap_i2c_constructor() { "type" : "staticmethod", "class" : "I2C", "name" : "find", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_I2C1, JSH_I2CMAX, pin)", "params" : [ ["pin","pin","A pin to search with"] From 743af5ddd20c188241c1cd73697a6f36f97b4c68 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 15:32:58 +0000 Subject: [PATCH 11/24] add deprecation docs --- libs/bluetooth/jswrap_bluetooth.c | 1 + libs/hexbadge/jswrap_hexbadge.c | 1 + libs/network/esp8266/jswrap_esp8266_network.c | 1 + libs/pixljs/jswrap_pixljs.c | 1 + libs/puckjs/jswrap_puck.c | 1 + scripts/common.py | 8 +++++++- src/jswrap_number.c | 2 ++ src/jswrap_serial.c | 1 + src/jswrap_spi_i2c.c | 2 ++ 9 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libs/bluetooth/jswrap_bluetooth.c b/libs/bluetooth/jswrap_bluetooth.c index 8165c3d96..a455c3801 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/hexbadge/jswrap_hexbadge.c b/libs/hexbadge/jswrap_hexbadge.c index b4d008274..388627e75 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/esp8266/jswrap_esp8266_network.c b/libs/network/esp8266/jswrap_esp8266_network.c index 83451e132..4b1e47493 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/pixljs/jswrap_pixljs.c b/libs/pixljs/jswrap_pixljs.c index cd794be7f..8f8bdeff2 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" ] } diff --git a/libs/puckjs/jswrap_puck.c b/libs/puckjs/jswrap_puck.c index acbcc0085..b285d0422 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 23739b911..da2d5a80d 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/src/jswrap_number.c b/src/jswrap_number.c index 0a03b638e..106116b8d 100644 --- a/src/jswrap_number.c +++ b/src/jswrap_number.c @@ -145,6 +145,7 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { "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;" @@ -154,6 +155,7 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { "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 788f5b405..da3690e00 100644 --- a/src/jswrap_serial.c +++ b/src/jswrap_serial.c @@ -104,6 +104,7 @@ Espruino boards) "type" : "staticmethod", "class" : "Serial", "name" : "find", + "deprecated" : true, "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_USART1, JSH_USARTMAX, pin)", "params" : [ diff --git a/src/jswrap_spi_i2c.c b/src/jswrap_spi_i2c.c index 5d21538de..cc627f939 100644 --- a/src/jswrap_spi_i2c.c +++ b/src/jswrap_spi_i2c.c @@ -73,6 +73,7 @@ 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" : [ @@ -533,6 +534,7 @@ 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" : [ From 1c3ae855737e068f9e176643e8dff4c17104306f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 12:26:57 +0000 Subject: [PATCH 12/24] Extracted graphicsBlendColorRGB565 to its own function --- libs/graphics/graphics.c | 29 ++++++++++++++++------------- libs/graphics/graphics.h | 2 ++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/libs/graphics/graphics.c b/libs/graphics/graphics.c index d5ace6863..da2772dcd 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 75c4fa3ae..189e70690 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) From a7466ef17e42b45da1dfe2eede912f5d53ec948d Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 12:27:13 +0000 Subject: [PATCH 13/24] Added user-configurable palette --- libs/pipboy/jswrap_pipboy.c | 111 ++++++++++++++++++++++++++++++------ libs/pipboy/jswrap_pipboy.h | 1 + 2 files changed, 93 insertions(+), 19 deletions(-) diff --git a/libs/pipboy/jswrap_pipboy.c b/libs/pipboy/jswrap_pipboy.c index 3a230411b..8f6fcf362 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 @@ -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,39 @@ 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 +*/ +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); + } +} /*JSON{ "type" : "init", @@ -1222,11 +1285,21 @@ 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; + } // 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; + graphicsInternal.data.fgColor = palette[0][15]; jsvUnLock(jswrap_graphics_clear(g, 0)); jsvUnLock(jswrap_graphics_drawImage(g, img, (LCD_WIDTH-200)/2, LCD_HEIGHT/2-16, NULL)); graphicsInternal.data.fontSize = JSGRAPHICS_FONTSIZE_6X8+1; @@ -1303,7 +1376,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 = palette[0][15]; // 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)); diff --git a/libs/pipboy/jswrap_pipboy.h b/libs/pipboy/jswrap_pipboy.h index 9d11b1138..ae56d556d 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(); From 610a0a92594b0d037edb88decb947d96ec7a3d19 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 12:29:02 +0000 Subject: [PATCH 14/24] docs --- libs/pipboy/jswrap_pipboy.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libs/pipboy/jswrap_pipboy.c b/libs/pipboy/jswrap_pipboy.c index 8f6fcf362..bcfb1ad01 100644 --- a/libs/pipboy/jswrap_pipboy.c +++ b/libs/pipboy/jswrap_pipboy.c @@ -1254,6 +1254,25 @@ JsVar *jswrap_pb_streamPlaying() { ] } 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); +``` */ void jswrap_pb_setPalette(JsVar *pal) { if (!jsvIsArray(pal)) { From f103d6d92bb5bc48a4d1e6c0365807c9408e29dd Mon Sep 17 00:00:00 2001 From: MaBecker Date: Tue, 3 Dec 2024 23:06:29 +0100 Subject: [PATCH 15/24] ESP32: add setIP and setAPIP remove global var --- libs/network/esp32/jswrap_esp32_network.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libs/network/esp32/jswrap_esp32_network.c b/libs/network/esp32/jswrap_esp32_network.c index b00e1a4ac..633ba93ff 100644 --- a/libs/network/esp32/jswrap_esp32_network.c +++ b/libs/network/esp32/jswrap_esp32_network.c @@ -92,9 +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) -// Global data structure for setIP and setAPIP -tcpip_adapter_ip_info_t info; - //===== mDNS static bool mdns_started = 0; @@ -1782,9 +1779,8 @@ void jswrap_wifi_getHostByName( static void setIP(JsVar *jsSettings, JsVar *jsCallback, int interface) { char ipTmp[20]; int len = 0; - // bool rc = false; esp_err_t err; - + tcpip_adapter_ip_info_t info; memset( &info, 0, sizeof(info) ); // first check parameter From a5f01e3df3f43210fdab7af2c58052b77aec1476 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 6 Dec 2024 08:51:51 +0000 Subject: [PATCH 16/24] Graphics.wrapString fix issue with missing final char if immediately after a '.' or other char we can split after (fix #2572) # Conflicts: # ChangeLog --- ChangeLog | 1 + libs/graphics/jswrap_graphics.c | 5 ++++- tests/test_graphics_wrapString.js | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 3ab5432ae..861636107 100644 --- a/ChangeLog +++ b/ChangeLog @@ -67,6 +67,7 @@ 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) 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/libs/graphics/jswrap_graphics.c b/libs/graphics/jswrap_graphics.c index e230340c4..455d23d3c 100644 --- a/libs/graphics/jswrap_graphics.c +++ b/libs/graphics/jswrap_graphics.c @@ -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 diff --git a/tests/test_graphics_wrapString.js b/tests/test_graphics_wrapString.js index b742e44eb..7e6765f75 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"); From 8205febfc1034b907f52aa629413b2ce4b1b5103 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 16:04:47 +0000 Subject: [PATCH 17/24] Update global colour theme with the palette that was set --- libs/pipboy/jswrap_pipboy.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/libs/pipboy/jswrap_pipboy.c b/libs/pipboy/jswrap_pipboy.c index bcfb1ad01..a2696e08e 100644 --- a/libs/pipboy/jswrap_pipboy.c +++ b/libs/pipboy/jswrap_pipboy.c @@ -1274,6 +1274,16 @@ for (var i=0;i<16;i++) { 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"); @@ -1295,6 +1305,7 @@ void jswrap_pb_setPalette(JsVar *pal) { jsvIteratorFree(&it); jsvUnLock(a); } + jswrap_pb_updateTheme(); } /*JSON{ @@ -1314,12 +1325,13 @@ void jswrap_pb_init() { 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 = palette[0][15]; - 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; @@ -1395,7 +1407,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 = palette[0][15]; // 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)); @@ -1413,7 +1425,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); } From e0ceed5f65b46905510a2fc1c144ca5a789cc289 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 12:27:13 +0000 Subject: [PATCH 18/24] Added user-configurable palette --- libs/pipboy/jswrap_pipboy.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/libs/pipboy/jswrap_pipboy.c b/libs/pipboy/jswrap_pipboy.c index a2696e08e..f63e06951 100644 --- a/libs/pipboy/jswrap_pipboy.c +++ b/libs/pipboy/jswrap_pipboy.c @@ -401,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"] } @@ -871,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); @@ -881,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); @@ -1026,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(); } @@ -1070,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); } @@ -1352,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)); @@ -1373,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);" @@ -1594,4 +1599,4 @@ function es8388_output_cfg(o1en, o2en){ es8388_write_reg(0x04, tempreg); } -*/ \ No newline at end of file +*/ From 0a8ad236b5b2f83fc2159f0fca212a81033565ff Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 15:39:40 +0000 Subject: [PATCH 19/24] Lower I2S ringbuffer size --- libs/pipboy/stm32_i2s.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/pipboy/stm32_i2s.h b/libs/pipboy/stm32_i2s.h index 48b25d073..d26319c67 100644 --- a/libs/pipboy/stm32_i2s.h +++ b/libs/pipboy/stm32_i2s.h @@ -19,7 +19,7 @@ #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... typedef enum { From acf9482c1d19f5d6dc2f7261e73e45ccc8becffd Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 5 Dec 2024 10:08:54 +0000 Subject: [PATCH 20/24] Ensure STM32_I2S_AddSamples starts playback if there isn't enough space, fix issue where calling AddSamples with too big a buffer would always cause failure --- libs/pipboy/stm32_i2s.c | 28 +++++++++++++++++++++++----- libs/pipboy/stm32_i2s.h | 6 ++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/libs/pipboy/stm32_i2s.c b/libs/pipboy/stm32_i2s.c index 90c0d906f..b2fc17ec6 100644 --- a/libs/pipboy/stm32_i2s.c +++ b/libs/pipboy/stm32_i2s.c @@ -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 d26319c67..a26330e6f 100644 --- a/libs/pipboy/stm32_i2s.h +++ b/libs/pipboy/stm32_i2s.h @@ -22,6 +22,12 @@ #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 From 31ae87ce154d08a3947943315e17e4a4b708aaa4 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 6 Dec 2024 09:16:40 +0000 Subject: [PATCH 21/24] Misc updates from PIPBOY branch --- libs/filesystem/fat_sd/sdio_diskio.c | 10 ++++++---- libs/pipboy/stm32_i2s.c | 20 ++++++++++---------- src/jsinteractive.c | 5 ++--- src/jsinteractive.h | 4 ++-- targets/stm32/jshardware.c | 2 +- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/libs/filesystem/fat_sd/sdio_diskio.c b/libs/filesystem/fat_sd/sdio_diskio.c index bf19c5ad2..8d9d4013c 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/pipboy/stm32_i2s.c b/libs/pipboy/stm32_i2s.c index b2fc17ec6..5fb7950ee 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); diff --git a/src/jsinteractive.c b/src/jsinteractive.c index c5082ce18..ea3436130 100644 --- a/src/jsinteractive.c +++ b/src/jsinteractive.c @@ -1332,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; diff --git a/src/jsinteractive.h b/src/jsinteractive.h index b1db59399..6a8db64ea 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); diff --git a/targets/stm32/jshardware.c b/targets/stm32/jshardware.c index 33c3fd2e1..678b332bc 100644 --- a/targets/stm32/jshardware.c +++ b/targets/stm32/jshardware.c @@ -1554,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); From 432be85f5e118be82960249ab9466ae2da46cc17 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 6 Dec 2024 15:16:27 +0000 Subject: [PATCH 22/24] Graphics: g.dump/asBMP can now output 16 bit images --- ChangeLog | 1 + libs/graphics/jswrap_graphics.c | 124 ++++++++++++++++++++------------ libs/graphics/lcd_fsmc.c | 3 +- 3 files changed, 80 insertions(+), 48 deletions(-) diff --git a/ChangeLog b/ChangeLog index 861636107..c86d85821 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,7 @@ 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 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/libs/graphics/jswrap_graphics.c b/libs/graphics/jswrap_graphics.c index 455d23d3c..ee4815dac 100644 --- a/libs/graphics/jswrap_graphics.c +++ b/libs/graphics/jswrap_graphics.c @@ -4145,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<2) { + 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 17db17532..d5d0d133d 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(); } From ff208409b5a60b0a7014fa1b79b4331da4885b98 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 6 Dec 2024 15:19:44 +0000 Subject: [PATCH 23/24] fix RGB565 output --- libs/graphics/jswrap_graphics.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/graphics/jswrap_graphics.c b/libs/graphics/jswrap_graphics.c index ee4815dac..c32ef7bc5 100644 --- a/libs/graphics/jswrap_graphics.c +++ b/libs/graphics/jswrap_graphics.c @@ -4260,8 +4260,6 @@ JsVar *jswrap_graphics_asBMP_X(JsVar *parent, bool printBase64) { } else { // <= 1 pixel per byte for (int x=0;x>1)&~31U); for (int j=0;j Date: Fri, 6 Dec 2024 15:56:36 +0000 Subject: [PATCH 24/24] Fix STM32 bootloader build --- scripts/create_zip.sh | 19 ++++++++++++++++--- targets/stm32_boot/utils.c | 2 ++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/scripts/create_zip.sh b/scripts/create_zip.sh index ab188f476..6c79e9736 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/targets/stm32_boot/utils.c b/targets/stm32_boot/utils.c index ab36a1db8..2cc6add9d 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];