Tema 14 · Bloque E · Sistemas informáticos. Programación

Tema 14. Estructuras de control, funciones y primer programa en la placa

Cómo decide y cómo repite un programa, cómo se parte en piezas con nombre propio y cómo se hace que actúe sobre el mundo físico.

Este es el tema central del bloque. Con los condicionales y los bucles se completan las tres estructuras de la programación estructurada del Tema 12; con las funciones, el programa deja de ser una lista de líneas y se convierte en algo que se puede organizar y comprobar por partes; y con la placa, el código sale de la pantalla. Las funciones llegan aquí a propósito, y no al final del bloque: sin ellas, todo lo que viene después se escribiría de una sola pieza.

Antes de empezar: el criterio 5.3 del currículo pide «mostrar el progreso paso a paso de la ejecución de un programa y predecir su estado final». Ese ejercicio es fácil con seis líneas y casi imposible con sesenta. Esa es la razón profunda por la que existen las funciones: no ahorran escritura, hacen que un programa se pueda entender.
Al terminar serás capaz de…
  • escribir condicionales simples, dobles y múltiples, y condiciones compuestas;
  • recorrer una secuencia con un bucle for y usar contadores y acumuladores;
  • escribir un bucle while con una condición de parada que se cumpla de verdad;
  • reconocer un bucle infinito, distinguir cuándo es un error y cuándo es lo deseado, e interrumpirlo;
  • combinar selección e iteración en estructuras anidadas sin perder la legibilidad;
  • definir funciones con parámetros y valor de retorno, y llamarlas;
  • explicar el ámbito local y global, y separar cálculo, entrada y presentación;
  • trazar la ejecución de una función y predecir su estado final;
  • escribir en MicroPython un programa que encienda una salida, lea una entrada digital y mida un sensor analógico en el ESP32.

1. Condicionales: if, else

Un condicional ejecuta un bloque solo si se cumple una condición. La condición es una expresión booleana de las del Tema 13.

# Selección simple: hacer algo o no hacer nada
humedad = 410
UMBRAL = 500

if humedad < UMBRAL:
    print("Sustrato seco: hay que regar")
# Selección doble: uno de los dos caminos, siempre
if humedad < UMBRAL:
    print("Sustrato seco: hay que regar")
else:
    print("Humedad suficiente: no regar")
Los dos puntos y el sangradoLa línea del if termina siempre en dos puntos, y lo que depende de la condición va sangrado cuatro espacios. No hay llaves ni fin si: el bloque termina cuando el sangrado vuelve al nivel anterior. Es la sintaxis del Tema 12 en acción, y el IndentationError más frecuente viene de aquí.

Condiciones compuestas

humedad = 410
hora = 8
modo_manual = False

# Tres condiciones a la vez, con nombres del Tema 13
seco = humedad < UMBRAL
en_horario = 7 <= hora < 10

if seco and en_horario and not modo_manual:
    print("Regar 30 s")
else:
    print("No regar")
Las condiciones se evalúan de izquierda a derecha y con cortocircuito. En a and b, si a es falso Python ni siquiera mira b: el resultado ya está decidido. Eso permite un truco muy útil en programas de control: poner primero la comprobación que evita el error. if lecturas > 0 and total / lecturas < 500: nunca divide entre cero, porque si lecturas es cero la segunda parte no se evalúa.

2. Selección múltiple: elif

lectura = 2847   # cuentas del ADC de 12 bits

if lectura < 1200:
    estado = "saturado"
elif lectura < 2000:
    estado = "húmedo"
elif lectura < 3000:
    estado = "seco"
else:
    estado = "muy seco o sensor desconectado"

