Saltar al contenido principal

JARU 0.9.7

  • Fecha: noviembre de 2026
  • Componentes: IDE (0.9.7.0) · VM Windows · VM ESP32 / ESP32-S3 · VM ESP32-P4 · VM ESP32-S31 (nueva) · Compilador
  • Versión anterior: 0.9.6

Esta versión trae cuatro bloques grandes:

  1. El editor de escenas, nuevo en el IDE: diseña salas 2D con capas, objetos, comportamientos, reglas y máquinas de estados, y las traduce a código JARU normal.
  2. Más velocidad y menos memoria en las placas: superinstrucciones en el bytecode, tablas de saltos en el intérprete, volcado parcial de pantalla, una sola imagen por sprite y bastantes KB menos de RAM interna por juego.
  3. Un IDE renovado: estilo visual unificado (JARU Slate), editor de sprites nuevo con actores y poses, y unos ochenta arreglos que salen de un análisis de usabilidad.
  4. Placa nueva, el ESP32-S31 (ESP32-S31-Korvo-1), y soporte para mandos I2C.

Además, Display.fps y Display.dt dan a los juegos un ritmo fijo en todas las placas, la colisión con mapas recibe una tanda larga de arreglos y el compilador deja de aceptar en silencio algunos errores que antes dejaban el programa sin ejecutar.

Lo más destacado​

Cambios incompatibles​

CambioQué hacer
Los programas compilados con 0.9.7 necesitan firmware 0.9.7. Un firmware 0.9.6 no sabe rechazarlos (solo comparaba el número mayor) y se para en el primer opcode nuevo con "Unknown Opcode encountered". Lo mismo pasa con los sprites del Build nuevo, que van en una sola imagen. Desde 0.9.7 el cargador compara también el menor y la revisión, y un programa más nuevo que la VM se rechaza con un mensaje claro.Graba el firmware de la VM en cada placa desde el IDE (Flash Firmware) antes de subir programas. Quien no pueda hacerlo todavía puede compilar desde la línea de órdenes con --no-superinstr, que emite el bytecode de 0.9.6; los sprites siguen necesitando el firmware nuevo.
/ entre dos enteros se calcula en doble precisión, como ya pasaba con los decimales. Antes se hacía en precisión simple: 1 / 3 daba 0.3333333432674408 e int(80 / (40 / 30)) daba 59. div entre enteros es exacto en todo el rango de 32 bits (antes fallaba a partir de 16.777.216). En el ESP32-P4, int(), toInt(), Math.floor y Math.ceil con un decimal negativo daban 0.Nada, salvo que el programa dependa del redondeo de antes. Los decimales grandes pasados a entero dan ahora lo mismo en Windows y en todas las placas.
NaN ya no es igual a sí mismo: nan == nan es false y nan != nan es true, como en cualquier lenguaje. Y 0.0 == -0.0 es true.Para saber si un valor es NaN, v != v. Las listas y los mapas siguen encontrando una clave NaN como antes.
Una función sin su end es un error de compilación. Antes el compilador terminaba bien y el programa no ejecutaba nada, porque todo lo que venía detrás pasaba a ser el cuerpo de la función. El caso típico es un else if, que en JARU es elsif: el error lo señala.Corrige el end que falta o cambia else if por elsif.
grid.set y grid.clear solo aceptan valores de 0 a 255. Antes recortaban sin avisar: 256 se guardaba como 0 y -1 como 255.Lo que esté fuera de rango lanza ERR_INVALID_VALUE y la celda no cambia.
flipX y flipY voltean también el collider, dentro del fotograma. Una caja descentrada (una espada, un morro) se quedaba del lado original al voltear el sprite. En placa, el dibujo volteado se ancla ahora igual que en Windows.Revisa los colliders descentrados de los sprites que se voltean.
Un sprite inactivo no choca, tampoco el que pregunta: hits, contacts y hitsGrid devuelven nil si el sprite que llama está inactivo. Y a.hits(a) ya no devuelve [a].Nada, si el juego ya trataba así a los inactivos.
El recurso del sprite trae su pivote, su collider, loop y yoyo tal como se dejan en el editor de sprites. Antes la VM los ignoraba y el sprite salía con el pivote en el centro y el collider del fotograma entero.Si el código compensaba a mano lo que se veía en el editor, quita esa compensación.
addImage() da error en un sprite cargado como hoja, que es como exporta ahora el Build los sprites del editor.Para ir añadiendo fotogramas, parte de un sprite creado con Sprite.new(bitmap).
En placa, Power.wakeReason() devuelve las constantes WAKE_*. Antes devolvía el número de causa del ESP-IDF, que no casaba con ellas: tras despertar por el timer daba 4, el valor de WAKE_EXT1. Y Power.wakeOnGPIO() pide el pin y el nivel; sin ellos no hacía nada.Compara con Power.WAKE_TIMER y las demás constantes, no con números, y llama a wakeOnGPIO(pin, nivel).
En un enum, los nombres sin = que van detrás de uno con un número pueden cambiar de valor: con ROJO, VERDE = 5, AZUL, AZUL vale 6 (antes 7), y con A = -1, B, B vale 0 (antes 1). Y un nombre repetido en un enum ya no compila; antes valía el primero.Si guardas esos valores en un fichero o los comparas con números, revísalos. Quita los nombres repetidos.
Un array escrito entre llaves con filas de distinto tamaño ya no compila. Antes una fila más corta corría las siguientes (o, si era la última, se rellenaba con ceros) y una más larga escribía fuera del array.Escribe todas las filas con el mismo número de elementos.
File.open solo acepta los modos de su tabla: r, w o a, con + y b detrás si hacen falta. Con otro, como "rt" o "rw", lanza ERR_INVALID_VALUE. Antes, en Windows, "rt" y "wt" abrían y otros cerraban la VM, y en las placas abría para leer o para escribir según las letras.Usa uno de la tabla: "r" en vez de "rt", y "w" en vez de "wt".
Sprite.load lanza la excepción si falta la imagen de un fotograma o de la hoja. Antes devolvía un valor de error.Si mirabas lo que devuelve, captura la excepción con try.
find(subcadena, n) con n menor que 1 lanza ERR_INVALID_VALUE; antes devolvía 0. Y desde la tercera aparición da otra posición: la buena.Usa n desde 1.
toUnsigned() de un negativo devuelve un decimal, su valor sin signo; antes devolvía el mismo entero.Si le sumabas 4294967296.0, quítalo.
grid.path del mismo sitio al mismo sitio devuelve [x, y]; antes, nil.Si mirabas el nil para saber si ya habías llegado, compara el origen con el destino.
En las placas, con los 32 timers en uso, after() y every() devuelven un valor de error; antes cancelaban el primero de los que había para hacer sitio.Comprueba el número del timer con id > 0, o usa menos timers.
MQTT en Windows entra con el usuario y la contraseña de la configuración. Antes no los usaba, y los proyectos guardados con una versión anterior llevan User iotdev y una contraseña de ejemplo: test.mosquitto.org, el broker de serie, rechaza ese usuario.Si el proyecto usa MQTT en Windows con test.mosquitto.org, deja User vacío en Config › Platform › Windows (Ajustes). Los proyectos nuevos ya lo traen vacío.
toFloat() lee todas las cifras: "3.14".toFloat() == 3.14 es true. Antes se quedaba con unas 7: "3.14" daba 3.1400001049041748, "1e39" daba infinito y "1e-46", 0.Nada, salvo que el programa dependa del redondeo de antes.
toInt() de un texto lanza ERR_TYPE_MISMATCH si el número no cabe en un entero, como cuando el texto no es un número; también con "inf" y "nan". Antes daba un número que no servía, sin error: "3000000000".toInt() daba -2147483648 en Windows y 2147483647 en las placas.Si el texto puede traer un número así, conviértelo dentro de un try.
En la placa, sin canales PWM libres, awrite, frequency(pin, hz) y resolution(pin, bits) lanzan ERR_OUT_OF_MEMORY, como en Windows. Antes no hacían nada, y en el P4 y el S31 el noveno pin pisaba el canal del primero.Usa como mucho tantos pines con PWM como canales tiene la placa (16 en el ESP32 y 8 en el S3, el P4 y el S31), o libera alguno con detach.

