viernes, 2 de septiembre de 2016

Librería RFM69 para micros PSoC 4 y PSoC 5LP.

De entre todas las posibilidades que hay actualmente en el mercado para comunicar dispositivos mediante radiofrecuencia de forma barata y sencilla, los módulos RFM69 de HopeRF son una de ellas. Estos son los módulos que he utilizado en uno de mis últimos proyectos y para los cuales he desarrollado una librería que facilite su uso con los microcontroladores PSoC de Cypress. Ahora que la mayor parte de las funcionalidades que necesitaba ya están implementadas he decidido liberar la librería por si a alguien le puede ser de utilidad. Son varios los tipos de módulos RFM69 disponibles, y la librería puede ser utilizada con todos ellos. Tanto con los módulo normales como con los módulos de alta potencia. En la figura siguiente puedes ver los distintos módulos disponibles hasta este momento y la distribución de sus patillajes.



Si estas interesado en la librería, puedes descargarla directamente del repositorio de github, junto con varios proyectos de ejemplo/pruebas tanto para micros PSoC 4 como para micros PSoC 5LP
Este es el enlace al repositorio: https://github.com/hackingchips/PSoC_RFM69

En las siguientes secciones te doy algunas indicaciones básicas para que puedas entender su funcionamiento y empezar a utilizar o hacer pruebas con la librería y los módulos RFM69.
Ten en cuenta que solo son indicaciones para el uso de la librería y no pretendo mostrar un tutorial completo sobre el módulo o sobre comunicaciones de radio frecuencia. Si tienes alguna duda, siempre puedes enviarme un mensaje y te ayudaré en lo que pueda.

 

Primeros pasos.

El primer paso será añadir todos los elementos necesarios para poder comunicar el microcontrolador PSoC con el módulo RFM69 y poder utilizar la librería en tu proyecto.
  • Añade un módulo de comunicaciones SPI al esquema de tu proyecto y configúralo como maestro en modo CPHA = 0, CPOL = 0.
  • La señal SS del bus SPI debe ser controlada por la librería y no por las API generadas en PSoC Creator. Para ello, añade un pin de salida que será utilizado para esta señal.
  • Si vas a utilizar la señal de RESET del módulo RFM69, añade otro pin de salida para esta señal.
  • Si vas a hacer uso de las interrupciones del módulo RFM69, añade un pin de entrada digital.
  • Añade los archivos de la librería a tu proyecto y ajusta en tu código los #includes necesarios (mira los ejemplos).
