Traducción al español del artículo original de Adafruit: CircuitPython Essentials y editado para el módulo Nonthue M0

Bienvenida

En este artículo recorreremos los funciones de CircuitPython y veremos algunos ejemplos que serán las bases para poder comenzar a programar y desarrollar nuestros proyectos.

Los ejemplos están hechos para ser programados en el módulo Nonthue M0 pero son compatibles (realizando algunos pequeños cambios como la asignación de pines) con otros módulos similares de Adafruit y otras empresas.

Circuitpython se puede programar con cualquier editor de texto (como el bloc de notas en Windows) pero este lenguaje requiere de exactitud en la sintaxis para evitar errores (veras que un espacio de más nos generará errores!) por ello recomendamos utilizar Mu Editor y hemos hecho un tutorial para instalarlo al que puedes acceder desde aquí.

Finalmente, la mejor forma de aprender es con práctica, para ello: comencemos!

Funciones incorporadas en el lenguaje

Todo lo habitual como if, elif, else, for, while funciona al igual que en otros lenguajes de programación.

Matemáticas

Al utilizar import math se accede a las funciones matemáticas disponibles:
‘name’, ‘e’, ‘pi’, ‘sqrt’, ‘pow’, ‘exp’, ‘log’, ‘cos’, ‘sin’, ‘tan’, ‘acos’, ‘asin’, ‘atan’, ‘atan2’, ‘ceil’, ‘copysign’, ‘fabs’, ‘floor’, ‘fmod’, ‘frexp’, ‘ldexp’, ‘modf’, ‘isfinite’, ‘isinf’, ‘isnan’, ‘trunc’, ‘radians’, ‘degrees’
CyrcuitPython soporta hasta 30-bits de números decimales, así que podes usar int o float cuando lo necesites.

Tuples, listas, vectores y diccionarios

Podes organizar datos con [], {} y () incluyendo strings, objetos, floats, etc.

Números al azar

Para obtener números al azar:

import random

random.random() devuelve un número decimal entre 0 y 1.0.

random.randint(min, max) devuelve un número entre el mínimo y máximo.

Digital In & Out

Para poder interactuar con el hardware se debe aprender a manejar las entradas y salidas digitales.
El ejemplo dado muestra como usar al mismo tiempo entradas y salidas digitales. Podes usar un botón para la entrada y el LED rojo integrado del módulo (D13) como la salida.

Encontrando los pines

Para este ejemplo vamos a utilizar el pin D10 y GND para el botón, y el pin D13 para encender y apagar el LED rojo que se encuentra integrado al módulo.

Otros pines que podemos utilizar

Para el ejemplo anterior se pueden utilizar todos los pines digitales (color violeta en PINOUT).

Tip: Debemos tener en cuenta que los pines cuentan en su mayoría con múltiples funciones y alguna de ellas las podemos estar utilizando ya en nuestro programa. Los resultados podrían verse afectados por ello.

Analog In

Este ejemplo muestra como leer un valor de tensión en una entrada analógica.

Creando la entrada analógica

analog1in = AnalogIn(board.A3)

Crea el objeto analogIn y lo asigna al pin A3 del módulo.

Limitar valor analógico

Por defecto, las lecturas analógicas van desde 0 (mínimo) hasta 65535 (máximo). Con la función getVoltage(pin) se convierten los valores de 0-65535 a 0-3.3V.

Si bien el módulo Nonthue M0 tiene un ADC (Convertidor Analógico-Digital) de 12 bits, Circuitpython convierte las lecturas a 16 bits.

Loop principal

El loop principal es bastante simple. Lo que hace es leer el valor del objeto analógico con get_voltage. Para ver los valores, debemos usar el monitor serie (Seriado en MU Editor o accediendo con la aplicación Putty).

Cambiando el valor del pin

Si no se conecta nada al pin analógico que hemos elegido en nuestro programa, se podrá observar que los valores en el monitor serie varian aleatoriamente. Si se quiere verificar el cambio de valor en el pin analógico, solo basta con conectar un cable a ese pin y después hacer contacto con GND o 3.3V. Esto hará que los valores del monitor serie vayan a 0 (GND) o 3.3 (3.3V).

Si se utiliza el programa Mu Editor, se podrá ver en un gráfico como varían los valores de lectura del pin al girar la perilla del potenciómetro.

Encontrando los pines