print("Estado del sustrato:", estado)   # seco
Solo se ejecuta una rama, y el orden importaPython comprueba las condiciones en orden y, en cuanto una es verdadera, ejecuta su bloque y sale de toda la estructura. Por eso no hace falta escribir elif 1200 <= lectura < 2000: si hemos llegado a ese elif, ya sabemos que la lectura no era menor de 1200. Y por eso el orden no es indiferente: si pusieras lectura < 3000 primero, las lecturas saturadas entrarían por esa rama y las dos siguientes no se alcanzarían nunca.
El else final es una red de seguridad. En la tabla anterior recoge el caso «muy seco o sensor desconectado», y no es un detalle decorativo: un sensor con el cable suelto da lecturas al máximo, indistinguibles de un sustrato desértico. Un programa de control debe tener siempre una rama para «esto no debería pasar», porque en el huerto pasa. Lo formalizaremos en el Tema 18 como estado de fallo.

3. Bucles de recorrido: for

Un bucle for repite un bloque una vez por cada elemento de una secuencia. Es el bucle que se usa cuando se sabe de antemano cuántas repeticiones hay.

# Recorrer una lista de medidas
medidas = [420, 515, 380, 610]
for medida in medidas:
    print("Lectura:", medida)

# Recorrer un rango de números
for i in range(5):        # 0, 1, 2, 3, 4
    print(i)

for vuelta in range(1, 5):    # 1, 2, 3, 4
    print("Vuelta", vuelta)
range(5) da cinco números, y el último es 4El límite superior no se incluye. Es la fuente inagotable del error «me falta un elemento» o «me sobra uno». range(5) es 0, 1, 2, 3, 4; range(1, 5) es 1, 2, 3, 4. Si lo que quieres son las vueltas 1 a 4 del husillo, escribe range(1, 5) y comprueba imprimiéndolo antes de fiarte.

Contadores y acumuladores

medidas = [420, 515, 380, 610]

total = 0          # acumulador: se inicializa a 0
lecturas = 0       # contador: se inicializa a 0
maxima = medidas[0]

for medida in medidas:
    total = total + medida
    lecturas = lecturas + 1
    if medida > maxima:
        maxima = medida

media = total / lecturas
print(f"Media: {media:.1f}  Máxima: {maxima}  Lecturas: {lecturas}")
# Media: 481.3   Máxima: 610   Número de lecturas: 4
Los tres patrones que resuelven casi todoContador: empieza en cero y suma uno en cada vuelta; sirve para «cuántos». Acumulador: empieza en cero y suma el valor; sirve para «total» y, dividido por el contador, para la media. Extremo: empieza con el primer elemento y se actualiza si encuentra algo mayor o menor; sirve para «el máximo» y «el mínimo». Con estos tres patrones se trata cualquier serie de medidas, que es exactamente lo que hará el Tema 15 con los datos del huerto.
Inicializar el extremo a cero es un error. Si buscas el mínimo y empiezas con minima = 0, ningún valor positivo será menor y el resultado será siempre cero. Y si buscas el máximo de temperaturas que pueden ser negativas, empezar en cero también falla. La forma robusta es empezar con el primer elemento de la serie, como en el código anterior.

4. Bucles con condición de parada: while

Un bucle while repite un bloque mientras se cumpla una condición. Se usa cuando no se sabe cuántas repeticiones harán falta.

# Esperar a que el sustrato se humedezca, comprobando cada segundo
import time

tiempo = 0
humedad = 410

while humedad < UMBRAL and tiempo < 60:
    time.sleep(1)
    tiempo = tiempo + 1
    humedad = leer_humedad()      # función que definiremos más adelante

if humedad >= UMBRAL:
    print(f"Umbral alcanzado en {tiempo} s")
else:
    print("Aviso: 60 s de riego sin alcanzar el umbral")