Editor de escenas (nuevo)​

Un tipo de recurso nuevo, la escena (.scn), con su propio editor en el IDE. Diseña salas 2D y al compilar las traduce a código JARU normal. Por eso no necesita nada especial de la VM: funciona igual en Windows, ESP32, ESP32-S3, ESP32-P4 y ESP32-S31.

Cómo encaja en un juego​

  • Las escenas viven en la carpeta Scenes del proyecto y cada una se edita en su pestaña del editor de escenas.
  • Al compilar, cada escena se traduce a Code/<Sala>.gen.aru, más un Code/SceneRuntime.gen.aru compartido con lo que usan todas. No se editan a mano: se regeneran en cada compilación, y el runtime solo lleva lo que el proyecto usa.
  • El juego importa la sala y la usa: <Sala>Load(spawn) para cargarla y, en el bucle, <Sala>Update(room), room.scene.update(), <Sala>Resolve(room) y <Sala>Draw(room). La cabecera del .aru generado lo explica con un ejemplo.
  • Salas como datos. Si el código del juego usa SceneLoad, el Build deja el mapa de cada sala en su .gmap y el resto de todas las salas en una tabla, Build/flash/Scenes/World.gmap, con un solo cargador. Así un juego con muchas salas no lleva el código de cada una, y lo que sale en todas las salas se carga una vez. Una sala con comportamientos, máquinas de estados, plantillas, variables o código propio no los ejecuta como datos: conserva su .gen.aru, y el Build lo avisa.
  • spawn(e) es opcional y deja que el juego decida cada objeto de la sala: devolver un sprite para que la sala use ese, false para ignorarlo o nil para que lo cree la escena.
  • Lo que es del juego y no de una sala vive en Scenes/Game.json y se edita en la pestaña Game: las variables que pasan de una sala a otra (los puntos, las vidas, la vida y la vida máxima, más las que quieras), los controles, con su tecla, el GPIO de cada placa o un botón del mando I2C, y el ritmo del juego (Display.fps). En el código, room.game es el mismo objeto en todas las salas, room.dt sale de Display.dt, SceneInputMap() mapea los controles y SceneGameReset() empieza una partida nueva.

Capas y cámara​

