· ·
Raspberry Pi · Python

API Python

Biblioteques Python per controlar l'IoT-Vertebrae des de Raspberry Pi, amb bus CAN (recomanat) o bus I2C. Inclou referència per al simulador en línia.

CAN v2.1 (recomanada) I2C Simulador en línia Raspberry Pi
CAN bus — /boot/firmware/config.txt
# I2C + SPI
dtparam=i2c_arm=on
dtparam=spi=on
# MCP2515 (8 MHz, IRQ GPIO6)
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=6,spimaxfrequency=1000000
dtoverlay=spi-bcm2835-overlay
# Instal·lar python-can
sudo apt install -y python3-can
I2C — /boot/firmware/config.txt
# Activar I2C
dtparam=i2c_arm=on
# Instal·lar smbus
sudo apt install -y python3-smbus
Nota: Cada crida I2C obre i tanca el bus. No cal can_on() / can_off().
Bus CAN — can_iotv_v2_1.py

API recomanada per a Head02 (ESP32-S3). Comunicació via bus CAN a 100 kbps. Suporta callbacks asíncrons de canvis d'entrada digital.

Ús ràpid:
import can_iotv_v2_1 as iotv

iotv.can_on()
print(iotv.dversion('0000'))   # '1.5'
print(iotv.aversion('0000'))   # '1.4'
iotv.can_off()
⬇ can_iotv_v2_1.py ⬇ can_iotv_v2_0.py ⬇ Head02TestRPi02.py
Control del bus
None can_on() RPi

Activa la interfície CAN (can0) a 100 kbps. Executa sudo ip link set up can0 type can bitrate 100000. Cal cridar-la al principi de qualsevol programa.

None can_off() RPi

Atura tots els listeners actius, tanca el bus CAN i executa sudo ifconfig can0 down. El tancament també es fa automàticament en sortir del programa (atexit).

Vèrtebra digital
str din(str addr, str side) CAN

Llegeix les 8 entrades digitals d'un costat. Les entrades arriben en actiu baix des de la vèrtebra; la biblioteca les inverteix i retorna el byte en actiu alt com a string de 8 bits.

ParàmetreTipusDescripció
addrstrAdreça binària de 4 bits (ex: '0000')
sidestr'A' o 'B'
strString binari de 8 caràcters (ex: '10110100'), o 'Error' en timeout
exemple
val = iotv.din('0000', 'B')  # ex: '00100100'
di1 = bool(int(val[6]))        # bit DI1 (posició 6 des de l'esquerra)
None dout(str addr, str side, int value) CAN

Escriu el byte complet (0–255) a un costat de sortida digital. Els 8 LEDs/relès s'actualitzen en un sol missatge CAN.

exemple
iotv.dout('0000', 'A', 0xFF)  # tots encesos
iotv.dout('0000', 'A', 0x00)  # tots apagats
None doutbit(str addr, str side, int posbyte, int value) CAN

Escriu un bit individual de sortida sense afectar els altres. posbyte és 0 (DO0) a 7 (DO7). value és 0 o 1.

exemple
for bit in range(8):
    iotv.doutbit('0000', 'A', bit, 1)
    time.sleep(0.1)
    iotv.doutbit('0000', 'A', bit, 0)
None dsetup(str addr, str modeA, str modeB) CAN

Configura els modes de treball dels dos costats d'una vèrtebra digital. El costat A pot ser 'din', 'dout' o 'pwm'. El costat B afegeix 'touch'. Restriccions: PWM només pot estar a un costat; 'touch' només al costat B.

exemple
iotv.dsetup('0000', 'dout', 'din')
iotv.dsetup('0001', 'pwm', 'din')
str getdsetup(str addr) CAN

Llegeix la configuració actual de la vèrtebra digital i retorna un string llegible.

exemple
iotv.getdsetup('0000')  # 'A:dout, B:din'
str dversion(str addr) CAN

Retorna la versió del firmware de la vèrtebra digital (ex: '1.5'), o '0.0' en timeout.

Canvis d'entrada asíncrons (nou a v2.1)
Com funciona: La vèrtebra digital publica missatges espontanis quan detecta un canvi a qualsevol entrada. on_din_change() registra un callback que s'executa en un thread de fons sense fer polling, de manera que no col·lapsa el bus.
None on_din_change(str addr, callable callback) async

Registra un callback per a canvis d'entrada digital espontanis de la vèrtebra a addr. El callback rep tres arguments: addr (int, adreça i2c), val_a (byte del costat A, actiu alt), val_b (byte del costat B, actiu alt). El callback s'executa en un thread de fons (core separat del while True principal).

Thread-safety: No crideu funcions de bloqueig (sleep, can_on/off) dins del callback. Useu una variable compartida per coordinar amb el bucle principal.
exemple complet (Head02TestRPi02)
seq_running = False
last_di1    = None

def on_din_change(addr, val_a, val_b):
    global seq_running, last_di1
    di1 = bool(val_b & 0x02)   # DI1 = bit 1 del costat B
    if di1 == last_di1: return
    last_di1 = di1
    if di1:
        seq_running = True
    else:
        seq_running = False
        iotv.dout('0000', 'A', 0x00)