Todo while de un sistema de control necesita dos condiciones de salidaUna es la deseada —se alcanzó el umbral— y la otra es el límite de seguridad: un tiempo máximo, un número máximo de intentos. Sin la segunda, un sensor averiado deja el programa regando indefinidamente. Ese es el «nunca más de 30 segundos» del enunciado del Tema 12, y ahora ya sabemos dónde se escribe. La regla del curso: un bucle que espera algo del mundo físico siempre lleva tiempo máximo.
Elegir el bucle
SituaciónBucleEjemplo en el KR-1
Se sabe cuántas repeticiones.for con rangeTomar 10 lecturas para promediar.
Hay una colección que recorrer.for sobre la colecciónProcesar la lista de medidas del día.
Se repite hasta que ocurra algo.whileEsperar a que la humedad suba, con tiempo máximo.
Debe repetirse siempre.while TrueEl bucle principal del sistema de control.

5. Bucles infinitos, break y continue

Un bucle infinito es el que nunca termina. En un programa de cálculo es un error; en un programa de control, es exactamente lo que se quiere, y es lo que representaba la flecha de retorno del diagrama de flujo del Tema 12.

# El bucle principal de un sistema de control: no termina nunca
while True:
    humedad = leer_humedad()
    if humedad < UMBRAL:
        regar(30)
    time.sleep(3600)      # esperar una hora

Bucle infinito por error

La condición nunca cambia porque olvidaste incrementar el contador, o porque la variable de la condición se modifica dentro de un if que no siempre se cumple. El programa se queda colgado.

Bucle infinito por diseño

while True con una espera dentro. Es la forma normal de un programa que gobierna una máquina: mientras haya corriente, vigila. Se detiene desconectando la placa.

# break sale del bucle; continue salta a la siguiente vuelta
medidas = [420, 515, -1, 380, 4095, 610]

suma = 0
validas = 0
for medida in medidas:
    if medida < 0 or medida > 4090:
        continue                  # lectura no válida: la salta y sigue
    if medida == 0:
        break                     # fallo grave: sale del bucle
    suma = suma + medida
    validas = validas + 1

print(f"{validas} lecturas válidas, media {suma / validas:.1f}")
# 4 lecturas válidas, media 481.3
Filtrar lecturas no válidas es trabajo de ingeniería, no de programaciónLas dos lecturas descartadas —un valor negativo y un 4095— no son datos: son síntomas. Un −1 significa que la función de lectura falló; un 4095 en un ADC de 12 bits es el máximo absoluto, y suele indicar que el sensor está desconectado. Un programa de control que promedia ciegamente todas las lecturas acabará decidiendo con basura. Saber qué valores son imposibles es conocimiento del Tema 11, no del lenguaje.
Cómo se interrumpe un bucle que se ha colgado. En Colab, el botón de detener la celda. En Thonny, el botón de parada o Ctrl+C en la consola. Y en la placa, Ctrl+C en el intérprete interactivo detiene el programa y devuelve el control. Conviene saberlo antes de escribir el primer while True, no después.

6. Estructuras anidadas

Anidar es poner una estructura dentro de otra. Cada nivel añade cuatro espacios de sangrado, y ahí está tanto su potencia como su peligro.

# Promediar tres series de lecturas: un for dentro de otro for
series = [[420, 515, 380], [610, 590, 640], [300, 310, 290]]

for numero, serie in enumerate(series, start=1):
    total = 0
    for medida in serie:
        total = total + medida
    media = total / len(serie)
    if media < UMBRAL:
        print(f"Serie {numero}: media {media:.0f} — seco")
    else:
        print(f"Serie {numero}: media {media:.0f} — húmedo")
Más de tres niveles de sangrado: algo va malCuando un bloque llega al cuarto nivel de sangrado, el programa ha dejado de ser legible y la traza del criterio 5.3 se vuelve impracticable. La solución no es reordenar el sangrado: es extraer una función, que es justo lo que viene en la sección siguiente. Un bucle anidado dentro de un if dentro de otro bucle dentro de otro if siempre se puede escribir como dos funciones legibles.
Piensa: en el código anterior, la variable total se pone a cero dentro del bucle exterior. ¿Qué pasaría si la inicialización estuviera fuera, antes del primer for? (Las series se acumularían unas sobre otras y las medias de la segunda y la tercera serían absurdas. Es un error de una sola línea de sangrado y de los más difíciles de ver.)