El paso siguiente será configurar varios parámetros en el archivo de la librería “PsoC_RFM69_Config.h”. Algunos de estos parámetros serán valores fijos que no variarán una vez compilado tu proyecto. Otros son parámetros con valores por defecto que podrán ser modificados en tiempo de ejecución mediante las funciones correspondientes de las librerías.
  • SPI_NAME
    En esta macro, pon el nombre que le hayas dado al módulo SPI en el esquema de tu proyecto. Gracias a esta macro, si decides cambiar el nombre del módulo SPI en el esquema, no tendrás que retocar los nombres de todas las funciones relativas al bus SPI dentro del código de la librería, solo tendrás que cambiar el valor asignado a esta macro.
  •  mmSPI_SS_Write(value)
    Función de la API, generada por PSoC creator tras la primera compilación, correspondiente a la línea que hayas utilizado para la señal SS del bus SPI.
    Ejemplo: Supón que añades en el esquema una salida digital a la que llamas SS. La API generada por PSoC Creator para el componente del bus SPI, proveerá una función llamada SS_Write(valor) que permitirá, desde el firmware, fijar el valor lógico de esa salida. Esta es la función que has de asignar a esta macro. El motivo es el mismo que para la macro anterior.
  • CS_DELAY_US
    Retardo en microsegundos desde que se activa la señal SS del bus SPI hasta que empieza el envío de datos.


  • mmRESET_PIN(value)
    Si no vas a utilizar un pin para manejar la señal de RESET del módulo RFM69 deja esta línea comentada.
    En caso contrario ajusta esta macro, al igual que hiciste con la línea SS; poniéndole la función de escritura que provee PSoC Creator tras la compilación.
    Ejemplo: Supón que añades un pin para la señal de RESET al que le pones el nombre RFM_RESET. Deja la línea sin comentar y asignar el valor RFM_RESET_Write(value) a la macro. “RFM_RESET_Write” es la función generada por PSoC Creator para poder fijar el valor del pin añadido en el esquema.
  • RFM69_HVARIANT
    Si vas a utilizar un módulo de potencia normal, deja esta línea comentada. Si por el contrario vas a utilizar uno de los módulos de alta potencia, deja la línea sin comentar.
    Es totalmente necesario ajustar correctamente esta macro en función del módulo utilizado o tu proyecto no va a funcionar.
  • PACKET_LENGTH_MODE
    Esta macro déjala tal como está, sin modificar. Está prevista para uso futuro, por si en algún momento decido implementar el uso del modo de longitud variable para el envío de paquetes de datos.
  • PAYLOAD_LENGTH
    Aquí has de fijar el tamaño de los paquetes de datos que vas a utilizar. La longitud máxima dependerá de cómo estés utilizando el módulo, de si utilizas encriptación de datos o no. Te recomiendo que utilices como valor máximo 64 hasta que entiendas la diferencia.
    Este es el valor por defecto en el momento de arrancar el sistema y podrá ser modificado en tiempo de ejecución mediante el uso de las funciones correspondientes.
    Lee el apartado “Observaciones” para entender mejor este valor.
  • SYNC_SIZE, SYNC_BITS_TOLERANCE, SYNC_VALUE_1 a SYNC_VALUE_8
    Estas macros tienen que ver con los bytes de sincronización que se envían al inicio de cada transmisión de datos. De momento, te recomiendo no tocarlos y utilizarlos tal cual están si no tienes claro de qué se trata este tema de la sincronización, en cuyo caso te recomiendo que busques algún tutorial sobre comunicación de datos en RF.
  • FREQUENCY_MSB, FREQUENCY_MID, FREQUENCY_LSB
    Fijan la frecuencia inicial tras el arranque. Los valores por defecto indicados en la librería corresponden a la banda ISM 433Mhz que es la utilizada en Europa y que es donde yo vivo.
    En la librería y el datasheet puedes ver cómo calcular estos valores en función de la frecuencia. También se proveen en la librería funciones para fijar esta frecuencia de manera fácil.
  • FREQDEV_MSB, FREQDEV_LSB
    Valores por defecto referentes a la desviación de frecuencia utilizada para las comunicaciones.
  • BITRATE_MSB, BITRATE_LSB
    Velocidad en baudios de la transmisión de datos.
    En el archivo de cabecera puedes ver macros con los valores calculados para las velocidades  estándar más utilizadas.
  • POWER_LEVEL
    Valor inicial de la potencia de salida tras arrancar el sistema. El valor máximo es 31 y ten en cuenta que la potencia de salida del módulo dependerá de si utilizas un módulo normal o uno de los de alta potencia.
  • ADDRESS_FILTERING
    Del valor de esta macro dependerá de si utilizamos filtrado de dirección o no.
    • = 0x00. No se va a utilizar filtrado de dirección. Puede ser útil si solo vas a utilizar un maestro y un esclavo.
    • = 0x01. Utilizar filtrado de dirección. En este caso, cada esclavo deberá tener asignada una dirección. Cada vez que el maestro envíe datos deberá direccionar a qué esclavo van dirigidos esos datos.
    • = 0x10. Utilizar filtrado de dirección y de emisión (broadcast). Si también se va a utilizar filtrado ‘broadcast’, los esclavos, además de su dirección deberán tener asignada una dirección de broadcast. Para que lo entiendas, es algo así como crear subredes de esclavos en que cada subred tiene su dirección (broadcast) y dentro de ella cada esclavo también tiene asignada su propia dirección.
Otra forma de filtrado, sería la modificación de los valores de los bytes de sincronización. Los bytes de sincronización son los primeros que se envían en cada transmisión y es requisito indispensable que estos bytes sean los mismos tanto en el maestro como en el esclavo. Así que indirectamente también podrías utilizarlos como una forma de direccionamiento.
  • NODE_ADDRESS
    La dirección asignada si vas a utilizar filtrado de dirección.
  • BROADCAST_ADDRESS
    La dirección asignada si vas a utilizar filtrado ‘broadcast’.
  • AES_ENCRYPTION
    = 1, si vas a utilizar encriptación en las comunicaciones.
    = 0, si quieres deshabilitar la encriptación.
  • AES_KEY_1 a AES_KEY_16
    Los valores para la clave de encriptación.

 