iotv.can_on()
iotv.dsetup('0000', 'dout', 'din')
iotv.on_din_change('0000', on_din_change)

while True:
    if seq_running:
        # ... lògica de seqüència ...
        time.sleep(0.05)
None off_din_change(str addr) async

Cancel·la el listener de canvis digitals per a l'adreça indicada. Atura el thread de fons de manera neta.

None off_all_din_change() async

Cancel·la tots els listeners actius. Es crida automàticament a can_off().

Vèrtebra analògica
int ain(str addr, str side, int ndac) CAN

Llegeix una entrada analògica. ndac és 1, 2, 3 o 4. Retorna el valor cru de 16 bits (0–26624) corresponent a −10V..+10V, o 'Error' en timeout.

exemple
raw = iotv.ain('0000', 'B', 1)
volts = iotv.ain2v(raw)   # ex: 4.98
None aout(str addr, str side, int ndac, int value) CAN

Escriu un valor cru (0–4095) a un canal DAC. ndac és 1–4. Useu v2aout() per convertir des de volts. Per enregistrar un valor a l'EEPROM del DAC (default en reset), sumeu 4096 al valor. Limiteu les escriptures EEPROM a menys de 20.000 vegades.

exemple
iotv.aout('0000', 'B', 1, iotv.v2aout(5.0))  # 5V al canal 1
# Enregistrar 9.3V a l'EEPROM del DAC canal 4:
iotv.aout('0000', 'B', 4, 4096 + iotv.v2aout(9.3))
str aversion(str addr) CAN

Retorna la versió del firmware de la vèrtebra analògica (ex: '1.4'), o '0.0' en timeout.

str getasetup(str addr) CAN

Llegeix la configuració de la vèrtebra analògica. Retorna string com 'A:ain, B:aout'.

Conversió de tensió
float ain2v(int ain_value) utilitat

Converteix el valor cru ADC (0–26624) a volts (−10.0 a +10.0 V, 2 decimals).

iotv.ain2v(26624)  # → 10.0
iotv.ain2v(13312)  # → 0.0
iotv.ain2v(0)      # → -10.0
int v2aout(float voltage_0_10) utilitat

Converteix una tensió entre 0 i 10 V al valor cru de 12 bits (0–4095) per al DAC. Els valors fora del rang es clampegen per evitar escriptures accidentals a l'EEPROM del DAC.

iotv.v2aout(10.0)  # → 4095
iotv.v2aout(5.0)   # → 2048
iotv.v2aout(0.0)   # → 0
Bus I2C — i2c_iotv.py

Compatible amb qualsevol versió de cap (Head01 i Head02). Cada crida obre i tanca el bus I2C; no cal inicialitzar un bus global.

Ús ràpid:
import i2c_iotv as iotv

print(iotv.dversion('0000'))  # 'Digital rib version: 1.2' → '0000000100000010'
print(iotv.din('0000', 'B'))    # '00100100'
⬇ i2c_iotv.py
Diferència respecte CAN: A I2C, din() retorna un string de 8 bits directament. No hi ha suport per a callbacks asíncrons (on_din_change). El canal analògic (ndac) és 1–4 igual que a CAN.
Vèrtebra digital
str din(str addr, str side) I2C

Llegeix les 8 entrades digitals d'un costat via I2C. Retorna un string de 8 bits (ex: '00100100').

exemple
val = iotv.din('0000', 'B')  # '00100100'
None dout(str addr, str side, int value) I2C

Escriu el byte complet (0–255) a un costat de sortida digital.

None doutbit(str addr, str side, int posbyte, int value) I2C

Escriu un bit individual de sortida (0–7) sense afectar els altres.

None doutpwm(str addr, str side, int value) I2C

Escriu un valor PWM (0–255) a tots els canals del costat configurat en mode PWM.

None doutbitpwm(str addr, str side, int posbyte, int value) I2C

Escriu un valor PWM (0–255) a un bit individual del costat configurat en mode PWM.

bool dsetup(str addr, str modeA, str modeB) I2C

Configura els dos costats. Modes A: 'ain', 'aout', 'aoutpwm'. Modes B: 'bin', 'bout', 'boutpwm', 'bintouch'. Retorna False si la combinació no és vàlida.

exemple
iotv.dsetup('0000', 'aout', 'bin')
iotv.dsetup('0000', 'ain', 'bout')
str dversion(str addr) I2C

Imprimeix 'Digital rib version: X.Y' i retorna un string binari de 16 bits que codifica la versió.

str getdsetup(str addr) I2C

Imprimeix la configuració actual i retorna un string binari de 8 bits que codifica el mode de cada costat.

exemple
iotv.getdsetup('0000')
# → imprimeix "A digital output, B digital input"
# → retorna '00010010'
Vèrtebra analògica
int ain(str addr, str side, int ndac) I2C

Llegeix una entrada analògica. ndac és 1–4. Retorna valor cru de 16 bits (0–26624).

exemple
v = iotv.ain2v(iotv.ain('0000', 'A', 4))  # 4.98
None aout(str addr, str side, int ndac, int value) I2C