Para este ejemplo vamos a utilizar el pin A3. Se necesita también de los pines 3.3V y GND. Todos estos pines se encuentra cercanos al conector USB.

Otros pines que podemos usar

Para el ejemplo anterior se deben utilizar los pines analógicos (color verde en PINOUT).

Tip: Debemos tener en cuenta que los pines cuentan en su mayoría con múltiples funciones y alguna de ellas las podemos estar utilizando ya en nuestro programa. Los resultados podrían verse afectados por ello.

Analog Out

Este ejemplo muestra cómo se utiliza el DAC (Digital to Analog Converter/Conversor Digital-Analógico).

Creando una salida analógica

analog_out = AnalogOut(A0)

Crea el objeto analog_out y lo asigna al pin A0 (DAC), el único pin con DAC del módulo.

Configurando la salida analógica

El DAC es unas salida de 10-bits. En teoría, se tiene una resolución de 0.0032V por bit.

Por ejemplo:

Si escribo 0 en el rango, es lo mismo que setearlo a 0V.

Si escribo 5000 en el rango, es lo mismo que setearlo a  5000 / 64 = 78 y 78 / 1024 * 3.3V = 0.25V

Si escribo 65535 en el rango, es lo mismo que setearlo a 3.3V

Para verificar la salida analógica en este ejemplo, se recomienda utilizar un osciloscopio.

El microcontrolador ATSAMD21G18 del módulo Nonthue M0 tiene, como si indica arriba, un convertidor digital-analógico de 10 bits. Sin embargo, Circuitpython acepta un rango de escritura de 16 bits (0-65535) que luego es convertido a los 10 bits del DAC.

Encontrando los pines

Para este ejemplo vamos a utilizar el pin DAC0 o A0. Este pin se encuentra cercano al conector USB y del lado izquierdo de los pines analógicos.

Otros pines que podemos usar

Para el ejemplo anterior solo se puede usar el pin D14, A0 o DAC0 que tiene una salida analógica (color fucsia en PINOUT)

Audio Out

Con Circuitpython se pueden reproducir desde tonos hasta archivos .wav. Con la ayuda de la librería audioio se pueden utilizar las funciones play, pause y resume. Podes tener 3V pico a pico de salida analógica o salida de I2S.

El primer ejemplo muestra como reproducir un tono al pulsar el botón. El segundo ejemplo muestra como reproducir un archivo .wav que se encuentra guardado en la memoria flash del módulo.

En el código se va a utilizar el pin A0 (DAC0) que es el único que tiene DAC.

Reproducir tono

Primero se crea el objeto del botón, se lo asigna al pin D10 y se lo setea como entrada con pull-up.

Teniendo en cuenta que el por volumen por defecto del tono es muy alto, se añadió tone_volumen. Se puede cambiar el valor que viene por defecto en el programa o se puede conectar un potenciómetro para controlarlo. El valor del volumen va de 0 a 1.0.

Para cambiar la frecuencia del tono, se debe modificar frequency al valor deseado.

Después, se genera un periodo de senoidal con la función math.sin y se lo asigna a sine_wave.

Luego, creamos el objeto de audio y lo asignamos al pin A0 (DAC0).

Creamos una muestra de la señal utilizando RawSample con sine_wave como parámetro.

Dentro del loop chequeamos que el se haya presionado el botón. El botón tiene dos estados True o False. El valor por defecto de button_value es True para cuando no se presionó. Para chequear que se haya presionado, se utiliza el valor False.

Una vez que el botón se presionó,  se utiliza play para reproducir el tono. El tono se reproduce por 1 segundo definido en time.sleep(1) y se detiene la reproducción con stop. Para aumentar el tiempo del tono se puede cambiar el valor de time.sleep().

Reproducir archivo WAV

Este módulo solamente acepta archivos .wav en formato MONO a 22KHz o menos y formato de 8-bits. La razón por la que solo acepta MONO, es porque tiene una sola salida DAC. Debe ser a 22KHz o menos, ya que Circuitpython no puede manejar más información que esa y la salida DAC es de 10-bits, por eso todo lo que sea mayor a 8-bits va a tomar más espacio pero sin mejorar calidad.

Los archivos deben ser menores a 2 Mb.

Para dar formato a los archivos .wav se recomienda utilizar el programa Audacity.

  1. Se arrastra el archivo a Audacity.
  • Revisar las propiedades del archivo.