Funciones de la librería (públicas).

  • uint8 RFM69_CheckPresence()
    Comprueba si hay algún módulo conectado en el bus SPI. Esta función es llamada desde RFM69_Start() durante la inicialización, así que es probable que no tengas necesidad de utilizarla directamente.
    Devuelve:
    • 0, si no ha encontrado un módulo conectado en el bus SPI.
    • 1, si ha detectado un módulo conectado en el bus SPI.
  • uint8 RFM69_Start()
    Inicializa y configura el módulo. Es la primera función que has de llamar en tu programa antes de empezar el uso normal del módulo.
    Devuelve:
    • 0, si no ha encontrado un módulo conectado en el bus SPI.
    • 1, si ha detectado un módulo conectado en el bus SPI.
  • void RFM69_SetMode(uint8 mode)
    Fija el modo o estado del módulo RFM69.
    Posibles valores de entrada:
    • OP_MODE_SLEEP
      Bajo consumo, no hay nada activado en el módulo.
    • OP_MODE_STANDBY
      En reposo. Mantiene activado el regulador interno y el oscilador de cristal.
    • OP_MODE_FS
      Mantiene activado además el sintetizador de frecuencia.
    • OP_MODE_TX
      Pone el módulo en modo transmisión de datos.
    • OP_MODE_RX
      Pone el módulo en modo de recepción y a la espera de datos.
  • void RFM69_SetPayloadLength(uint8 plength)
    Permite ajustar el tamaño del paquete de datos de las comunicaciones.
    El valor de entrada será la nueva longitud del paquete de datos.
  • uint8 RFM69_SetSync(uint8 syncsize, uint8 syncbitstolerance, uint8 *syncvalue)
    Modifica los parámetros relativos a los bytes de sincronización en tiempo de ejecución.
    Entradas:
    • syncsize. Nuevo tamaño del paquete de datos.
    • syncbitstolerance. Bits de tolerancia.
    • synvalue. Puntero a un array de bytes que contendrá los nuevos bytes de sincronización.
    Salidas:
    • = 0, fallo; si syncsize > 8 o syncbitstolerance > 7
    • = 1, la función se ejecutó sin problemas.
  • void RFM69_SetFrequency(uint32 frequency)
    Modifica la frecuencia utilizada para las comunicaciones. Como valor de entrada le indicaremos la nueva frecuencia para la portadora en Hz.
  • void RFM69_SetFrequencyDeviation(uint16 frequency)
    Fija la desviación de frecuencia. El valor de entrada será la nueva desviación en Hz.
  • void RFM69_SetBitrate(uint16 bitrate)
    Fija la velocidad de las comuniciones. El valor de entrada serán los bits por segundo. La función calcula los valores necesarios para los registros internos del módulo.
  •  void RFM69_SetBitrateCls(uint8 msb, uint8 lsb)
    Fija la velocidad de las comunicaciones. En este caso podremos utilizar los valores precalculados disponibles en el archivo de cabecera “PsoC_RFM69.h”
  • void RFM69_SetPower(uint8 power)
    Fija el valor de pontencia de salida. El valor máximo es 31.
    • Para módulos normales, la pontencia de salida : -18dBm + power
    • Para módulos H (alta potencia) : -11dBm + power
  • uint8 RFM69_SetAddressFiltering(uint8 filtering, uint8 nodeaddress, uint8 broadcastaddress)
    Activa/desactiva el filtrado de direcciones y fija las direcciones del módulo.
    Entradas:
    • filtering: valor según lo indicado anteriormente para la macro ADDRESS_FILTERING
    • nodeaddress: dirección asignada al módulo si es un esclavo o con el que va a comunicar si es un maestro.
    • broadcastaddress: dirección ‘broadcast’ si se va a utilizar este modo también.
  • Salida:
    • = 0, si se selecciona un modo ‘filtering’ incorrecto.
    • = 1, si todo fué correcto.
  • uint8 RFM69_Encryption(uint8 setunset, uint8 *aeskey)
    Activa/desactiva la encriptación y fija la clave utilizada para la misma.
    Entradas:
    • setunset. = 1, activa encriptación. = 0, desactiva encriptación.
    • aeskey: puntero a un array de bytes que contiene la clave de encriptación.
    Salida:
    • = 1, si todo fue correcto.
    • = 0, si setunset contiene valor incorrecto ( > 2).
  • void RFM69_DataPacket_TX(uint8 *buf, int len)
    Transmisión de un paquete de datos.
    Entradas:
    • buf: puntero a un array de bytes conteniendo los datos a enviar.
    • len: longitud de los datos a enviar.
  • int RFM69_DataPacket_RX(uint8 *buffer, uint8 *rssi)
    Recibe un paquete de datos.
    Entradas:
    • buffer. Puntero a un array de bytes donde se almacenarán los datos recibidos.
    • Rssi. Puntero a una variable en la cual obtendremos el valor RSSI de la última transmisión.
      Salidas:
    •         longitud de los datos recibidos.
    •         Si la longitud es 0 no hay datos disponibles.
  • uint8 RFM69_GetIRQFlags()
    Lee los flags relativos a las interrupciones generadas en el módulo. La librería no implementa su gestión directamente así que el tratamiento de estos flags y de las interrupciones será responsabilidad tuya.
    Deberás leer el datasheet para entender el significado de estos datos.
  • uint8 RFM69_GetTemperature()
    Lee la temperatura del módulo. El valor de salida será el valor leído directamente de los registros internos del módulo sin realizar la conversión a temperatura real.
  • void RFM69_HardwareReset()
    Activa la señal del RESET del módulo durante 1ms.
    La disponibilidad de esta función dependerá del valor asignada a la macro ‘mmRESET_PIN’ en el archivo de configuración.

 