Objetos y comportamientos​

  • Sprites, imágenes, formas, zonas y marcadores, con tipo de cuerpo, etiqueta, rotación fija y "Keep in room". El collider es el del sprite: se define en el editor de sprites y la escena lo usa.
  • Física por objeto: masa, fricción, rebote, escala de gravedad, frenos, filtro de colisión, colisión continua y velocidad y giro iniciales.
  • Doce comportamientos que se añaden a un objeto sin escribir código: Linear motion, Patrol, Follow route, Follow / flee, Sine wave, Platform control, Top-down control, Health, Destroy when it leaves, Show a variable (enseña una variable en pantalla), Animation y Spawner. Pueden mirar hacia donde van; Patrol da la vuelta y Follow se para contra el mapa también en salas sin física; Animation va de ida y vuelta o al revés.
  • Rutas: la herramienta Path dibuja una polilínea nodo a nodo y "Follow route" la sigue por su etiqueta, en bucle, ida y vuelta o hasta el final. Varios marcadores con la misma etiqueta también valen como ruta.
  • Plantillas: un objeto guardado sin posición del que los spawners y la acción "Create from a template" hacen copias, con sus propiedades.
  • Zonas que el juego puede pintar como un rectángulo de su color ("Show in game"), para ver dónde están mientras se prueba.

Reglas de la sala​

  • Eventos: al empezar, cada cierto tiempo, cuando dos etiquetas se tocan, al entrar en una zona, cuando un objeto se sale de la sala (la caída al foso), cuando se pulsa un control y cuando una variable cumple una condición.
  • Acciones: cambiar una variable, crear desde una plantilla, destruir, quitar vida, perder una vida, empezar una partida nueva, encender o apagar un comportamiento, reproducir una animación, tocar una nota o un efecto, poner música, pedir otra sala (o repetir esta) y llamar a una función propia, que recibe f(spr, other, room).
  • Las reglas se copian y se pegan entre salas con Ctrl+C y Ctrl+V.

Máquinas de estados​

  • Un objeto puede llevar estados. Cada uno dice qué comportamientos están encendidos, con qué valores (patrullar despacio y perseguir deprisa con el mismo componente), qué hace al entrar y qué lo saca de ahí. Al salir de un estado, los valores vuelven a los del objeto.
  • Transiciones: al pasar un tiempo, a menos o a más de una distancia de otro objeto, al tocar una etiqueta y según una variable.
  • Se editan en su propia ventana; en el lienzo se ven los radios de las distancias y la máquina entera del objeto elegido. Diez objetos con la misma máquina comparten una sola copia del código generado.

Código propio​

Trabajar en el lienzo​

Velocidad y memoria en placa​

  • Superinstrucciones. El compilador fusiona las secuencias de bytecode más frecuentes (local + constante + operación, comparación + salto, i++...) en 27 opcodes nuevos que ejecutan todas las VMs. Medido sobre JaruBench: ESP32 1,48x, ESP32-S3 1,48x y ESP32-P4 1,47x, con hasta 2,5x en bucles apretados; en Windows, 1,2x. No cambia nada del lenguaje: cada instrucción fusionada vuelve al camino clásico con cualquier tipo que no sea entero.
  • Tablas de saltos en el intérprete de las placas, que el IDF y Arduino compilaban como un árbol de comparaciones: JaruBench 1,2-1,7x más rápido en ESP32 y S3, y hasta 1,9x en el P4.
  • Mapas de tiles en 8 bits en ESP32 y S3, y el fondo de un tilemap que no cambia se reutiliza.
  • Volcado parcial de pantalla (ESP32 y S3 con PSRAM): Display.update() manda al LCD solo los bloques de 8x8 que han cambiado.
  • Un BMP por sprite en vez de uno por fotograma: JaruMix pasa de 145 a 84 ficheros y unos 375 B menos de RAM por fotograma; JaruGoody carga en la TTGO en 6,1 s en vez de 49,4 s.
  • Menos RAM interna en ESP32 y S3: el montón sin heap poisoning ahorra 16,7 B por bloque reservado (40-61 KB por juego en la TTGO); las cadenas van en un solo bloque (6-9 KB por juego); el objeto de cada imagen va a PSRAM (360 B menos por fotograma); el compilador comparte las constantes repetidas de cada función y la VM reserva sus tablas a medida.
  • Sin números de línea, si hace falta: la casilla Line numbers de Build › Target (o --no-line-info en el compilador) guarda el programa sin sus tablas de líneas, que solo sirven para las trazas de error: de 1,8 a 9,4 KB de RAM interna según el juego.