7. Funciones

Una función es un bloque de código con nombre propio que se puede ejecutar cuantas veces se quiera. Se define una vez con def y se llama escribiendo su nombre con paréntesis.

def cuentas_a_porcentaje(cuentas, seco=3000, saturado=1200):
    """Convierte una lectura del ADC en porcentaje de humedad.

    cuentas: lectura del ADC, de 0 a 4095
    seco, saturado: valores de calibración del Tema 11
    Devuelve un porcentaje entre 0 y 100.
    """
    if cuentas >= seco:
        return 0.0
    if cuentas <= saturado:
        return 100.0
    return (seco - cuentas) / (seco - saturado) * 100


# Llamadas a la función
print(cuentas_a_porcentaje(3000))    # 0.0
print(cuentas_a_porcentaje(2100))    # 50.0
print(cuentas_a_porcentaje(1200))    # 100.0
print(cuentas_a_porcentaje(2847))    # 8.5
Las partes de una función
ParteQué esEn el ejemplo
DefiniciónLa línea def, terminada en dos puntos.def cuentas_a_porcentaje(...):
ParámetrosLos nombres que recibe, entre paréntesis.cuentas, seco, saturado
Valor por omisiónValor que toma un parámetro si no se le pasa.seco=3000
DocumentaciónEl docstring del Tema 13.El bloque entre triples comillas.
CuerpoEl bloque sangrado que se ejecuta.Los tres if y el cálculo.
Valor de retornoLo que devuelve con return; termina la ejecución.El porcentaje.
ArgumentosLos valores concretos que se pasan al llamarla.2847
LlamadaEl nombre con paréntesis, donde se necesite.cuentas_a_porcentaje(2847)
Para qué sirven de verdad las funciones1. Reutilizar: la conversión se escribe una vez y se usa cincuenta. 2. Nombrar: cuentas_a_porcentaje(x) dice lo que hace mejor que tres líneas de aritmética. 3. Probar: se puede comprobar la función sola, con valores conocidos, sin ejecutar todo el sistema. 4. Cambiar sin miedo: si mañana se recalibra el sensor, se toca una función y no cincuenta sitios. Esta última es la razón por la que en la industria nadie escribe programas sin funciones.
# Y ahora la función se puede probar sola, con los casos del Tema 13
assert cuentas_a_porcentaje(3000) == 0.0
assert cuentas_a_porcentaje(1200) == 100.0
assert cuentas_a_porcentaje(5000) == 0.0      # fuera de rango: no rompe
assert abs(cuentas_a_porcentaje(2100) - 50.0) < 0.1
print("La conversión funciona")
return termina la función ahí mismo. En cuanto se ejecuta un return, la función devuelve el valor y no sigue leyendo su cuerpo. Eso permite el estilo del ejemplo: resolver primero los casos especiales y dejar el cálculo general al final, sin else anidados. Y una función sin return devuelve None, que es el valor «nada»: si al imprimir el resultado de tu función aparece None, te has olvidado de devolver algo.

8. Ámbito de las variables

El ámbito de una variable es la parte del programa en la que existe.

UMBRAL = 500              # variable global: visible en todo el archivo

def hay_que_regar(humedad):
    margen = 20           # variable LOCAL: solo existe dentro de la función
    return humedad < UMBRAL - margen

print(hay_que_regar(410))   # True
# print(margen)             # NameError: margen no existe aquí

Variable local

Nace al entrar en la función y desaparece al salir. Nadie de fuera puede verla ni estropearla. Es lo normal y lo deseable.

Variable global

Definida fuera de toda función; visible desde cualquier parte. Útil para las constantes de configuración, peligrosa para todo lo demás.