En este caso, el archivo seleccionado es de 32-bits a 44KHz y estereo. Esto no es compatible con el módulo, y por ende se debe modificar.

  • Dar formato al archivo (en caso de ser necesario).
  • Pasar a MONO:

Se debe seleccionar toda la pista y después buscar en la barra superior la opción “Pista”, luego se debe ir a “Mezcla” y por último se debe seleccionar “Mezclar pista estéreo a mono”.

  • Convertir el archivo a 16-bits:
  • Cambiar la frecuencia a 22KHz o menos:

                      En la barra inferior del programa hay un recuadro que dice “Frecuencia    (Hz)”. Apretar ahí para cambiar la frecuencia a 22050 Hz o menor.

  • Exportar archivo como WAV:
  • Guardar el archivo como WAV (Microsoft) :

El archivo se va a reproducir por 6 segundos, pausarse y reanudar cuando se apriete el botón.

Se crea el objeto del botón en el pin D10 y se lo setea como entrada con pull-up.

Luego abrimos el archivo “ArchivoDeAudio.wav” como readable binary y se guarda en el objeto wave_file, el cual es el que usa para leer el archivo de audio: wave_file = open(“ArchivoDeAudio.wav, “rb”).

Ahora vamos a cargar los datos del archivo de audio con wave = audioio.WaveFile(wave_file) y finalmente lo reproducimos por A0 (DAC0) con audio = audioio.AudioOut(board.A0).

El archivo está listo y se puede reproducir en cualquier momento con audio.play(wave).

Dentro del loop, reproducimos el archivo de audio.

Después se encuentra el bloque de tiempo que hace esperar 6 segundos antes de interrumpir la reproducción. Para esto se usa time.monotonic() debido a que al no bloquear el resto del código, se puede hacer otra cosa como controlar servos o LEDs. Despues de cierto tiempo, time.monotinic() es igual al número de segundos desde que el módulo se enciende. Cuando es llamada, devuelve un número decimal. Cuando se asigna time.monotonic() a una variable, esa variable es igual al número de segundos de time.monotonic() al momento de ser asignado a la variable. Se puede llamar nuevamente a time.monotonic() para restarle la primera variable para obtener la cantidad de tiempo que paso.

Entonces, asignamos t = time.monotonic() como punto de partida. Después, ponemos pass hasta que la diferencia de t y time.monotonic() sea mayor a 6 segundos. Se puede agregar código entre medio de la espera de los 6 segundos y se va a ejecutar sin problema.

Luego, usamos pause para detener el audio y por consola se puede leer «Esperando que se pulse el botón para volver a reproducir!».

Ahora vamos a esperar hasta que se pulse el botón con while button.value, que es lo mismo que decir, mientras el botón no este presionado, devolver True, en caso de ser pulsado, devolver False.

Una vez que se pulsa el botón, se vuelve a reproducir el audio con resume. Se finaliza de reproducir el audio con audio.playing: pass.

Como último, se puede leer «Hecho!» en el monitor serie cuando se termine de reproducir el audio.

Encontrando los pines

Para este ejemplo vamos a utilizar el pin DAC0 o A0. Este pin se encuentra cercano al conector USB y del lado izquierdo de los pines analógicos. También, se va a utilizar el pin D10. Este pin se encuentra en la fila de conectores cercana al conector de la batería.

Atención: En la imagen anterior el parlante se encuentra conectado de forma directa al pin DAC0. Sin embargo esta conexión tiene solo un fin didáctico. En realidad será necesario un circuito amplificador para que el parlante pueda reproducir un sonido. El sensor Nonthue Speaker incluye un amplificador y por ello se puede conectar de forma directa al DAC.

Otros pines que podemos usar

Para el ejemplo anterior solo se puede usar el pin D14, A0 o DAC0 que tiene una salida analógica (color fucsia en PINOUT)

PWM

Este módulo soporta la función pulseio, la cual permite generar señales PWM a LEDs, controlar servos, hacer sonidos con buzzers y manejar trenes de pulsos para sensores como el DHT11 o emisores infrarrojos.

Para utilizar la función pulseio con PWM, se deben utilizar pines con soporte para PWM.

PWM con frecuencia fija

Este ejemplo utiliza el LED integrado en el módulo (pin D13 o LED_BUILTIN).