Lenguaje y API​

  • Display.fps = N (0-1000; 0 por defecto, como hasta ahora): después de presentar, Display.update() espera al siguiente paso y devuelve cuántos tocan, 1 o más si el fotograma ha llegado tarde. Display.dt: segundos entre los dos últimos Display.update(). Así un juego va a la misma velocidad haya scroll o no, y en cualquier placa.
  • sprite.contacts(sprite | lista) devuelve por cada sprite tocado [otro, nx, ny, profundidad], con la normal en la dirección en que hay que mover el sprite para salir. Vale con rectángulos y círculos girados, sin escena ni física.
  • sprite.prevX y sprite.prevY, de lectura y escritura: el punto desde el que barre hitsGrid. Un programa sin escena las pone al empezar cada fotograma; cualquiera, al teletransportar un sprite.
  • Input.set(acción, estado): una fuente externa para una acción de Input (un mando I2C, por ejemplo), que se suma a la tecla o al GPIO mapeados.
  • I2C.transfer(port, addr, bytes, n, delayUs): quinto argumento opcional para los chips que necesitan una pausa entre escribir y leer.
  • draw.getPixel devuelve el color en Windows, como ya hacía en placa (antes imprimía un texto y devolvía nil).
  • Input.map acepta los GPIO 0-63 en todas las placas.
  • HW_ESP32S31 (= 7) y platform() lo devuelve en el ESP32-S31.
  • bitmap.tcolor(color) cambia el color transparente del bitmap; antes no hacía nada. En placa, draw.bitmap y draw.gridBitmap usan el color transparente del bitmap (el de Bitmap.load(fichero, color) o el de tcolor) en vez del negro fijo, como ya hacían Windows y los sprites.
  • foreach sobre un mapa: asignar a la variable del bucle (v = x) cambia el valor de la entrada actual. Antes creaba claves numéricas nuevas y podía machacar otras entradas.
  • MQTT: MQTT.unsubscribe existe también en Windows, y sin conexión publish, subscribe y unsubscribe no hacen nada (antes, en Windows, publish y subscribe cerraban el programa). MQTT.pubinfo() publica la versión de la VM en <usuario>/<id>/device y devuelve true, o false sin conexión; antes solo la escribía en la consola.
  • Power: wakeOnGPIO(pin, nivel) despierta la placa con cualquier pin, no solo con los RTC de wakeOnPin(), y con varios a la vez: cada llamada añade uno. Antes no recibía ningún pin y no despertaba con ninguno. En placa, cada programa empieza sin las fuentes de despertar que dejó puestas el anterior. sleep() y wakeOnTimer() ya no escriben mensajes [DEBUG] en la consola.
  • I2C: readReg16 y writeReg16 respetan su último argumento, littleEndian; antes lo ignoraban y usaban siempre Big Endian. Si un programa pasaba true e intercambiaba además los bytes a mano para compensarlo, ahora sobra el intercambio.

Físicas y colisiones​

  • Las pilas de cuerpos se sostienen solas, también a 20-30 fps en placa: el solver arranca cada contacto con los impulsos del paso anterior, resuelve las dos esquinas de una cara a la vez y corrige la posición en cuatro pasadas.
  • Un KINEMATIC que gira golpea: una paleta de pinball que gira cambiando angle o con angularVel transmite su giro a lo que toca, más fuerte en la punta que en la base.
  • Un cuerpo apoyado en un grid elástico ya no rebota sin fin, y un bote contra el grid ya no gana energía.
  • Colisión con mapas: grid.clone() copia el mapa entero; hitsGrid ya no da la normal de una cara tapada por otro tile; una plataforma de un solo sentido pisada por la esquina es suelo, con 1 px de holgura; un rectángulo girado choca con el mapa tal como está; grid.get envuelve con wrap y acepta decimales; los errores de hits, get, set y see nombran el método real.

Compilador​

  • Un return sin valor justo antes de elsif, else, case, catch o elfor ya compila.
  • Las llamadas encadenadas, f(1)(2), llaman de verdad a lo que devuelve la anterior; antes compilaban sin error y la VM acababa reventando.
  • return, break y continue dentro de un try sueltan el manejador al salir.
  • Los bucles dentro de funciones anidadas ya no se pisan el estado, y el ámbito de la tabla de símbolos vuelve a su sitio tras cada sentencia.
  • Asignar a una función nativa o a algo que no admite asignación (Math.sqrt = 5) da un error de compilación; antes cerraba el compilador.
  • Las variables de una misma sentencia var se evalúan en el orden en que se escriben: en var a = 1, b = a, b ve a. Antes el orden dependía de los nombres. Un nombre repetido en la misma sentencia da error, como con dos var.
  • Todos los errores llevan su línea y su fichero (algunos salían en la línea 0 y sin fichero). Tras un error ya no queda un .jxr a medias, y compilar desde la línea de órdenes sin -o deja el .jxr junto al fuente en vez de pisar el .aru.
  • La compilación nativa (jaruaot) ya no se rompe con una cadena constante de más de 4 KB: su línea del .cpp se cortaba y perdía la comilla de cierre.
  • x++ y x-- ya no se comen el resto de la expresión: var c = a++ + b suma b. Y valen con un elemento de una lista o de un mapa: lista[0]++ sumaba en la línea siguiente y ++lista[0] cerraba el compilador. Con lo que no se puede sumar, como 5++, dan un error.
  • En un enum, la cuenta sigue bien detrás de un nombre con un número: con ROJO, VERDE = 5, AZUL, AZUL vale 6, no 7, y con A = -1, B, B vale 0. Un nombre repetido da un error; antes valía el primero.
  • Un operator que no se puede sobrecargar, o con parámetros de más o de menos, y un import circular dan un error con su línea; antes cerraban el compilador sin ningún mensaje.
  • .., que no es un operador de JARU, da un error de compilación; antes compilaba sin avisar y descolocaba la VM. También dan error un array escrito entre llaves con filas de distinto tamaño, que corría las filas o escribía fuera del array, y un struct de más de 127 campos, que cerraba la VM al declararlo.
  • Los mensajes de error del compilador salen en inglés, como los de la VM (había muchos en castellano), y sin erratas: «Unrecognised», «method». Un error al final del fichero, como un end que falta, sale detrás de lo último escrito y dice end of file; antes salía en la línea siguiente a la última, con el texto de otro sitio. Y en el Output del IDE, cada error dice lo que ha encontrado el compilador, como en la consola.
  • Una función (func) con el nombre de una de la librería estándar, como len, abs o rgb, da un aviso al compilar: la tapa en todo el programa, también en los ficheros importados. El programa compila igual; el aviso sale en amarillo en el Output del IDE. Los métodos (def) con esos nombres no avisan: se usan con el objeto delante.
  • Los decimales escritos en el código ya no pierden cifras: 1e-18 se quedaba en 0 y 1.23456789e-10 en 1.2345679e-10, y un programa con un decimal de 1e110 o más no cargaba (Error loading file). Solo uno menor que 2.2e-308, sin contar el 0, se queda en 0.

