Si visitamos la wikipedia de la página del proyecto ultibo, encontramos una definición más técnica.
Ultibo core es un entorno completo para el desarrollo integrado o básico (sin sistema operativo) en Raspberry Pi (todos los modelos excepto Pico). No es un sistema operativo en sí, pero proporciona muchos de los mismos servicios que un sistema operativo, como administración de memoria, subprocesos, redes y sistemas de archivos. Con el objetivo principal de hacer que el desarrollo de aplicaciones integradas sea lo más similar posible al desarrollo para otras plataformas al proporcionar características y servicios comunes y eliminar la necesidad de comenzar desde cero, Ultibo core se puede utilizar de muchas maneras diferentes.
La respuesta corta sería, es un proyecto que usa Free Pascal y Lazarus para programar las placas Raspberry pi, como si fueran placas arduinos.
Estos es, nos permite el microprocesador las placas Rasberry Pi directamente, como si fuera un microcontrolador más, sin tener un sistema operativo. Además se puede interactuar con los periféricos, por ejemplo si se desea hacer uso de la comunicación SPI, debemos inicializar el la conexión SPI que lleva integrada el microprocesador de nuestra Raspberry PI, por ejemplo
1 |
SPIDevice:=PSPIDevice(DeviceFindByDescription('BCM2836 SPI0 Master')); |
Contenido
Hardware requerido.
Para empezar necesitas un placa de Raspberry Pi, da igual si es una Raspberry PI 1B, como una Zero, o la versión 4B. La última versión, a la hora de escribir este texto, es la versión 5 que aún no está soportada por Ultibo.
Además necesitarás una pantalla con entrada HDMI, una tarjeta SD para la placa Raspberry Pi (normalmente son microSD) y un lector de tarjetas SD, si tu ordenador no dispone de lector incorporado.
Instalando Ultibo.
Lo primero de todo es dirigirse a la página Ultibo, y en la sección de descargas, descargar el Core que corresponda al sistema operativo que tengamos.
Una vez instalado Ultibo, abrimos la aplicación y nos encontramos con el entorno de Lazarus, adaptado y preparado para empezar a crear el primer programa.
Para crear un proyecto, vamos al menú Proyecto y seleccionamos Nuevo Proyecto.
Debemos elegir la versión que corresponda con el modelo de Raspberry Pi que tengamos. Al pulsar OK, se creará un programa nuevo con todos los ajustes según el modelo elegido.
Primer programa. Hola Mundo
Siguiendo la tradición, vamos a crear el clásico saludo «Hola Mundo».
Añade la unidad Console, y escribe el siguiente código:
1 2 3 4 5 6 7 |
var windowHandle: TWindowHandle; begin windowHandle := ConsoleWindowCreate(ConsoleDeviceGetDefault,CONSOLE_POSITION_FULL, True); ConsoleWindowWriteLn(windowHandle, 'Hola mundo'); ThreadHalt(0); end. |
En la línea 2, declaramos una variable de tipo TWindowHandle, la cual contendrá la dirección de la consola que vamos a crear.
En la línea 4, llamamos a la función ConsoleWindowCreate, el primer parámetro es un puntero a una consola, en este caso indicamos que use la consola por defecto. El segundo parámetro es una constante que indica su posición, en este caso le indicamos que ocupe toda la pantalla. Y el último parámetro le indicamos que será, de ahora en adelante, la consola por defecto.
La línea 5, simplemente, escribimos una línea en la consola. Para ello como primer parámetro indicamos en que consola, en este caso, la que acabamos de crear. Y como segundo parámetro el texto a mostrar.
Para terminar, en la línea 6 detenemos este hilo.
Guarda, este programa en una carpeta. En el menú Ejecutar, seleccionar Compilar, o simplemente pulsa la combinación de teclas Ctrl+F9.
Preparar la tarjeta SD
El siguiente paso es preparar la tarjeta SD, para que la placa Raspberry Pi pueda arrancar desde ella y ejecutar nuestro programa
Cualquier tarjeta SD que tengas a mano te servirá, incluso una de poco capacidad, ya que el programa que hemos creado y los archivos necesarios apenas llegan a los 2 MB. Debes asegurarte que la tarjeta esté formateado con el formato FAT.
Debes copiar los siguientes archivos, los cuales son parte del firmware de la Raspberry Pi.
- bootcode.bin
- start.elf
- fixup.dat
En el caso de la Raspberry Pi 4, los archivos que debes copiar son los siguientes:
- start4.elf
- fixup4.dat
- armstub32-rpi4.bin
- config.txt
Estos archivos los puedes localizar en la carpeta dónde instalaste Ultibo, normalmente en C:\Ultibo\Core\firmware
Introduce la tarjeta en tu pc, bien en la ranura destinado para ello, o usando un adaptador, y copia los archivos del firmware según la versión de tu Raspberry Pi. Además copia el archivo que acabas de compilar. Lo encontrarás en la carpeta dónde has guardado el proyecto.
El archivo se llama Kernel.img, en el caso que hayas compilado para la versión de la Raspeberry Pi 2B o 3B se llamara Kernel7.img. Y si la versión es Raspberry Pi 4B, el archivo se llamara kernel7l.img.
Nombre archivo | Versión Raspberry PI |
Kernel.img | Raspberry A, B, A+, B+ or Zero |
Kernel7.img | Raspeberry 2B o 3B |
Kernel7l.img | Raspberry Pi 4B |
Tras copiar los archivos, coloca la tarjeta en la Raspberry Pi y enciéndala.
De ahora en adelante, cada vez que hagas un cambio en el programa, solo tienes que copiar a la tarjeta SD el archivo Kernel.
Actualizaciones por red.
El proceso de cargar nuestro programa colocando la tarjeta SD en el pc puede ser bastante latoso. Lo más cómodo es enviar por red el programa cada vez que deseemos. Este proceso se puede simplificar habilitando un servicio TFTP en nuestros programas.
Para ello, lo primero es conectar a tu router la placa Raspberry Pi con un cable ethernet. Luego descarga el archivo uFTP.pas y guárdalo en un carpeta. Este archivo lo debemos incluir en todos nuestros proyectos.
Al conectar la placa con un cable al router, este le asignará un dirección IP como a todos los dispositivos que haya conectados a la red. Debemos conocer esta dirección IP para enviar el programa.
Crea un proyecto nuevo, añade el archivo que acabas de descargar. Para ello ve al menú Archivo, luego seleccionar abrir.
Localiza el archivo que acabas de descargar y ábrelo.
Una vez abierto, añádelo al proyecto actual, para ello pulsa shift+F11, y acepta el siguiente mensaje.
Localizar la dirección IP
Crea el siguiente código para que cuando la placa Rasbperry Pi arranque muestre por consola su dirección Ip.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
program project1; {$mode objfpc}{$H+} { Raspberry Pi Application } { Add your program code below, add additional units to the "uses" section if } { required and create new units by selecting File, New Unit from the menu. } { To compile your program select Run, Compile (or Run, Build) from the menu. } { To build for the QEMU target select Project, Project Options ... from the } { menu, go to Config and Target and choose the appropriate Target Controller. } uses RaspberryPi, GlobalConfig, GlobalConst, GlobalTypes, Platform, Threads, SysUtils, Classes, uTFTP, Ultibo { Add additional units here }, Console, Winsock2; var windowHandle: TWindowHandle; IPAddress: string; function WaitForIPComplete: string; var TCP: TWinsock2TCPClient; begin TCP := TWinsock2TCPClient.Create; Result := TCP.LocalAddress; if (Result = '') or (Result = '0.0.0.0') or (Result = '255.255.255.255') then begin while (Result = '') or (Result = '0.0.0.0') or (Result = '255.255.255.255') do begin sleep(1000); Result := TCP.LocalAddress; end; end; TCP.Free; end; begin windowHandle := ConsoleWindowCreate(ConsoleDeviceGetDefault, CONSOLE_POSITION_FULL, True); ConsoleWindowWriteLn(windowHandle, 'Hola mundo'); IPAddress := WaitForIPComplete; ConsoleWindowWriteLn(windowHandle, 'Ip actual: ' + IPAddress); ThreadHalt(0); end. |
Compila el programa y guárdalo en la tarjeta SD, como hiciste antes.
Al arrancar saldrá un mensaje, por la consola indicando la dirección IP de la placa Raspberry PI.
Desde ahora, y siempre que el programa incluya el fichero uTFTP (fíjate que está añadido en la clausula uses del código anterior), podremos enviar un programa cliente TFTP.
Windows tiene, entre sus características un cliente TFTP, el cual se puede activar, desde el panel de control en el apartado programas y características.
Para probarlo, añade un nueva línea en el programa anterior, por ejemplo:
1 |
ConsoleWindowWriteLn(windowHandle,'Este programa fue cargado por TFTP'); |
Compila el programa, y abre una terminal de Windows y ve al directorio donde está el archivo Kernel.img y escribe:
1 |
TFTP -i 192.168.100.188 PUT kernel.img |
TFTP es el nombre del cliente, el parámetro -i indica que la conexión será en binario, luego la dirección IP de la Raspberry PI. PUT indica que vamos enviar un archivo, y por último el nombre del archivo. Tras un momentos, la placa Raspberry PI se reiniciará y arrancará con el programa nuevo.
Dirección IP estática.
Tener una dirección IP dinámica puede ser un inconveniente, por tanto podemos indicar una dirección estática a la placa Raspberry PI. Esto se puede hacer por código (fuente foro Ultibo).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
uses SysUtils, Classes, GlobalSock, Network, Transport, IP; function SetIPAddress(const AName,AAddress,ANetmask,AGateway:String):Boolean; var Adapter:TNetworkAdapter; Transport:TNetworkTransport; TransportAdapter:TIPTransportAdapter; begin {} Result:=False; if Length(AName) = 0 then Exit; if Length(AAddress) = 0 then Exit; if Length(ANetmask) = 0 then Exit; {Gateway can be empty} {Get Adapter} Adapter:=AdapterManager.GetAdapterByName(AName,True,NETWORK_LOCK_READ); if Adapter = nil then Exit; try {Get IP Transport} Transport:=TransportManager.GetTransportByType(AF_INET,PACKET_TYPE_IP,True,NETWORK_LOCK_READ); if Transport = nil then Exit; try {Get Transport Adapter (The connection between the Network device and the IP Transport)} TransportAdapter:=TIPTransportAdapter(Transport.GetAdapterByAdapter(Adapter,True,NETWORK_LOCK_WRITE)); if TransportAdapter = nil then Exit; {Set the Adapter Status to Down} TransportAdapter.Adapter.Status:=ADAPTER_STATUS_DOWN; {Unlock Transport Adapter} TransportAdapter.WriterUnlock; WriteLn(AName + ' set to ADAPTER_STATUS_DOWN'); WriteLn('Waiting until Transport Adapter is Unconfigured'); {Lock Transport Adapter (Reader)} TransportAdapter.ReaderLock; {Wait for Transport Adapter to be Unconfigured} while TransportAdapter.Configured do begin {Wait} Sleep(500); end; {Unlock Transport Adapter} TransportAdapter.ReaderUnlock; WriteLn('Setting IP, Netmask and Gateway'); {Lock Transport Adapter (Writer)} TransportAdapter.WriterLock; {Set the IP address, netmask and gateway} TransportAdapter.ConfigDefault:=CONFIG_TYPE_STATIC; TransportAdapter.ConfigAddress:=InAddrToHost(StringToInAddr(AAddress)); TransportAdapter.ConfigNetmask:=InAddrToHost(StringToInAddr(ANetmask)); if Length(AGateway) <> 0 then begin TransportAdapter.ConfigGateway:=InAddrToHost(StringToInAddr(AGateway)); end; {Set the Adapter Status to Up} TransportAdapter.Adapter.Status:=ADAPTER_STATUS_UP; {Unlock Transport Adapter} TransportAdapter.WriterUnlock; WriteLn(AName + ' set to ADAPTER_STATUS_UP'); Result:=True; finally Transport.ReaderUnlock; end; finally Adapter.ReaderUnlock; end; end; |
Haciendo una llamada a la función, de la siguiente manera, se puede establecer una dirección Ip fija.
1 |
SetIPAddress('Network0','192.168.1.55','255.255.255.0','192.168.1.1'); |
Existe otra posibilidad, para establecer una dirección Ip estática, sin tener que cambiar o recompilar el código. Para ello debemos crear un archivo llamado cmdline.txt. Colocarlo en el directorio raíz de la tarjeta. Y dentro de este archivo podemos especificar diversos comandos. Por ejemplo:
1 2 3 4 |
NETWORK0_IP_CONFIG=STATIC NETWORK0_IP_ADDRESS=<IP Address> (example NETWORK0_IP_ADDRESS=192.168.123.123) NETWORK0_IP_NETMASK=<Subnet Mask> (example NETWORK0_IP_NETMASK=255.255.255.0) NETWORK0_IP_GATEWAY=<Router Address> (example NETWORK0_IP_GATEWAY=192.168.123.1) |
Más información de los comandos y ejemplos está disponible en el foro de ultibo.
Conclusiones
Iniciarse en la programación embebida a veces puede ser frustrante debido a la cantidad de horas y trabajo necesario para poder llegar a escribir una sola línea de código. La instalación de Ultibo, nos proporciona el compilador Free Pascal, con una colección de bibliotecas, así como el entorno de desarrollo integrado Lazarus, todo ello listo para empezar a programar.