Ya que se utiliza el LED del módulo, vamos a llamar al objeto led y usamos pulseio.PWMOut para crear la salida por el pin deseado (en este caso el D13).

Nos queda asi: led = pulseio.PWMOut(board.D13, frequency=5000, duty_cycle=0).

En el loop principal usamos range() para regular la intensidad del LED. Cuando range es menor a 50, el PWM hace más brilloso al LED. Cuando range es mayor a 50, el PWM hace menos brilloso al LED.

PWM con frecuencia variable

La frecuencia fija es ideal para controlar LEDs y servos, pero si se quiere utilizar un buzzer, se debe variar la frecuencia.

El ejemplo utiliza pulseio para hacer una serie de tonos en el buzzer.

En caso de tener la librería simpleio cargada en el directorio /lib del módulo, el programa se hace más fácil.

Encontrando los pines

Este ejemplo utiliza los pines D10 y GND. Estos pines se encuentra en la fila de conectores cercana al conector de la batería. El pin que se elija (en este caso D10) para enviar la señal al buzzer debe soportar PWM, de lo contrario no funcionará.

Otros pines que podemos usar

Para el ejemplo anterior se pueden utilizar todos los pines PWM (señal senoidal ~ en el PINOUT).

Tip: Debemos tener en cuenta que los pines cuentan en su mayoría con múltiples funciones y alguna de ellas las podemos estar utilizando ya en nuestro programa. Los resultados podrían verse afectados por ello.

Servo

Con los servos se puede utilizar tanto pulseio como adafruit_motor. En estos ejemplos vamos a utilizar adafruit_motor.

Los servos vienen de dos tipos:

  • Servo standard: el eje se mueve 180 grados en total (90 para cada lado desde el 0).
  • Servo continuo: el eje se mueve 180 grados como un motor de continua, pero en lugar de setearle ángulos, se dan valor de aceleración como por ejemplo 1.0 para máxima velocidad, 0.5 para media velociad y 0 para frenado. Los mismos valores pueden usarse con signo negativo.

Servo standard

En el ejemplo se hace que el servo recorra desde 0 grados hasta 180 grados (-90 grados a 90 grados y vuelve). Esto lo hace con un incremento de 5 grados por cada paso que da el servo.

Servo continuo

En este ejemplo, el objeto servo es creado de forma distinta al anterior ejemplo: my_servo = servo.ContinuousServo(pwm).

En lugar de utilizar myservo.angle, se utiliza my_servo.throttle utilizando los valores desde 1.0 hasta 0. Para ir en reversa, se utilizan valores desde -1.0 hasta 0. Cada valor intermedio es una velocidad parcial.

El ejemplo hace funcionar al servo al máximo para adelante por 2 segundos, lo frena 2 segundos y lo hace funcionar al máximo para atrás por 2 segundos.

Encontrando los pines

Este ejemplo utiliza los pines D10, VPOW y GND. D10 es el pin por el cual se transmite la señal que controla los movimientos del servo y debe ser PWM. VPOW es el pin positivo de la alimentación y GND el negativo. Los servos usualmente funcionan con 5V. Para fines didácticos tanto los 5V de VPOW (cuando esta conectado el USB) o los 4,2V (cuando este conectada la batería) servirán para moverlo sin problemas. Sin embargo, si se quiere usar en un proyecto específico, será necesario alimentarlo con un circuito externo.

Atención: El servo NO debe alimentarse nunca de los 3,3V del módulo Nonthue ya que el regulador no tiene capacidad para alimentarlo y la tensión será demasiado baja para un correcto funcionamiento.

Otros pines que podemos usar

Para el ejemplo anterior se pueden utilizar todos los pines PWM (señal senoidal ~ en el PINOUT).

Tip: Debemos tener en cuenta que los pines cuentan en su mayoría con múltiples funciones y alguna de ellas las podemos estar utilizando ya en nuestro programa. Los resultados podrían verse afectados por ello.

Cap Touch

El módulo Nonthue M0 tiene como característica que en las entradas analógicas tiene sensores táctiles capacitivos.

Este ejemplo utiliza la funcionalidad táctil capacitiva del módulo.

Primero se asigna la variable touch_pad a un pin. Este ejemplo utiliza el pin A2, por ello definimos touch_pad = board.A2. Después creamos el objeto touch y lo asociamos con touch_pad.

Se hace un loop en el cual se verifica si el pin fue tocado y se imprime por consola (en el monitor serie).