Las funciones reciben y devuelven; no espíanUna función bien escrita recibe todo lo que necesita como parámetros y comunica el resultado con return. Si en lugar de eso lee variables globales y las modifica, deja de ser comprobable: su resultado depende del estado del resto del programa, y ya no se puede probar por separado. La excepción aceptada son las constantes de configuración en mayúsculas —UMBRAL—, que se leen pero nunca se modifican.

Separar cálculo, entrada y presentación

# MAL: una función que lo mezcla todo
def procesar():
    valor = int(input("Lectura: "))
    print("Humedad:", (3000 - valor) / 18)

# BIEN: tres responsabilidades, tres funciones
def pedir_lectura():
    return int(input("Lectura del ADC: "))

def cuentas_a_porcentaje(cuentas):
    return (3000 - cuentas) / 18

def mostrar(porcentaje):
    print(f"Humedad: {porcentaje:.1f} %")

# El programa principal solo enlaza las tres
lectura = pedir_lectura()
humedad = cuentas_a_porcentaje(lectura)
mostrar(humedad)
Por qué la segunda versión es mucho mejor. La función de cálculo se puede probar con assert, porque no pide nada por teclado ni imprime nada. Y cuando en la sección siguiente el dato venga del sensor de la placa en lugar del teclado, solo hay que cambiar una función: el cálculo y la presentación siguen intactos. Esa es la separación que convierte un programa de aula en un programa que sobrevive al cambio de hardware.

9. Trazar una función

Ahora la traza del Tema 12 sobre una función, que es como el criterio 5.3 resulta practicable.

def media_valida(medidas, minimo=0, maximo=4090):
    total = 0
    validas = 0
    for m in medidas:
        if minimo <= m <= maximo:
            total = total + m
            validas = validas + 1
    if validas == 0:
        return None
    return total / validas

print(media_valida([420, 4095, 515]))
Traza de media_valida([420, 4095, 515])
PasoQué ocurremtotalvalidas
1Entra en la función; se crean las locales.00
21.ª vuelta: 420 está en rango.4204201
32.ª vuelta: 4095 > 4090, no entra en el if.40954201
43.ª vuelta: 515 está en rango.5159352
5validas no es 0: no devuelve None.5159352
6return 935 / 2 → 467,5. Las locales desaparecen.
Predecir antes de ejecutarEl ejercicio que pide el currículo no es rellenar la tabla después de ver el resultado: es escribir el estado final antes de pulsar ejecutar, y después comprobar. Cuando la predicción y el resultado coinciden, has entendido el programa. Cuando no coinciden, has encontrado o un error del programa o un hueco en tu comprensión, y las dos cosas son valiosas. Hazlo a mano unas cuantas veces: lo que el depurador de Thonny mostrará en el Tema 15 es exactamente esta tabla, generada automáticamente.

10. Primer programa en la placa

Ha llegado el momento. MicroPython es Python adaptado a microcontroladores: el mismo lenguaje, la misma sintaxis, y unos módulos propios para hablar con los pines. Lo que has aprendido hasta aquí sirve tal cual.

Pines del ESP32: a la izquierda alimentación de 3,3 voltios y masa, los GPIO 36, 39, 34 y 35 de ADC1 y solo entrada, y los GPIO 32 y 33 de ADC1 de entrada o salida; a la derecha entrada de 5 voltios, masa y los GPIO 23, 22, 21, 19, 18 y 5 como salida y PWM, más los GPIO 25 y 26 de ADC2 utilizables solo como salida.
Los pines que se usan en el curso. La sonda va al GPIO 34 porque pertenece a ADC1, el único convertidor que sigue funcionando con el Wi-Fi activo.

Salida digital: encender un LED

from machine import Pin
import time

led = Pin(23, Pin.OUT)        # GPIO 23 como salida

for i in range(5):
    led.value(1)              # encender
    time.sleep(0.5)
    led.value(0)              # apagar
    time.sleep(0.5)

Entrada digital: leer un pulsador

from machine import Pin
import time