Escriu un valor cru (0–4095) a un canal DAC. Per enregistrar a l'EEPROM, suma 4096 al valor (màxim 20.000 escriptures).

exemple
iotv.aout('0000', 'B', 4, iotv.v2aout(9.3))
str aversion(str addr) I2C

Imprimeix 'Analog rib version: X.Y' i retorna un string binari de 16 bits.

Conversió de tensió
int v2aout(float voltage_0_10) utilitat

Converteix 0–10 V a valor cru DAC (0–4095). Limita a [0, 4095].

iotv.v2aout(10.0)  # → 4095
iotv.v2aout(5.0)   # → 2048
float ain2v(int ainValue) utilitat

Converteix el valor cru ADC (0–26624) a volts (−10.0 a +10.0 V).

Simulador en línia — iotvSim

El simulador de iotvsim.binefa.cat executa codi Python transpilat a JavaScript. L'API és similar però amb diferències importants respecte a les biblioteques RPi.

Diferències respecte CAN/I2C
Funció / CaracterísticaRPi (CAN/I2C)Simulador
Importacióimport can_iotv_v2_1 as iotvobjecte iotv disponible directament (no cal import)
Inici del busiotv.can_on() / can_off()iotv.init() / iotv.reset()
dindin(addr, side) → string binaridin(addr, side) → string binari (igual)
doutdout(addr, side, value)dout(addr, side, value) (igual)
doutbitdoutbit(addr, side, pos, val)doutbit(addr, side, pos, val) (igual)
dinbitNo existeixdinbit(addr, side, bit) → int (0 o 1)
ain canalain(addr, side, ndac) ndac=1–4ain(addr, side, ch) ch=0–3
ainBatchNo existeixainBatch(addr, side) → array[4]
aoutBatchNo existeixaoutBatch(addr, side, voltages)
v2ainNo existeixv2ain(voltage) → int (rang ADC entrada)
aout2vNo existeixaout2v(raw) → float (0–10 V)
v2aoutv2aout(voltage)v2aout(voltage) (igual)
ain2vain2v(raw)ain2v(raw) (igual)
on_din_changeon_din_change(addr, cb) (v2.1)No existeix (polleig manual)
PLCMemoryNo existeixPLCMemory.set(key, val) / .get(key)
time.sleeptime.sleep(s)time.sleep(s) (igual, suporta floats)
Limitacions del transpilador (Python → JavaScript)
El codi Python es transpila a JS. Eviteu:
Sí que funciona:
Vèrtebra digital (simulador)
str din(str addr, str side) sim

Llegeix les 8 entrades digitals. Retorna string binari de 8 caràcters. Igual que a CAN/I2C.

int dinbit(str addr, str side, int bit) sim

Llegeix un bit individual d'entrada (0–7). Exclusiva del simulador.

exemple
estat = iotv.dinbit('0000', 'B', 1)  # 0 o 1
None dout / doutbit / doutpwm / doutbitpwm sim

Mateixa signatura que CAN/I2C. doutpwm(addr, side, val) escriu un valor PWM (0–255) a tots els canals. doutbitpwm(addr, side, bit, val) escriu PWM a un bit individual.

Vèrtebra analògica (simulador)
int ain(str addr, str side, int ch) sim

Llegeix una entrada analògica. Atenció: al simulador el canal ch va de 0 a 3 (a CAN/I2C va de 1 a 4). Retorna valor cru 0–4095.

exemple
# Simulador: ch 0..3
raw = iotv.ain('0000', 'B', 0)
v = iotv.ain2v(raw)
list ainBatch(str addr, str side) sim

Llegeix els 4 canals analògics d'un costat en una sola crida. Retorna una llista de 4 valors crus (0–4095). Exclusiva del simulador.

exemple
vals = iotv.ainBatch('0000', 'B')  # [1024, 2048, 0, 4095]
v0 = iotv.ain2v(vals[0])
None aoutBatch(str addr, str side, list voltages) sim

Escriu múltiples canals analògics de sortida alhora. voltages és una llista de fins a 4 valors en volts (0–10 V). Exclusiva del simulador.

exemple
iotv.aoutBatch('0000', 'A', [0.0, 5.0, 10.0, 2.5])
Exemple complet per al simulador
Codi generat per a la petició: "Fes un blink de DO5 del costat A de la vèrtebra 0000"
# Blink de DO5 (costat A, vèrtebra 0000)
# Funcions de primer nivell — sense try/except, sense classes

def setup():
    iotv.dsetup('0000', 'output', 'input')

def main():
    setup()
    while True:
        iotv.doutbit('0000', 'A', 5, 1)   # DO5 encès
        time.sleep(0.5)
        iotv.doutbit('0000', 'A', 5, 0)   # DO5 apagat
        time.sleep(0.5)

main()
Equivalent per a RPi (CAN v2.1):
import can_iotv_v2_1 as iotv
import time

iotv.can_on()
iotv.dsetup('0000', 'dout', 'din')
while True:
    iotv.doutbit('0000', 'A', 5, 1)
    time.sleep(0.5)
    iotv.doutbit('0000', 'A', 5, 0)
    time.sleep(0.5)