No se necesita ningún otro componente, ya que con solo tocar el pin alcanza. Para experimentar se puede utilizar un cable y conectarlo a elementos conductivos como por ejemplo cubiertos, frutas u otras comidas, líquidos o papel aluminio.

Cada vez que se cambie el objeto, se debe recargar el código o reiniciar el módulo porque el código “calibra” la sensibilidad dependiendo del objeto que se le ponga. Si hay demasiadas respuestas de toque o no hay ninguna, se necesita recargar el código o resetear el módulo.

Encontrando los pines

Este ejemplo utiliza el pin A2. Este pin se encuentra en la fila de conectores cercana al conector USB.

Los pines analógicos se pueden usar para esta funcionalidad, pero dado que varios se encuentran afectados a otras funciones, se recomienda utilizar únicamente los pines A2, A3, A4 y A5.

Otros pines que podemos usar

Para el ejemplo anterior se deben utilizar los pines analógicos (color verde en PINOUT).

Tip: Debemos tener en cuenta que los pines cuentan en su mayoría con múltiples funciones y alguna de ellas las podemos estar utilizando ya en nuestro programa. Los resultados podrían verse afectados por ello.

LED RGB Interno

El primer ejemplo muestra cómo cambiar el color y brillo del LED.

Primero importamos la librería neopixel al directorio /lib del módulo. Creamos el objeto de LED y lo asociamos al pin NEOPIXEL o D8 y ponemos que hay 1 solo LED en el módulo. Nos quedaría algo asi: led = neopixel.NeoPixel(board.NEOPIXEL, 1).

Para configurar el brillo, se utiliza el atributo brightness. Los valores para este atributo van desde 0 a 1, pasando por todos los intermedios. Entonces si yo pongo led.brightness = (0.3), el brillo del LED es al 30%.

Los colores del LED se configuran mediante el uso de RGB (rojo, verde y azul). Cada miembro del RGB es seteado entre los valores 0 y 255. Combinando los distintos colores del LED, se crean muchos más colores. Por ejemplo, si quiero que el LED tenga color rojo utilizo (255, 0, 0), si quiero verde utilizo (0, 255, 0) y por último si quiero azul utilizo (0, 0, 255).

Después, se añade time.sleep(0.5) para poder visualizar cada color. Si no coloco time.sleep(0.5) , que es una pausa de medio segundo, se hace casi imposible poder ver los colores.

Arco iris

Para crear el arcoíris se necesita un poco de matemáticas y de la función wheel.

Se añade wheel después de la configuración pero antes del loop. Antes de entrar al loop, seteamos la variable i a 0. El loop contiene una parte matemática que hace que se sume ese valor i hasta 256 y se repita para realizar el arcoíris. A esto se le añade wheel. El time.sleep() define cuando cambia el color del LED.

Encontrando los pines

Este ejemplo utiliza el pin NEOPIXEL o D8.

Neopixel

Se pueden controlar hasta 300 LEDs Neopixel con el atributo brightness y 1000 LEDs Neopixel sin utilizar ese atributo. Esto es así, ya que para ajustar el brillo se debe recrear dinámicamente los datos que vayan al LED .

Lo primero que se hace es crear el objeto del LED. El Neopixel tiene 2 argumentos obligatorios y 2 argumentos opcionales. Como argumento obligatoria está el número de LEDs y el pin por donde sale la información, y como opcional esta brightness y auto_write.

El Neopixel puede ser manejado por cualquier pin del módulo. En el caso del ejemplo se eligió el pin D10. Para definir el pin, hay que asignar la variable pixel_pin al pin deseado, en este caso board.D10.

Asignar número total de pixels utilizados en num_pixels. Configurar brillo en brightness. La función del auto_write = True es la de enviar automáticamente cada cambio de color al LED. En caso de ser auto_write = False, se debe agregar pixels.show() cada vez que se quiera hacer un cambio de color. Esto último hace al código más complejo, pero las animaciones se realizan más rápido.

Existen funciones que facilitan el trabajo con Neopixel. Primero esta wheel(), que funciona como una rueda de colores en donde se combinan rojo, verde y azul para formar distintos colores (ver LED interno RGB). Después tenemos color_chase() que necesita que se le indique un color y el tiempo que se quiere tener ese color (color_chase(RED, 01)). Por último, tenemos rainbow_colors().

