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:
- 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.
- 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.
- 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.
- 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
- Editor de escenas. Un recurso nuevo, la escena (
.scn): capas de tiles, de imagen y de objetos con parallax, doce comportamientos que se añaden sin escribir código, reglas de la sala, máquinas de estados, rutas, variables que pasan de una sala a otra y una vista previa que ejecuta la sala en la VM. - El intérprete va más rápido. Las superinstrucciones dan 1,47-1,48x sobre JaruBench en ESP32, ESP32-S3 y ESP32-P4, y las tablas de saltos suman otro 1,2-1,7x en ESP32 y S3.
- Menos memoria en placa. Sin el heap poisoning del core de Arduino, con las cadenas en un solo bloque, con las constantes repetidas compartidas y con un BMP por sprite en vez de uno por fotograma, cada juego gasta decenas de KB menos de RAM interna. JaruGoody carga en la TTGO en 6,1 s en vez de 49,4 s.
- Ritmo fijo:
Display.fps = 30hace queDisplay.update()espere al siguiente paso y diga cuántos tocan;Display.dtda los segundos reales entre fotogramas. - Editor de sprites nuevo, con deshacer, corte por lotes, pivote y collider editables, y actores y poses: un actor agrupa los sprites de un personaje y el IDE genera el código que los carga.
- JARU Slate, un estilo oscuro común a todas las ventanas del IDE.
- ESP32-S31 con pantalla RGB de 800x480, teclado USB y SD, y mando I2C (Adafruit Mini I2C Gamepad) configurable desde el IDE.
sprite.contacts()devuelve la normal y la profundidad de cada choque, para quien resuelve sus propias colisiones.- Ejemplos del instalador en inglés, con JaruBlocks, nuevo y con música original, y el mando I2C de la recreativa JaruCade en los 23 juegos que se juegan.
Cambios incompatibles
| Cambio | Qué 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 unCode/SceneRuntime.gen.arucompartido 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.arugenerado 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.gmapy 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,falsepara ignorarlo onilpara que lo cree la escena.- Lo que es del juego y no de una sala vive en
Scenes/Game.jsony 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.gamees el mismo objeto en todas las salas,room.dtsale deDisplay.dt,SceneInputMap()mapea los controles ySceneGameReset()empieza una partida nueva.
Capas y cámara
- Capas de tiles con posición libre, parallax y repetición (Wrap). Una de ellas es el mapa de escena, el que se pinta justo debajo de los sprites. Las capas con Wrap enseñan en el lienzo una copia atenuada a cada lado.
- Capas de imagen, con parallax y desplazamiento automático en px/s: fondos, nubes.
- Capas de objetos, y capas de decorado: objetos que se pintan en su sitio de la pila pero no entran en la física ni en las reglas.
- Mapa de colisión con valores sólidos y plataformas de un solo sentido, o por el número de tile de la capa que lo pinta (Tile numbers).
- Límites y modo de la cámara (CLAMP, FREE, WRAP), seguimiento de un objeto y un marco con la pantalla del dispositivo que se arrastra para ver qué enseñará la 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
- Código de creación de la sala y código por objeto, en ventanas que se editan como el editor del IDE: autocompletado (también de
room.yroom.game.), firmas, Ctrl+clic y errores subrayados.
Trabajar en el lienzo
- Zoom hacia el cursor, fantasma al colocar, selección múltiple, guías de ajuste a otros objetos (Alt lo suelta), alinear y repartir, y orden de dibujo paso a paso.
- Panel de problemas: avisa de lo que no va a funcionar antes de ejecutar (una llamada a una función que no existe, un comportamiento que no puede mover su cuerpo, un juego sin entrada en la placa, la capa de colisión que no se mueve con el mapa...) y en algunos casos lo arregla con un clic.
- Play compila la sala sola y la ejecuta en la VM; lo que imprime el juego llega al Output del IDE. Los gizmos dibujan en el lienzo lo que hace cada comportamiento sin ejecutar nada.
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-infoen 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 últimosDisplay.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.prevXysprite.prevY, de lectura y escritura: el punto desde el que barrehitsGrid. 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 deInput(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.getPixeldevuelve el color en Windows, como ya hacía en placa (antes imprimía un texto y devolvíanil).Input.mapacepta los GPIO 0-63 en todas las placas.HW_ESP32S31(= 7) yplatform()lo devuelve en el ESP32-S31.bitmap.tcolor(color)cambia el color transparente del bitmap; antes no hacía nada. En placa,draw.bitmapydraw.gridBitmapusan el color transparente del bitmap (el deBitmap.load(fichero, color)o el detcolor) en vez del negro fijo, como ya hacían Windows y los sprites.foreachsobre 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.unsubscribeexiste también en Windows, y sin conexiónpublish,subscribeyunsubscribeno hacen nada (antes, en Windows,publishysubscribecerraban el programa).MQTT.pubinfo()publica la versión de la VM en<usuario>/<id>/devicey devuelvetrue, ofalsesin 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 dewakeOnPin(), 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()ywakeOnTimer()ya no escriben mensajes[DEBUG]en la consola. - I2C:
readReg16ywriteReg16respetan su último argumento,littleEndian; antes lo ignoraban y usaban siempre Big Endian. Si un programa pasabatruee 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
angleo conangularVeltransmite 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;hitsGridya 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.getenvuelve con wrap y acepta decimales; los errores dehits,get,setyseenombran el método real.
Compilador
- Un
returnsin valor justo antes deelsif,else,case,catchoelforya 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,breakycontinuedentro de untrysueltan 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
varse evalúan en el orden en que se escriben: envar a = 1, b = a,bvea. Antes el orden dependía de los nombres. Un nombre repetido en la misma sentencia da error, como con dosvar. - 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
.jxra medias, y compilar desde la línea de órdenes sin-odeja el.jxrjunto 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.cppse cortaba y perdía la comilla de cierre. x++yx--ya no se comen el resto de la expresión:var c = a++ + bsumab. 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, como5++, dan un error.- En un
enum, la cuenta sigue bien detrás de un nombre con un número: conROJO, VERDE = 5, AZUL,AZULvale 6, no 7, y conA = -1, B,Bvale 0. Un nombre repetido da un error; antes valía el primero. - Un
operatorque no se puede sobrecargar, o con parámetros de más o de menos, y unimportcircular 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
endque falta, sale detrás de lo último escrito y diceend 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, comolen,absorgb, 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-18se quedaba en0y1.23456789e-10en1.2345679e-10, y un programa con un decimal de1e110o más no cargaba (Error loading file). Solo uno menor que2.2e-308, sin contar el0, se queda en0.
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.loadTFTya pinta fuera del view (también en el S31). - Sprites:
framecon decimales se redondea hacia abajo;loopacepta enteros;addImageconserva el pivote propio;yoyoydir = -1animan de verdad, yyoyocon un solo fotograma ya no tumba la VM;Sprite.loadya 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.foreachsobre 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 unforeachsobre ese mapa el recorrido ya no repite ni se salta entradas, y sobrescribir no cambia el orden dekeys(),printniJSON.stringify. - Leer una propiedad que no existe dice su nombre.
- Un
spi_hostde 2 enboot.cfgya no tumbaDisplay.opencuando la pantalla no es SPI. - Sin
jaru_appenboot.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 ym.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.textcon 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.OUTPUToGPIO.OPEN_DRAINse ve como una salida digital, y el LED se enciende conGPIO.write. Antes se veía como un PWM sin canal. MQTT.publish,subscribeyunsubscribecon algo que no es una cadena lanzanERR_TYPE_MISMATCH; antes, un número cerraba la VM.JSON.stringifyescribe 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
frameigual aframesya 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 hastaresumeMusic()ostopMusic().- Con el zumbador, las notas con duración de
Sound.tone,pulseynoisese callan solas al acabar, como con I2S; antes sonaban hastaSound.stop(), y el canal seguía ocupado:isPlaying()dabatruey una nota de menos prioridad se descartaba. - Con el zumbador y sin
Sound.init(),Sound.isPlaying,loadSampleyplaySampledevuelvenfalse, como con el sonido arrancado; antes podían hacer caer la VM. - En las placas,
File.opencon"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 datError; antes no devolvía nada y descolocaba las variables de la función. Y en las placas,type(e)del error de uncatchdatError, como en Windows (antes, 25).draw.bytescon 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.loadBGcon un fichero que no existe devuelvefalsey deja el fondo de antes, como en Windows; antes la placa se reiniciaba. File.opencon un modo que no es de la tabla, como"rw", lanzaERR_INVALID_VALUE; antes cerraba la VM en Windows, y en las placas"rw"vaciaba el fichero.Bytes.newcon un tamaño negativo lanzaERR_INVALID_VALUE; antes la VM terminaba.- Cadenas:
find(subcadena, n)da la posición buena desde la tercera aparición, y connmenor que 1 lanzaERR_INVALID_VALUE;rightStrip()de una cadena de solo espacios la deja vacía; yreplace("", otro)devuelve el texto igual, en vez de quedarse colgado. texto[i] = …con algo que no es una cadena lanzaERR_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 (-1da4294967295); 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.printyprintlnescriben los decimales con las cifras justas para volver a leer el mismo número:0.1sale0.1(antes,0.10000000000000001) yPI,3.141592653589793;0.1 + 0.2, que no es exactamente 0.3, sigue saliendo0.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 desde1e15; antes, desde1e17.toString()no cambia.get()ycontains()de un mapa aceptan las mismas claves quemapa[clave]: todo menos listas y mapas. Antes lanzabanERR_TYPE_MISMATCHcon una clave decimal o un objeto. Y en el ESP32-P4,mapa[-1]encuentra lo que se guardó enmapa[-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 lanzaERR_TYPE_MISMATCH; antes daba un número que no servía, distinto en Windows y en las placas.- En Windows, con
showBG,autoClearborra 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 deloadTFT. - En las placas,
draw.bitmapBatch,draw.tilemapy las capas dedraw.sceneusan el transparente de la imagen (el deBitmap.loadotcolor), como en Windows; antes tomaban siempre el negro. - En las placas,
GPIO.awritebusca 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, lanzaERR_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.detachdeja libre el canal, ygetdutyde un pin sin PWM da 0 sin darle canal. grid.pathcon el origen igual que el destino da[x, y]; antes,nil.Sprite.loadlanza 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 quetryno veía.- En las placas,
GPIO.awritecon un valor negativo saca 0, como en Windows; antes sacaba el máximo. - En el ESP32 y el ESP32-S3,
I2C.readytransfercon más de 128 bytes, o con una longitud negativa, lanzanERR_INVALID_VALUE; antes escribían fuera de su memoria. - En las placas, con los 32 timers en uso,
after()yevery()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_Win32y 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()datruecuando 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.comantes 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 dedraw.tilemapytSizenombrangrid.tileset = bitmapygrid.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
- Rehecho: deshacer y rehacer, corte por lotes (marquesina, "Cut All" y detección automática), tira de fotogramas que se marca, reordena, duplica e invierte, pivote y collider editables con guías siempre visibles, silueta del fotograma anterior y recorte del aire de la hoja.
- Actores y poses: un actor agrupa los sprites de un personaje (quieto, andar, saltar...). El editor enseña la barra de poses, el fantasma de otra pose para alinearla y el pivote común; el explorador encuentra los sprites por actor; y el Build genera
Code/Actors.gen.aru, con una estructura por actor y las funciones que la cargan y la copian (ActorTikiLoad(),ActorTikiCopy(tiki)). - Un sprite nuevo nace a 8 fps y hereda la hoja y el fondo del anterior; "Fit to content" abraza todas las poses del actor; Mayúsculas+clic marca un tramo de fotogramas en el Sprite Manager.
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
justPressedsaltaba 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_JaruCadepara 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.cfgde 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 sin0xse lee en decimal, como decía el formulario:118ya no sale como0x118. - 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.mapcon un nombre de tecla en un programa para placa reconoceif (windows)cuandowindowsguardaplatform() == HW_WIN32, y ya no avisa de líneas que en la placa no se ejecutan.
Avisos de actualización
- El aviso de versión nueva tiene Download, Remind me later (vuelve a los 7 días) y Skip this version, y un enlace a las novedades. Una actualización importante sale en cada arranque.
- El IDE puede enseñar también avisos cortos para ciertas versiones, por ejemplo de un fallo conocido.
- Sigue sin enviar nada: descarga el mismo fichero que todos y compara las versiones en tu equipo, donde se guarda también lo que eliges en cada aviso.
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
- Guía nueva, Tu primer juego con salas: las salas del editor de escenas, el código que generan, el bucle del juego y cómo se cambia de sala.
- Guías al día con lo nuevo:
Display.fpsyDisplay.dt,sprite.contacts,prevX/prevY, la hoja de fotogramas ydivy/en la guía de operadores. - Corregidos los ejemplos que usaban funciones que no existen (
Display.redraw,GPIO.IN,draw.rectfill...) y las páginas debitmap,powerytimer.
Problemas conocidos
Input.justPressedpuede perder pulsaciones. Rodeo: leerInput.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.stampantes 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 sinend,grid.set/grid.cleary los colliders volteados son los puntos que pueden pedir tocar código.