VMs​

  • VM nueva para el ESP32-S31 (placa ESP32-S31-Korvo-1): pantalla RGB de 800x480, teclado USB, SD y despertar por EXT1.
  • Al terminar un programa en placa se sueltan sus timers, sus interrupciones de GPIO, el callback de MQTT y las colisiones pendientes. Antes seguían vivos al recargar el siguiente programa y podían colgarlo.
  • ESP32-P4: ya no enseña un fotograma negro cada pocos segundos; la VM interpretada corre en una tarea propia de 16 KB (un juego grande agotaba la pila al cargar sprites); la CPU va a 360 MHz, porque los 400 que pedía no arrancan en las placas anteriores a la revisión 3; Display.loadTFT ya pinta fuera del view (también en el S31).
  • Sprites: frame con decimales se redondea hacia abajo; loop acepta enteros; addImage conserva el pivote propio; yoyo y dir = -1 animan de verdad, y yoyo con un solo fotograma ya no tumba la VM; Sprite.load ya no deja fotogramas liberados dentro del sprite; Sprite.new(sprite) conserva el collider del original.
  • clone() copia los arrays anidados en structs, listas, mapas e instancias, y deja en el clon el mismo bitmap: en un struct, una instancia o un array dejaba uno sin rellenar que podía cerrar la VM.
  • foreach sobre un mapa recorre la tabla de una pasada: antes cada paso volvía a contar desde el principio (150.000 entradas, unos 7 s).
  • Cambiar el valor de una clave que ya existe (m[k] = x) ya no rehace la tabla del mapa o del objeto: dentro de un foreach sobre ese mapa el recorrido ya no repite ni se salta entradas, y sobrescribir no cambia el orden de keys(), print ni JSON.stringify.
  • Leer una propiedad que no existe dice su nombre.
  • Un spi_host de 2 en boot.cfg ya no tumba Display.open cuando la pantalla no es SPI.
  • Sin jaru_app en boot.cfg, la placa avisa cada 5 s en vez de reintentar.
  • Arreglados varios fallos que aparecían cuando el recolector de memoria corría a mitad de una operación: asignar en un mapa (m[k] = v), hitsGrid, la traza de una excepción y m.items(). También uno tras un error en tiempo de ejecución, que seguía usando la pila ya borrada.
  • ESP32 y ESP32-S3: LovyanGFX 1.2.27, con la versión fijada en el firmware.
  • En Windows, draw.text con una cadena vacía no pinta nada; antes cerraba la VM. Tampoco la cierra ya una petición de Http abandonada por el tiempo de espera, aunque el servidor conteste después.
  • En el simulador GPIO, un pin en GPIO.OUTPUT o GPIO.OPEN_DRAIN se ve como una salida digital, y el LED se enciende con GPIO.write. Antes se veía como un PWM sin canal.
  • MQTT.publish, subscribe y unsubscribe con algo que no es una cadena lanzan ERR_TYPE_MISMATCH; antes, un número cerraba la VM.
  • JSON.stringify escribe las claves enteras y booleanas como texto ({1: "uno"} sale {"1": "uno"}), y lanza su error si un valor que no se puede escribir va dentro de una lista o de un mapa; antes, las dos cosas cerraban la VM.
  • Pintar un sprite con frame igual a frames ya no cierra la VM: nextFrame() en el último fotograma vuelve al primero, y al volver a activar un sprite que acabó su animación sin bucle, la animación empieza otra vez.
  • pauseMusic() calla las notas que sonaban; antes se quedaban sonando hasta resumeMusic() o stopMusic().
  • Con el zumbador, las notas con duración de Sound.tone, pulse y noise se callan solas al acabar, como con I2S; antes sonaban hasta Sound.stop(), y el canal seguía ocupado: isPlaying() daba true y una nota de menos prioridad se descartaba.
  • Con el zumbador y sin Sound.init(), Sound.isPlaying, loadSample y playSample devuelven false, como con el sonido arrancado; antes podían hacer caer la VM.
  • En las placas, File.open con "r+", "w+" y "a+" lee y escribe, como en Windows; antes, "r+" vaciaba el fichero, y "w+" y "a+" no dejaban leer.
  • type() de un valor de error da tError; antes no devolvía nada y descolocaba las variables de la función. Y en las placas, type(e) del error de un catch da tError, como en Windows (antes, 25).
  • draw.bytes con la paleta en una lista o con menos bytes de los que pinta devuelve su valor de error sin descolocar las variables de la función; antes el programa fallaba o se cerraba más tarde.
  • En las placas, Display.loadBG con un fichero que no existe devuelve false y deja el fondo de antes, como en Windows; antes la placa se reiniciaba.
  • File.open con un modo que no es de la tabla, como "rw", lanza ERR_INVALID_VALUE; antes cerraba la VM en Windows, y en las placas "rw" vaciaba el fichero.
  • Bytes.new con un tamaño negativo lanza ERR_INVALID_VALUE; antes la VM terminaba.
  • Cadenas: find(subcadena, n) da la posición buena desde la tercera aparición, y con n menor que 1 lanza ERR_INVALID_VALUE; rightStrip() de una cadena de solo espacios la deja vacía; y replace("", otro) devuelve el texto igual, en vez de quedarse colgado.
  • texto[i] = … con algo que no es una cadena lanza ERR_TYPE_MISMATCH, y con una cadena vacía, ERR_INVALID_VALUE; antes un número cerraba la VM y una cadena vacía estropeaba la cadena.
  • toUnsigned() de un negativo da su valor sin signo, como decimal (-1 da 4294967295); antes devolvía el mismo entero.
  • toFloat() lee el número con todas sus cifras; antes lo recortaba a unas 7, y "1e39" daba infinito. Y en la VM de Windows, "nan".toFloat() da un NaN de verdad: antes salía un valor roto, y con "-nan" la VM se cerraba.
  • print y println escriben los decimales con las cifras justas para volver a leer el mismo número: 0.1 sale 0.1 (antes, 0.10000000000000001) y PI, 3.141592653589793; 0.1 + 0.2, que no es exactamente 0.3, sigue saliendo 0.30000000000000004. Sale lo mismo en Windows y en todas las placas (el ESP32-P4 y el ESP32-S31 ya escribían así). Los números redondos muy grandes salen en notación científica desde 1e15; antes, desde 1e17. toString() no cambia.
  • get() y contains() de un mapa aceptan las mismas claves que mapa[clave]: todo menos listas y mapas. Antes lanzaban ERR_TYPE_MISMATCH con una clave decimal o un objeto. Y en el ESP32-P4, mapa[-1] encuentra lo que se guardó en mapa[-1.0], como en las demás plataformas; antes no lo encontraba. El error de un mapa escrito con una lista o un mapa de clave ya no dice que solo valen cadenas, enteros, booleanos y nil.
  • toInt() de un texto con un número que no cabe en un entero lanza ERR_TYPE_MISMATCH; antes daba un número que no servía, distinto en Windows y en las placas.
  • En Windows, con showBG, autoClear borra el view antes de pintar la imagen de fondo, como en la placa; antes, lo que la imagen no tapaba se quedaba de un fotograma a otro.
  • En Windows, draw.cls() borra solo el view, como en la placa; antes borraba toda la ventana y, con ella, el marco de loadTFT.
  • En las placas, draw.bitmapBatch, draw.tilemap y las capas de draw.scene usan el transparente de la imagen (el de Bitmap.load o tcolor), como en Windows; antes tomaban siempre el negro.
  • En las placas, GPIO.awrite busca solo los canales PWM que tiene el chip (16 en el ESP32 y 8 en el S3, el P4 y el S31) y, si no queda ninguno libre, lanza ERR_OUT_OF_MEMORY, como en Windows; antes, en el S3 no hacía nada, y en el P4 y el S31 el noveno pin sacaba lo mismo que el primero. detach deja libre el canal, y getduty de un pin sin PWM da 0 sin darle canal.
  • grid.path con el origen igual que el destino da [x, y]; antes, nil.
  • Sprite.load lanza la excepción, con el nombre del fichero, si falta la imagen de un fotograma o de la hoja; antes devolvía un valor de error que try no veía.
  • En las placas, GPIO.awrite con un valor negativo saca 0, como en Windows; antes sacaba el máximo.
  • En el ESP32 y el ESP32-S3, I2C.read y transfer con más de 128 bytes, o con una longitud negativa, lanzan ERR_INVALID_VALUE; antes escribían fuera de su memoria.
  • En las placas, con los 32 timers en uso, after() y every() devuelven un valor de error, como en Windows; antes cancelaban sin avisar el primero de los que había.
  • En el ESP32 y el ESP32-S3, la WiFi vuelve sola: si la red se cae, la placa vuelve a conectar, y si no conecta al arrancar, conecta cuando aparece la red, y MQTT con ella. Antes, una caída la dejaba sin WiFi hasta reiniciarla, y sin red al arrancar MQTT no conectaba nunca.
  • En el ESP32 y el ESP32-S3, con varios puntos de acceso con el mismo nombre (una red en malla), la placa busca en todos los canales y prueba primero el de más señal. Antes se quedaba con el primero que encontraba, aunque estuviera lejos, y a veces no llegaba a conectar al arrancar.
  • En el ESP32-P4 y el ESP32-S31, HTTPS funciona y comprueba el certificado del servidor; antes las peticiones a direcciones https:// fallaban siempre.
  • MQTT en placa: con Interval ms por debajo de 1000, la placa usa 60 segundos; antes quedaba en 0, y en el ESP32 y el ESP32-S3 la conexión se cortaba en seguida.
  • En el ESP32 y el ESP32-S3, si el broker MQTT tarda en contestar al conectar, o no contesta, la placa espera como mucho 4 segundos y lo vuelve a intentar. Antes, a los 5 segundos se reiniciaba, y como al arrancar vuelve a conectar, se quedaba reiniciándose.
  • En el ESP32-P4 y el ESP32-S31, MQTT ya no rehace la conexión durante el primer minuto: una suscripción hecha nada más conectar se perdía sin que MQTT.isConnect() lo dijera. Si la conexión se pierde, vuelve a conectar cada 10 segundos.
  • MQTT en Windows entra en el broker con el Client ID, el User y el Password de la configuración; con User vacío, sin usuario. Antes entraba siempre como JARU_Win32 y sin usuario ni contraseña: un broker con contraseña no la dejaba entrar, y dos VMs en el mismo broker se echaban la una a la otra. MQTT.isConnect() da true cuando el broker ha aceptado la conexión, y si no la acepta, la consola lo dice.
  • MQTT en Windows ya no necesita internet: la VM espera al broker 3 segundos como mucho y, si no conecta, el programa empieza igual y ella lo sigue intentando cada 5 segundos; si se pierde la conexión, vuelve a conectar. Antes probaba www.google.com antes de empezar el programa: sin internet, o sin el broker, el programa no empezaba nunca, con un núcleo del procesador al 100 %, y si el broker se reiniciaba no volvía a conectar.
  • Mensajes de error de la VM: los de % dicen el operador (salía «operator ''»); los de draw.tilemap y tSize nombran grid.tileset = bitmap y grid.tSize(ancho, alto), que son los que existen; el de las interrupciones GPIO, en Windows, sale en inglés; y el trazado de un error grave de la VM ya no pone <<main>>.