El loop es sumamente sencillo. El código consta de que cambie el color del LED de rojo a verde, verde a azul y azul a rojo. A lo anterior se le suma una pausa de 0.1 segundos para poder ver bien los colores en el LED. Luego tenemos  color_chase(), que cumple la misma función que lo anterior, pero se le agrega la función del timer en la función. Por último tenemos rainbow_cycle(0) que hace que la animación de arcoíris sea lo más rápido posible.

Encontrando los pines

Este ejemplo utiliza el pin digital D32 como salida (Digital Out).

Otros pines que podemos utilizar

Para el ejemplo anterior se pueden utilizar todos los pines digitales (color violeta en PINOUT).

Tip: Debemos tener en cuenta que los pines cuentan en su mayoría con múltiples funciones y alguna de ellas las podemos estar utilizando ya en nuestro programa. Los resultados podrían verse afectados por ello.

UART

Además de poder realizar comunicaciones a través del USB e imprimir los resultados en el monitor serie, también se pueden establecer comunicaciones con dispositivos UART (Universal Asynchronous Receiver-Transmitter) utilizando los pines de comunicación serial RX y TX.

Para este ejemplo se utilizo un módulo ESP8266, el cuál tenia un programa para mandar información por medio de los pines RX y TX.

Se necesita tener cargada la librería adafruit_bus_device en el directorio /lib del módulo.

Primero creamos el objeto UART, en donde vamos a configurar los aspectos de la comunicación.

uart = busio.UART(board.TX, board.RX, baudrate=9600)

Vamos a utilizar los pines de comunicación serial del módulo en este ejemplo. El orden en que se definen los pines es primero el TX (transmisor) y le sigue el RX (receptor). Por último indicamos el valor de de los baudios a utilizar en la comunicación. Este valor debe coincidir con el del módulo a comunicar, caso contrario no podrá realizarse la comunicación.

Lo siguiente que se hace es leer la información que llega por los pines de comunicación serial. Para ello se utiliza:

data = uart.read(32)

Esto nos permite leer de hasta 32 bytes. Se revisa que se haya recibido información. En caso de recibir información, se la convierte de bytes a string para poder facilitar la lectura. Por último, se la imprime en el monitor serie para poder verificar que la información recibida no es errónea. Se agrega también que el LED en el pin D13 parpadee cada vez que se reciba información.

Encontrando los pines

Este ejemplo utliza los pines 0 (PIN_SERIAL1_RX), 1 (PIN_SERIAL1_TX), 3.3V y GND. Para garantizar que la comunicación se realice sin problema, se debe conectar el pin GND del módulo con el pin GND del módulo a comunicar. Caso contrario, no se podrá realizar la comunicación.

Otros pines que podemos utilizar

Para el ejemplo anterior se pueden utilizar únicamente los pines de comunicación Serial (color cyan en PINOUT) que vienen preconfigurados en el firmware.

En el módulo Nonthue M0 es posible reconfigurar el firmware para crear más puertos Serie o UART.

I²C

Encontrar tu sensor

Antes que nada, se recomienda que el sensor este bien cableado para no tener fallas luego. Este ejemplo detecta un sensor por I²C e imprime su dirección en la consola.

Primero creamos al objeto i2c y le definimos los pines a utilizar para la comunicación. Para poder escanear y detectar al sensor, se debe bloquear I²C y solamente tiene que estar conectado el sensor a identificar. Se incluye un loop en donde no se continua el programa hasta que I²C sea bloqueado. Como último, tenemos el loop que hace el escaneo i2c, i2c_scan().

I²C siempre se muestra en forma hexadecimal, por eso se agregó una parte de código para poder mostrar la dirección en hexadecimal. Al abrir la consola se ve el resultado. El programa muestra vectores de direcciones. En caso de no aparecer ninguna dirección, revisar cableado del sensor.

Encontrando los pines

Este ejemplo utiliza los pines SCL, SDA, 3.3V y GND. Los pines SCL y SDA son utilizados en este caso para realizar una comunicación I²C con el módulo.

Otros pines que podemos utilizar

Para el ejemplo anterior se pueden utilizar únicamente los pines de comunicación I²C (color celeste en PINOUT) que vienen preconfigurados en el firmware.

En el módulo Nonthue M0 es posible reconfigurar el firmware para crear más puertos I²C.

HID Keyboard y Mouse