Observaciones.

  • Los paquetes de datos manejados por los módulos RFM69 pueden ser de dos tipos. De longitud fija o de longitud variable.
    En esta librería solo se implementa la posibilidad de utilizar paquetes de longitud fija .
    Los paquetes de datos que podemos enviar tienen un máximo de longitud que dependerá de si utilizamos encriptación o no.
    Según el datasheet del módulo, en caso de utilizar encriptación, la longitud máxima de un paquete de datos será de 64 bytes. Y si no se utiliza encriptación, podremos enviar paquetes de datos de hasta 255 bytes. Esto es algo que no he comprobado ya que nunca he necesitado enviar paquetes de datos de más de 64 bytes.
    Como ya has visto, cuando vamos a enviar un paquete de datos le enviamos a la función un array con el contenido de los datos a enviar además de la longitud de los datos RFM69_DataPacket_TX(uint8 *buf, int len).
    Pero hay que tener en cuenta una cosa, y es si vamos a utilizar o no el filtrado de dirección.
    Si utilizamos el filtrado para dirigir los datos a un esclavo con una dirección específica, el primer byte de este array debe contener la dirección del esclavo al que van dirigidos los datos.
    Si te fijas en el código de los ejemplos, verás que siempre que se hace un envío de un paquete, el byte de la posición 0 contiene la dirección del esclavo al que va dirigido.Que utilicemos únicamente el modo de longitud fija no quiere decir que no podamos variar la longitud del paquete. Quizá tengamos una red de esclavos en que cada uno tiene una función y que recibe una longitud de datos distinta a otros esclavos.
    Lo que quiere decir, es que es obligatorio, que la longitud de datos que envía el maestro, sea igual a la longitud de datos que espera el esclavo al que van dirigidos.
    Por ejemplo:
    Tienes una red con un maestro, y dos esclavos. El primer esclavo está configurado para recibir paquetes con una longitud de 10 bytes. El segundo esclavo está configurado para recibir paquetes con una longitud de 30 bytes.Cuando el maestro envía datos al primer esclavo, ha de ajustar la longitud (el payload) a 10 bytes, y enviar efectivamente 10 bytes a ese esclavo. Cuando va a enviar datos al segundo esclavo, deberá reajustar la longitud del paquete de datos para enviar a ese esclavo los 30 bytes que espera. Esto se puede hacer antes de cada transmisión de datos mediante la función RFM69_SetPayloadLength(uint8 plength).
  • Otra cosa que has de tener en cuenta es el caso en el que utilices filtrado de dirección al mismo tiempo que encriptación. Y esto te lo comento por si tienes el mismo despiste que tuve yo durante el desarrollo y que me dio algún dolor de cabeza hasta que me di cuenta.
    En una ocasión, haciendo pruebas; me pasó que utilicé varios esclavos, unos con encriptación y otros no. Unos con filtrado de dirección y otros no. Unas pruebas locas de esas que se hacen durante el desarrollo para encontrar cosas raras.
    El caso, es que uno de los nodos no me respondía cuando le enviaba los datos encriptados y sin haber activado el filtrado de dirección en el maestro. Así que el maestro, encriptaba absolutamente todos los datos, incluido el primer byte correspondiente a la dirección de esclavo. Evidentemente, el esclavo no respondía. Al activar el filtrado de dirección, evitamos en caso de utilizar también encriptación, que el primer byte correspondiente a la dirección del esclavo sea encriptado.
  • Si utilizas filtrado de dirección recuerda que el primer byte que has de poner en el paquete de datos a enviar es la dirección del esclavo. Pero ten en cuenta también, que cuando el esclavo recibe esos datos, el primer byte que recibe es su propia dirección, así que probablemente; en tu código tendrás que descartar ese primer byte y tener en cuenta los siguientes.

 