IDE​

JARU Slate​

  • Un estilo oscuro común a todas las ventanas: los mismos colores para el contenido, los paneles, las tarjetas y los campos, botones planos, Segoe UI para la interfaz y Consolas para el código.
  • Azul solo para lo que está encendido, pulsado o es la acción principal; al pasar el ratón, el botón se aclara sin cambiar de color. Las cabeceras de los paneles acoplados marcan el que tiene el foco.
  • Las pistas (hints) se leen bien sobre fondo azul.
  • Los editores de sprites y de mapas abren un tercio más rápido.

Editor de sprites​

Editores de imágenes y mapas​

  • Imágenes: selección rectangular (mover, copiar, cortar y pegar), fila con los colores que usa la imagen, rejilla de tile, botón 1:1 y elipse sin suavizado. New Image propone el tamaño de la última.
  • Mapas: herramientas Rect y Line; Map Size se aplica al salir de la casilla.
  • Tracker: plantilla centrada, cabeceras enteras e instrumento con dos cifras.
  • Imágenes: las que crea o importa el IDE entran con Include in Build encendida; Include in Build y Palette se guardan en el proyecto en cuanto se cambian; Output sigue al tamaño de la imagen al deshacer, y guardar con Crop se puede deshacer.

Ejecutar y depurar​

  • Selector de destino en la barra (Windows o las placas del proyecto) para Run.
  • El Play ya no deja una consola abierta: lo que imprime el juego va al Output, igual que los errores.
  • Al parar en un punto de parada, el IDE dice dónde y se pone delante; F9, F5 y F10 funcionan en el editor; Watch se abre flotante.
  • Run en placa cuenta sus pasos, mira qué lleva la placa y, si no es la VM de JARU, ofrece Flash Firmware. Open Serial usa el puerto de la placa y vuelve tras el reinicio; Scan Ports elige el puerto que parece una placa.
  • Ventana de la VM x1 a x4 en Windows (configuración de Windows, panel Display): los juegos de 320x240 ya no salen diminutos en un monitor de PC.
  • Si el compilador se cae, no llega a conectarse o no arranca, el IDE ya no se queda en «compilando» con la cinta bloqueada: lo dice y vuelve a estar listo.
  • Una tecla pulsada en el Output con un programa en marcha ya no da un error del IDE; lo que pida el programa se sigue escribiendo en su consola.
  • Run vuelve a compilar también si han cambiado imágenes, sprites, mapas o música. Un sprite que nunca se guardó ya no para el Build (se salta con un aviso), y un fichero que no se puede leer ya no deja el IDE en «compilando».