HID significa Dispositivo de Interfaz Humano (Human Interface Device). Esto significa que puedo controlar mouse y teclado desde este módulo. Se utiliza la librería hid, que tiene que estar cargada en el directorio /lib del módulo.

Teclado

Encontrando los pines

Para el ejemplo del teclado se van a utilizar los pines D10, D7 y GND. Estos pines se encuentran en la fila de conectores cercana al conector de la batería. El botón que se conecte al pin D10 va a escribir la letra H y el botón que se conecte al pin D7 va a escribir «Hello World!».

La emulación de teclado en el módulo funciona cuando se desconecta un cable. Al desconectar el cable, se escribe la letra definida en el código.

Primero asignamos unas variables para usar más adelante. Creamos 3 vectores asignados a las variables keypress_pins, key_pin_array y keys_pressed. El primero son los pines. El segundo esta vacío todavía, después se va a utilizar. El tercero es la o las letras o el o los números de nuestro teclado (en este caso la “A” y la frase “Hello World!”). Creamos la última variable asociada a control_key que nos permite más adelante utilizar la tecla shift.

Los siguientes objetos creados son keyboard y keyboard_layout. Por el momento se utiliza el teclado con distribución de Estados Unidos. El time_sleep(1) está para evitar un error que se genera si el programa corre antes de que se conecte el módulo con la computadora.

Luego definimos en que pins van a estar las teclas y le seteamos pull-up a ambos. Aplicamos los pines a key_pin_array para poder usarlos más adelante.

Configuramos al LED del módulo para que sirva como status.

Lo último que queda es imprimir en consola “Esperando letra…” para saber cuándo ya se puede utilizar el código.

Loop

Dentro del loop se revisa que cada pin no haya cambiado su estado. Una vez que cambio se imprime en consola “Pin # está conectado a masa.” Para saber que hubo cambio de estado del botón. Se enciende el LED del módulo. Se espera a que se vuelva a cambiar el estado del botón.

Si el código recibe un string, lo imprime usando keyboard_layout para determinar las teclas. Por otro lado, el código utiliza control_key y presiona la letra “a”, lo que resulta en “A”. Por último, utiliza keyboard.release_all() para limpiar las teclas. Siempre se recomienda limpiar las teclas después de pulsar una tecla para evitar que quede pegada la tecla.

Mouse

Para este ejemplo se utiliza un joystick de 2 ejes con botón. Lo usamos para emular el movimiento del mouse y el click izquierdo.

Encontrando los pines

Los pines que se van a utilizar son A0 (eje X), A2 (eje Y), A3 (botón), 3.3V y GND. Estos pines se encuentran en la fila de conectores cercana al puerto USB. Los ejes X e Y tienen que ser conectados a pines analógicos.

Para conectarlo:

  • VCC a 3.3V
  • VRX a A0
  • VRY a A3
  • SWITCH a A2
  • GND a GND

Primero creamos el objeto mouse como mouse = Mouse(). Después definimos x_axis e y_axis a los pines correspondientes. Por ultimo definimos el pin para el botón.

Los ejes X e Y actúan como 2 potenciómetros. Los vamos a usar como en el ejemplo de Analog In. Seteamos pot_min y pot_max como los mínimos y máximos de voltaje que leen los potenciómetros. Usamos la variable step = (pot_max – pot_min) / 20.0 para una función más adelante.

Funciones

La primera función se utiliza con get_voltage() como se hizo en Analog In. Esto se hace para tener mediciones correctas de los potenciómetros.

La segunda función es la de steps(axis). Para usarla, se necesita de proveerle los ejes del joystick. Acá es donde se va a utilizar la variable step. El rango del potenciómetro va desde 0 a 3.29. Teniendo en cuenta que el joystick se para en el medio del rango (1.66) vamos a mapear los numero para que vayan desde 0 a 20 usando esta función. Esto hace más simple el código, ya que no necesitamos ver cuantos decimales se corrió de lugar el joystick.

Loop

En el loop asignamos a x e y para leer el voltaje de x_axis e y_axis.

Lo próximo es ver cuando el estado del botón es False. Cuando es presionado, se envía el comando del click izquierdo. El time.sleep(0.2) previene de múltiples lecturas del botón.