led = Pin(23, Pin.OUT)
boton = Pin(18, Pin.IN, Pin.PULL_UP)   # resistencia de pull-up interna

while True:
    if boton.value() == 0:             # con pull-up, pulsado = 0
        time.sleep(0.02)               # antirrebote del Tema 11
        if boton.value() == 0:
            led.value(not led.value())  # cambia de estado
            while boton.value() == 0:   # esperar a que suelte
                time.sleep(0.01)
    time.sleep(0.01)
El antirrebote, resuelto por programaEl Tema 11 explicaba que un contacto mecánico rebota durante unos milisegundos. Aquí está la solución en software que allí se anunciaba: al detectar la pulsación, esperar 20 ms y volver a comprobar. Si sigue pulsado, era real; si no, era un rebote. Y el bucle final espera a que se suelte, para que una pulsación larga no cuente como muchas. Son cinco líneas que sustituyen a un condensador, y es el ejemplo perfecto de que un problema físico puede resolverse en hardware o en software.

Entrada analógica: leer la sonda de humedad

from machine import Pin, ADC
import time

sonda = ADC(Pin(34))                  # GPIO 34: ADC1
sonda.atten(ADC.ATTN_11DB)            # margen de medida hasta ~3,3 V

SECO = 3000
SATURADO = 1200

def leer_humedad(n=10):
    """Devuelve el porcentaje de humedad promediando n lecturas."""
    total = 0
    for i in range(n):
        total = total + sonda.read()
        time.sleep(0.01)
    cuentas = total / n
    if cuentas >= SECO:
        return 0.0
    if cuentas <= SATURADO:
        return 100.0
    return (SECO - cuentas) / (SECO - SATURADO) * 100

while True:
    print(f"Humedad: {leer_humedad():.1f} %")
    time.sleep(2)
Fíjate en lo que acaba de pasarLa función leer_humedad() es la misma que escribimos en la sección 7, con dos cambios: el dato ahora viene del ADC en lugar de un argumento, y promedia diez lecturas para atenuar el ruido del Tema 11. El cálculo no ha cambiado, porque estaba separado en su propia función. Y la promediación es una decisión de ingeniería, no de programación: diez lecturas separadas 10 ms filtran el ruido eléctrico sin retrasar nada apreciable.
Cuatro cosas antes de conectar la placa1. El pin de la sonda tiene que ser de ADC1 —GPIO 32 a 39—, o dejará de leer al activar el Wi-Fi en el Tema 16. 2. Ninguna señal por encima de 3,3 V. 3. La electroválvula no se conecta a un pin: va con el MOSFET y el diodo del Tema 11. 4. Las masas de la placa y de la alimentación de 12 V, unidas. Las cuatro reglas vienen del Bloque D y las cuatro se incumplen el primer día si nadie las recuerda.
Y si no hay placa para todos los equipos: Wokwi simula el ESP32 con MicroPython en el navegador, incluidos el LED, el pulsador y un potenciómetro que hace de sonda. El programa que funciona en el simulador funciona en la placa casi sin cambios, y permite que todos los equipos avancen a la vez.

Comprueba lo que has aprendido

Este tema se demuestra con un programa organizado en funciones que funciona en la placa, y con una predicción de traza acertada.

Qué debería mostrar tu trabajo
AspectoQué debes demostrarEvidencias posibles
CondicionalesQue usas selección múltiple con el orden correcto y una rama final de seguridad.Clasificación del estado del sustrato con elif y rama para sensor desconectado.
BuclesQue eliges for o while con criterio y aplicas los tres patrones.Contador, acumulador y extremo sobre una serie real de medidas; un while con tiempo máximo.
FuncionesQue tu programa está organizado en funciones probables por separado.Al menos tres funciones con docstring, parámetros y retorno, y sus assert.
Separación de responsabilidadesQue el cálculo no imprime ni pide datos.La función de cálculo reutilizada sin cambios al pasar del teclado al sensor.
TrazaQue predices el estado final antes de ejecutar.Tabla de traza de una función con bucle y condicional, con la predicción fechada antes de la ejecución.
La placaQue gobiernas una salida, lees una entrada digital con antirrebote y mides un analógico.Los tres programas funcionando, con vídeo o captura, y la sonda en un pin de ADC1.