Placas y hardware​

  • ESP32-S31 en el IDE: chip, pantalla RGB con sus tiempos y sus 21 pines, teclado USB y SD. El Python embebido lleva esptool 5.4.0, que sabe grabarlo.
  • Mando I2C seesaw (Adafruit Mini I2C Gamepad): bus I2C en el perfil de placa, botones en Game › Controls del editor de escenas y driver en el gestor de drivers para los juegos hechos a mano.
  • Driver del mando 1.2.0: dos fuentes en la misma acción (dos botones, o un botón y el joystick) se suman. Antes solo contaba la última, y en una acción sin tecla ni GPIO justPressed saltaba en cada fotograma.
  • El mando I2C de las salas usa solo los pines del perfil de tu placa. Si no tiene bus I2C, no abre el bus y lo dice en la consola; antes cogía los de otra placa del proyecto con el mismo chip, como los GPIO 11 y 10 de la JaruCade.
  • Perfiles de placa con el nombre de la placa, marca y modelo como los vende el fabricante: Waveshare_ESP32-S3-Touch-LCD-2.8, LILYGO_T4_v1.3, Espressif_ESP32-S31-Korvo-1… El chip, la pantalla y el bus ya salen debajo de la lista. Generic_SH8601Z_QSPI_(template) es una plantilla sin pines, no una placa.
  • Perfil JARU_JaruCade para la recreativa JaruCade: Waveshare ESP32-S3-LCD-2.8, con o sin táctil, con el mando I2C de Adafruit en su bus.
  • Los perfiles de placa de fábrica se ponen al día solos al abrir el IDE: si los tienes tal cual los dejó una versión anterior, se sustituyen por los nuevos; si los has editado, no se tocan.
  • New de ESP32 Tools ofrece los dispositivos de los proyectos recientes.
  • Si Target es una placa, elegir otra en Device de ESP32 Tools, guardarla o crear una nueva la pone también en Target, y contestar No a los cambios sin guardar deja la ventana con la placa guardada, su chip y su puerto. Antes Run podía anunciar una placa y mandar el boot.cfg de la otra, o por el puerto de otra.
  • Drivers I2C: el ejemplo del PCF8563 compila (guardaba la hora en una variable time), y sin IA, una dirección sin 0x se lee en decimal, como decía el formulario: 118 ya no sale como 0x118.
  • Get Info, Generate y Get MAC ponen el Device Name de la placa como Client ID de MQTT si sigue el de serie o está vacío. El de serie era el mismo en todas las placas, y dos con el mismo se echaban del broker.