Después, se usa la función steps() para el movimiento del mouse. Hay dos pares de dos if para cada eje. Recordar que 10 es la mitad de nuestro rango desde 0 a 20. El primer par para cada eje dice que si se movió un paso fuera del centro (izquierda o derecha del eje x o arriba o abajo del eje y), se mueva 1 unidad el mouse. El segundo par para cada eje dice que si el joystick es movido al mínimo o máximo de cada eje, mover el mouse en la dirección apropiada 8 unidades. Con esto se logran distintas velocidades de movimiento.

Para ver en que paso se encuentra cada eje, se puede descomentar  los print para que se pueda imprimir en la consola los valores de los ejes.

Storage

CircuitPython ofrece la posibilidad de escribir en la memoria flash. Sin embargo, es un poco difícil. Se debe agregar un archivo llamado boot.py. Este archivo se ejecuta solamente cuando se desconecta y vuelve a conectar el módulo. Lo único que realiza boot.py es verificar si el pin D7 del módulo está conectado a masa para empezar a escribir en la memoria flash.

Los archivos de la memoria flash no podrán ser editados mientras el programa este en funcionamiento.

Boot.py trabaja en conjunto de un código principal, el cual se encarga de crear el archivo de texto en donde se van a guardar los datos y recolecta los datos que se quieren guardar. En el caso de este ejemplo, se guardan las mediciones de temperatura del micro controlador.

Boot.py:

Code.py:

El archivo boot.py  va colocado en la carpeta donde se encuentra code.py. Se debe desconectar y volver a conectar el módulo para que empiece a funcionar. Con cargar un archivo nuevo o apretar el boton de RESET (soft-reboot) no alcanza.

Para que el módulo comience con la escritura, se debe conectar a masa el pin D7 del módulo. Esto permite que el módulo cambie su estado de escritura/lectura y así poder empezar a escribir los datos que se vayan midiendo. Al hacer esto, no se puede editar code.py, boot.py y el archivo de texto en donde se guardan las mediciones, ya que

Una vez que se abre el archivo de texto generado por el módulo o se desconecta el cable del pin D7, se detiene la escritura. Para poder editar code.py se debe desconectar el cable del pin D7 a masa y reiniciar el módulo.

En caso de no utilizar más esta función, se recomienda borrar boot.py para evitar problemas con otro código.

Encontrando los pines

Este ejemplo utiliza los pines D5 y GND. Se encuentran en la fila de conectores cercana a el conector de la batería.

CPU Temp

En cada microcontrolador ATSAMD21 hay integrado un sensor de temperatura de CPU. Para poder leer la temperatura solamente se necesita entrar a la consola, estar en el modo RETL y usar 2 comandos para que muestre la temperatura.

Para estar en modo RETL solamente se debe apretar CTRL + C y ya nos permite escribir la consola.

Para leer la temperatura, solamente se necesita escribir en consola esto:

import microcontroller

micrococontroller.cpu.temperature

Y asi nos muestra la temperatura actual del microcontrolador.

Si se quiere visualizar la temperatura en grados Fahrenheit, se debe escribir:

import microcontroller

microcontroller.cpu.temperature * (9/5) + 32

Cambiar de Arduino a CircuitPython y viceversa

Arduino a CircuitPython

Para pasar el módulo a CircuitPython, se debe descargar desde la página https://www.eltech.com.ar/productos/nonthue/nonthue-m0-main/aprender-nonthue-m0/#descargas el archivo que dice CircuitPython uf2. Una vez descargado, apretar 2 veces el botón de reset hasta que el NeoPixel se prenda rojo para luego pasar a verde. Aparece una carpeta llama NONTHUEBOOT. Pegar el archivo previamente descargado ahí y la placa se reinicia sola. Al terminar de reiniciar, aparece una carpeta llamada CIRCUITPY y ya se puede comenzar a programar en CircuitPython.

CircuitPython a Arduino

Para usar el módulo con algún programa de Arduino solamente hace falta abrir Arduino IDE y cargar el programa en el puerto que se encuentre el módulo. En caso de tener problemas al carga, apretar 2 veces el botón de reset (NeoPixel se pone rojo y después pasa a ponerse verde) y cargar nuevamente el programa.

Librerías necesarias

Para hacer funcionar estos ejemplos, se necesita un conjunto de librerías de Adafruit. Entrar a: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases y descargar la última versión disponible.

Para instalarla cualquier librería en el módulo, seleccionar el archivo o carpeta deseado y pegarlo en la carpeta lib dentro del módulo. En caso de no existir lib, crearla.