Prueba de fuego del tema: cambia la calibración del sensor. Si te basta con modificar dos constantes al principio del archivo, has organizado bien el programa. Si tienes que buscar números por todas partes, todavía no.

Glosario del tema

Acumulador
Variable que se inicializa a cero y va sumando valores dentro de un bucle.
Ámbito
Parte del programa en la que una variable existe y es accesible.
Anidar
Colocar una estructura de control dentro de otra.
Antirrebote
Técnica que descarta las conmutaciones espurias de un contacto mecánico.
Argumento
Valor concreto que se pasa a una función al llamarla.
Bucle infinito
Bucle que no termina; error en un cálculo y comportamiento deseado en un programa de control.
Contador
Variable que se inicializa a cero y se incrementa en cada vuelta de un bucle.
Cortocircuito de evaluación
Propiedad por la que una condición compuesta deja de evaluarse en cuanto el resultado está decidido.
Función
Bloque de código con nombre propio, parámetros y valor de retorno, que puede llamarse cuantas veces se quiera.
Global
Variable definida fuera de toda función y visible desde cualquier parte del archivo.
Local
Variable que nace al entrar en una función y desaparece al salir de ella.
MicroPython
Versión de Python adaptada a microcontroladores, con módulos para acceder a los pines.
Parámetro
Nombre que aparece en la definición de una función y recibe un valor al llamarla.
Pull-up
Resistencia que fija el nivel alto de una entrada digital en reposo; el ESP32 la tiene integrada.
Return
Instrucción que devuelve un valor y termina inmediatamente la ejecución de la función.
Valor por omisión
Valor que toma un parámetro cuando no se le pasa argumento.

Resumen esencial

  1. La línea de un if, un for, un while o un def termina en dos puntos, y su bloque va sangrado cuatro espacios.
  2. Las condiciones compuestas se evalúan con cortocircuito, lo que permite poner primero la comprobación que evita el error.
  3. En una cadena de elif solo se ejecuta una rama, y el orden de las condiciones cambia el resultado.
  4. Un programa de control necesita siempre una rama final para «esto no debería pasar»: en el huerto, pasa.
  5. for se usa cuando se sabe cuántas repeticiones o hay una colección; while, cuando se repite hasta que ocurra algo.
  6. range(5) da cinco números y el último es 4: el límite superior no se incluye.
  7. Contador, acumulador y extremo son los tres patrones que resuelven casi cualquier serie de medidas; el extremo se inicializa con el primer elemento, nunca a cero.
  8. Todo bucle que espera algo del mundo físico lleva un límite de seguridad además de su condición deseada.
  9. Un while True con espera dentro es la forma normal del bucle principal de un sistema de control.
  10. Descartar lecturas imposibles es conocimiento del sistema, no del lenguaje: un −1 o un 4095 son síntomas, no datos.
  11. Pasado el tercer nivel de sangrado, la solución no es reordenar: es extraer una función.
  12. Las funciones sirven para reutilizar, nombrar, probar por separado y cambiar sin miedo.
  13. Una función recibe por parámetros y comunica por return; leer y modificar variables globales la vuelve imposible de comprobar.
  14. Separar cálculo, entrada y presentación permite pasar del teclado al sensor cambiando una sola función.
  15. El ejercicio del criterio 5.3 consiste en predecir el estado final antes de ejecutar, y después comprobar.
  16. MicroPython es el mismo Python: Pin gobierna una salida o lee una entrada, y ADC mide un analógico, siempre en un pin de ADC1.

Fuentes y recursos fiables