Inteligencia del código​

  • El autocompletado baja por los campos de un struct, y Ctrl+clic llega a los de un struct definido en otro fichero.
  • El build avisa de lo que el editor subraya como error, y vuelve a revisar el editor después de generar los .gen.aru.
  • Si una función no se cierra por un else if, la pista lo dice.
  • El aviso de Input.map con un nombre de tecla en un programa para placa reconoce if (windows) cuando windows guarda platform() == HW_WIN32, y ya no avisa de líneas que en la placa no se ejecutan.

Avisos de actualización​

Otras mejoras​

  • Cerrar el proyecto, abrir otro o cerrar el IDE pregunta por lo que no se ha guardado en todos los editores, y Cancel no cierra nada. Reabrir o renombrar un sprite ya abierto, borrar un mapa que no es el abierto o guardar un mapa o una imagen nuevos ya no pierden trabajo.
  • New Project ya no escribe encima de una carpeta que tiene algo: lo dice y deja poner otro nombre.
  • Git: cambiar de rama guarda antes lo que hay sin guardar y ya no tira los cambios sin commit; si chocan con la otra rama, no cambia y lo dice. Recarga también los mapas y las salas abiertos.
  • Borrar desde el explorador manda el fichero a la papelera cuando la unidad la tiene (en un USB o en una carpeta de red se borra del todo, y la pregunta ya no promete la papelera), y el explorador da el tamaño real de cada fichero.
  • Las ventanas sueltas caben en la pantalla al abrirse y ya no tapan todo Windows.
  • El asistente de proyecto lleva el logo nuevo, ya no queda encima de otros programas y cerrarlo ya no puede tumbar el IDE.
  • La cinta enseña la ayuda de sus botones; la ventana principal sale en la barra de tareas.
  • Los textos que el IDE todavía enseñaba en castellano salen en inglés: el panel de Git (la columna Type, el detalle de un commit, New Branch y sus mensajes), el final de la consola de Debug, el aviso del Beautifier, varios errores de la Error List, el LED del simulador GPIO sin canal, las ayudas del módulo JSON, la categoría Other de los drivers I2C, un aviso de ESP32 Tools y los mensajes del desensamblador y de Watch.

Ejemplos​

  • Los 38 ejemplos del instalador, en inglés: textos de pantalla, mensajes y comentarios. Particulas pasa a llamarse Particles, y entran LanceOfAshes y FistOfAsphalt.
  • JaruBlocks, nuevo: bloques que caen, con música original.
  • El mando I2C de la JaruCade (Adafruit Mini I2C Gamepad) en los 23 juegos que se juegan: 20 con el driver en su código y 3 desde los controles del editor de escenas.
  • Cada ejemplo trae el perfil de placa JARU JaruCade y se abre sin el aviso de perfil no encontrado.
  • Los botones de la TTGO se asignan solo en la TTGO y las teclas solo en Windows: seis juegos reiniciaban la S3 en bucle y xnom no arrancaba en la S3 ni en el P4.
  • Los ejemplos ya no llevan las fuentes del arte, los scripts de Python ni los lanzadores, y PacMan sale del instalador.

Documentación​

Problemas conocidos​

  • Input.justPressed puede perder pulsaciones. Rodeo: leer Input.pressed() una vez por fotograma y calcular los flancos a mano.
  • Renombrar, mover o borrar un asset no reconstruye los recursos del Play del editor de escenas. Rodeo: borrar Build/assets.stamp antes de pulsar Play, o compilar con Build › Compile, que los reconstruye siempre.
  • Compilador: indexar lo que devuelve una llamada, f(x)[0], no funciona. Rodeo: guardarlo antes en una variable.
  • readLine() en placa no recibe entrada por serie: los ejemplos que piden un dato (CheckPrime, Factorial_ForLoop, FibonacciGenerator y Palindromes) se quedan esperando. En Windows funcionan.

La lista entera, con todos los problemas conocidos de la 0.9.7 y cómo evitarlos, está en Problemas conocidos.

Notas de instalación / actualización​

  • IDE Windows: instalador JARU_IDE_0.9.7_Setup. Si actualizas desde la 0.9.6, al terminar abre en el navegador las novedades de la 0.9.7; en una instalación nueva, los primeros pasos; al reinstalar la misma versión, nada.
  • Graba el firmware de la VM (Flash Firmware) en cada placa antes de subir programas compilados con 0.9.7. Con el firmware 0.9.6 no hay aviso: el programa se para en la primera instrucción nueva con «Unknown Opcode encountered».
  • Recompila el proyecto tras actualizar para regenerar los recursos (sprites en hoja, salas del editor de escenas, actores).
  • Perfiles de placa: al abrir el IDE nuevo, los de nombre viejo que no hayas editado desaparecen y salen los nuevos; uno que hayas editado se queda con su nombre de antes. Tus proyectos no cambian: el nombre del perfil del que salió cada placa es solo una referencia.
  • Revisa la tabla de cambios incompatibles: la división entre enteros, NaN, las funciones sin end, grid.set/grid.clear y los colliders volteados son los puntos que pueden pedir tocar código.