Proyectos de ejemplo.

Junto con la librería te proporciono varios proyectos que sirven de ejemplo y para aprender cómo utilizar la librería. En concreto son tres proyectos:
  • Uno destinado a ser utilizado con microcontroladores PSoC 4 y preparado para el kit de desarrollo CY8CKIT-042.
  • Otro para microcontroladores PSoC 4M y para el kit de desarrollo CY8CKIT-044
  • Y el último para microcontroladores PSoC 5LP usando el kit CY8CKIT-059.





La idea de los ejemplos es montar una pequeña red donde un maestro se comunica e interroga a dos esclavos. Si no tienes suficientes kits de desarrollo, puedes también hacerlo teniendo un maestro y un único esclavo.
Selecciona el kit que vas a utilizar como maestro y abre el proyecto correspondiente. Configura la función de maestro en el archivo “main.h”. Para ello, deja sin comentar la lína “#define COMPILE_FOR_MASTER”  y comenta las otras dos (//#define COMPILE_FOR_SLAVE_1, //#define COMPILE_FOR_SLAVE_2). Compila el proyecto y cárgalo en la placa.
Selecciona el kit que vas a utilizar como esclavo uno (este tendrá asignada la dirección 100, puedes verlo en el código). En su archivo “main.h” deja sin comentar la línea #define COMPILE_FOR_SLAVE_1 y comenta las otras dos. Y ya sabes, compilar y cargar en la placa.
Selecciona el kit que vas a utilizar como esclavo dos (tendrá la dirección 200). De la misma manera, edita en el “archivo.h” la línea #define COMPILE_FOR_SLAVE_2 dejándola sin comentar y comentando las otras dos. Otra vez, compilar y cargar.
Para controlar el maestro, tendremos que tener el circuito conectado a nuestro ordenador para poder controlarlo mediante un programa de terminal tipo TeraTerm o similar el cual tendremos configurado a 19200 baudios.
En cuanto alimentemos el circuito, podremos ver en el programa terminal un menú con opciones para probar tanto el esclavo 1 como el esclavo 2. Podremos activar o desactivar los leds que hay en las placas de desarrollo o podremos interrogar a cada uno de los esclavos y recibir el valor de su registro de temperatura así como el valor RSSI de la última comunicación recibida por ese esclavo.
Una observación. Si conectas tanto maestro como esclavos al mismo ordenador y los tienes cerca unos de otros y además tienes configurado un nivel de potencia de salida muy elevado, es posible que tengas fallos intermitentes de comunicaciones por saturación de la entrada de los módulos RFM69. En este caso, sepáralos o ajusta un nivel de potencia de salida más pequeño.

 

Para finalizar.

Si has llegado hasta aquí es seguro que estás interesado en esta librería, así que espero que te sea de utilidad y si tienes algún problema para utilizarla o entender algo; envíame un mensaje y te ayudaré en lo que pueda.


Compartir:  Facebook Twitter

4 comentarios:

  1. Hello, I can't find the link to the example with the CY8CKIT-042 kit. Is it possible to have it? Thank you.

    ResponderEliminar
    Respuestas
    1. You can find it in Github:
      https://github.com/hackingchips/PSoC_RFM69

      this is for PSoC 4:
      https://github.com/hackingchips/PSoC_RFM69/tree/master/Test/PSoC4_RFM69

      Eliminar
  2. Hola! muy buena aportación. Pregunta: los niveles lógicos que maneja el modulo RFM69 es de 3.3v? y es por eso que en el PSoC 5lp se quita el diodo?

    ResponderEliminar
    Respuestas
    1. Exacto. Levantando el diodo eliminamos la alimentación de 5V al módulo proveniente del USB y de esa manera podemos alimentarlo externamente con 3,3V

      Eliminar