Difference between revisions of "Sensorino"

From Wiki Makespace Madrid
Jump to: navigation, search
(Avances: Pruebas de distancia)
(Avances: añadida sección de sleep)
Line 410: Line 410:
 
* a 5m a traves de una pared fallaba la mayoria de veces, pero algunas programaba bien.
 
* a 5m a traves de una pared fallaba la mayoria de veces, pero algunas programaba bien.
 
* a 9m a traves de dos paredes fallaba en promedio en el momento de haber subido 30-40% del programa, que seria despues de haber transmitido ~150-200 paquetes sin fallo, o sea que la tasa de error todavia debe ser baja (~1%).
 
* a 9m a traves de dos paredes fallaba en promedio en el momento de haber subido 30-40% del programa, que seria despues de haber transmitido ~150-200 paquetes sin fallo, o sea que la tasa de error todavia debe ser baja (~1%).
 +
 +
'''Pruebas 13/2/2014:'''
 +
 +
Estoy intentando poner el PIC a dormir y despertarlo con el watchdog o los interrupts externos.
 +
 +
'''Sleep''': para el maximo ahorro se utiliza el SLEEP_MODE_PWR_DOWN
 +
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 +
  sleep_enable();
 +
En principio podríamos llegar a tener un consumo de solo [https://www.sparkfun.com/tutorials/309 1uA] !
 +
 +
'''Wakeup con watchdog''': hay que programar una ISR
 +
  ISR( WDT_vect ) {
 +
  //codigo aquí
 +
  }
 +
y habilitar el watchdog y su interrupción y que no resetee el PIC:
 +
 +
  MCUSR &= ~(1 << WDRF);
 +
  WDTCSR |= (1 << WDCE) | (1 << WDE);
 +
  WDTCSR = (1<< WDP0) | (1 << WDP3);
 +
  WDTCSR |= _BV(WDIE);
 +
 +
en la ISR conviene deshabilitar el watchdog para que no toque los huevos hasta que nos durmamos otra vez
 +
 +
'''Wakeup con interrupt en pin''': una buena guía es [http://www.gammon.com.au/forum/?id=11488 esta].
 +
el problema es que si utilizamos el "External Interrupt Request" en modo sleep PWR_DOWN solo funciona cuando el pin va a LOW, además solo podemos utilizarlo para los pines 2 y 3. Entonces he utilizado los itnerrupts de pin change (PCINT0_vect, PCINT1_vect y PCINT2_vect), pero para programarlos hay que meter un poco de codigo porqué el API de Arduino no los incluye.
 +
Me he basado en [https://sites.google.com/site/qeewiki/books/avr-guide/external-interrupts-on-the-atmega328 esta documentación].
 +
 +
  //Programamos las ISR (no hacen nada y valen para todo tipo de pin)
 +
  ISR(PCINT0_vect){
 +
  }
 +
  ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
 +
  ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
 +
 +
Luego es necesario identificar, en pase al pin en concreto, cual es el PCINT a utilizar en la mascara, para ello me he hecho una función:
 +
  byte pinToInt(byte pin){
 +
    if(pin <=7)
 +
      return PCINT16 + pin;
 +
    else if(pin >=8 && pin <=13)
 +
      return PCINT0 + (pin-8);
 +
    else return PCINT8 + (pin -14);
 +
  }
 +
Y el PCIE para habilitar el interrupt (se habilitan grupos de pines juntos, en 3 grupos):
 +
  byte pinToIE(byte pin){
 +
    if(pin <=7)
 +
      return PCIE2;
 +
    else if(pin >=8 && pin <=13)
 +
      return PCIE0;
 +
    else return PCIE1;
 +
  }
 +
y habilitamos:
 +
 +
  PCICR |= (1 << pinToIE(pinInt));
 +
    if(pinInt <=7)
 +
      PCMSK2 |= (1 << pinToInt(pinInt));
 +
    else if(pinInt >=8 && pinInt <=13)
 +
      PCMSK0 |= (1 << pinToInt(pinInt));
 +
    else PCMSK1 |= (1 << pinToInt(pinInt));
 +
Para una panoramica de como se nombran los pines y la correspondencia entre numero de pin, mascara, y registro para habilitarlos mirar este dibujo:
 +
 +
                      +-\/-+
 +
    PCINT14      PC6  1|    |28  PC5 (AI 5)PCINT13
 +
    PCINT16(D 0) PD0  2|    |27  PC4 (AI 4)PCINT12
 +
    PCINT17(D 1) PD1  3|    |26  PC3 (AI 3)PCINT11
 +
    PCINT18(D 2) PD2  4|    |25  PC2 (AI 2)PCINT10
 +
    PCINT19(D 3) PD3  5|    |24  PC1 (AI 1)PCINT9
 +
    PCINT20(D 4) PD4  6|    |23  PC0 (AI 0)PCINT8
 +
                VCC  7|    |22  GND
 +
                GND  8|    |21  AREF
 +
    PCINT6      PB6  9|    |20  AVCC
 +
    PCINT7      PB7 10|    |19  PB5 (D 13)PCINT5
 +
    PCINT21(D 5) PD5 11|    |18  PB4 (D 12)PCINT4
 +
    PCINT22(D 6) PD6 12|    |17  PB3 (D 11)PCINT3
 +
    PCINT23(D 7) PD7 13|    |16  PB2 (D 10)PCINT2
 +
    PCINT0 (D 8) PB0 14|    |15  PB1 (D 9) PCINT1
 +
                      +----+
 +
 
 +
  pin  PICINT PCMSK
 +
  0      16    2
 +
  1      17    2
 +
  2      18    2
 +
  3      19    2
 +
  4      20    2
 +
  5      21    2
 +
  6      22    2
 +
  7      23    2
 +
  8      0    0
 +
  9      1    0
 +
  10    2    0
 +
  11    3    0
 +
  12    4    0
 +
  13    5    0
 +
  PB5    6    0
 +
  PB7    7    0
 +
  A0(14) 8    1
 +
  A1(15) 9    1
 +
  A2(16) 10    1
 +
  A3(17) 11    1
 +
  A4(18) 12    1
 +
  A5(19) 13    1
 +
  PC6    14    1
 +
 
 +
 
 +
  pin  -> PCINT -> PCMSK
 +
  0-7  -> 16-23 ->  2
 +
  8-13  -> 0-5  ->  0
 +
  14-19 -> 8-13  ->  1

Revision as of 20:00, 13 February 2014

Nombre proyecto

ESTADO: PRIMERAS PRUEBAS

Miembros: User:Dario_Salvi, User:Bozo, User:Roberto Zabala


Objetivo

construir una red de sensores compatibles con el IDE de Arduino y que cuesten menos que 5 € con todo incluido.

Motivación

Hay muchas aplicaciones donde lo que queremos es simplemente un medio de enviar y recibir información sencilla sin tener pero que cablear toda el entorno. Un ejemplo es la casa, por ejemplo nos interesa saber que puertas o ventanas están abiertas, queremos apagar o encender tal luz o electrodoméstico etc.


Antecedentes

Hay muchos proyectos para redes de sensores y productos comerciales, pero ninguno hasta ahora verdaderamente barato (es decir <5€), ver Plataformas IOT.

El proyecto mas parecido al nuestro de momento es este, quitandole quizas los LEDs para ahorrar. Otro proyecto muy parecido es este.

Métodos y técnicas utilizadas

Requisitos:

  • cada nodo tiene una MCU y una conexión RF
  • el nodo base tiene que ser muy barato: < 5€
  • se tiene que poder programar con el IDE de Arduino
  • se tiene que poder alimentar con una pila de tipo botón o dos AA por un año

Estamos buscando alternativas, abajo hay una recopilación de posibilidades.

Detalles

Low power

https://www.sparkfun.com/tutorials/309 http://www.gammon.com.au/forum/?id=11497

Ideas para el Hardware

De momento nos centramos en un ATmega328 y en el chip nRF24L01 de Nordic. El coste de los dos puede llegar a ser inferior a 1 €. Placas micro compatibles Arduino (de donde poder copiar el diseño): femtoduino, Arduino mini pro

Asignación de pines según la librería de maniacbub (ver articulo original):

  • PD2 -> IRQ
  • PB0 -> CS
  • PB5 -> SCK
  • PB4 -> MISO
  • PB3 -> MOSI
  • PB1 -> CE
  • GND -> GND
  • VCC -> VCC


Alternativas son, para la MCU, el ATtiny24 o 84 aunque sus capacidades son limitadas y no hay plena compatibilidad con Arduino. El precio de un Attiny24 es de medio euro.

Pinouts: ATMega328P, nrf24l01

Otros:

  • Una Application Note de Atmel sobre deteccion de zero-crossing en la corriente AC - se usaria si quisiesemos graduar el brillo de luces, y es un esquema muy simple comparado con el tipico basado en los TRIAC.

Ideas para el SW

Queremos la compatibilidad con Arduino, por eso nos centramos en chips AVR. Hay unas cuantas librerías para controlar el chip radio:

Y unos posts sobre el tema:


A esto podemos añadir:

Del lado "servidor" o sea el colectore de todas las medidas y generador de aplicaciones domoticas se puede usar:


Funcionalidades que queremos:

  • recibe mensajes de la red de sensores a través de un nrf24 (probablemente conectado a través de un Arduino)
  • almacena historial de datos (BBDD)
  • permite controlar dispositivos actuadores (encender/apagar etc...)
  • lanza reglas del tipo if(...) then... mejor si definidas con un lenguaje alto nivel interpretado
  • almacena "escenarios de uso", por ejemplo: "escenario me voy de vacaciones": todo se apaga y se activa al alarma
  • que se conecte a servicios web externos tipo "enviar SMS" o "hacer un tweet"
  • la interfaz:
    • mejor si web, aunque se pueden pensar alternativas (movil, PC) etc.
    • que permita ver el estado de los dispositivos
    • que tenga gráficos para ver tendencias, explorar patrones etc.
    • que permita "actuar los actuadores" (pulso ON y se enciend la luz)
    • que permita crear escenarios de uso y activarlos
    • que permita definir reglas

Avances

Reunión 14/9/2013:

Hemos testeado 4 módulos nRF24L01 con dos Arduinos, uno ha fallado, pero los otros tres se ha conectado etnre si. Hemos los cogidos de ejemplo de la librería RF24 que parece ser la mejor hecha. Hemos probado la conexion estrella y ha funcionado.

Decisiones: para la semana que viene vamos a intentar conectar un Atmega328p directamente a un modulo nRF24L01 en una placa de prototipado. Queremos producir 4 nodos e ir haciendo experimentos. Experimentos posibles:

  • mejor tipo de baterías, 2 AA? 1 de moneda de 3V ?
  • aguante de baterías
  • alcance de la comunicación
  • ir haciendo pruebas con el SW

objetivo final que hemos visualizado: una placa integrada con modulo radio y atmega, super pequeña, de bajo coste (<5€), con un kit de SW para redes de sensores. Como SW se considera incluso hacer un porting del stack 6loWPAN.


Pruebas 26/9/2013:

Hemos montado el Atmega328p en la breadboard. Para programar el ATMega desde Arduino hemos seguido este tutorial. Hay dos fases: en una cargas el bootloader en el ATmega virgen. Para ello se necesita un Arduino que envíe el firmware por los pines del SPI. RECORDAR de cargar el sketch Arduino as ISP de la carpeta de los ejemplos !

En la segunda fase puedes cargar tus sketch, en este caso se usa el puerto serie, y se puede usar un Arduino quitandole el chip O un cable USB-to-serial (FTDI chip) si lo hay.

Primer prototipo

Para ir probando la comunicación con el nRF24L01 hemos cargado un sketch para usar el chip Nordic. Hemos utilizado la librería de maniacbug aquí y el codigo de ejemplo GettingStarted.

Para cargar el sketch en el ATmega hemos utilizado un cable FTDI y NO HA FUNCIONADO, no sabemos porqué. Entonces hemos usado el Arduino SIN MCU y HA FUNCIONADO. Hemos conectado el nRF24L01 según este esquema, cuidado con hacer el mapeado entre los pines de Arduino y los del Atmeg328 (ver aquí)!

El Atmega ve el chip y es capaz de enviar paquetes pero no de recibirlos. No sabemos porqué pero sospechamos que es por tener el clock a 8MHz en lugar de 16.Hay indicios de que afecta de alguna manera. El chip puede comunicar hasta 10 Mhz en el bus SPI. En la libreria tenemos SPI.setClockDivider(SPI_CLOCK_DIV4); Entonces con un un atmega328 @ 8 Mhz, 8/4 = 2 mbs , no debería haber problema. Otra posible causa pueden ser los delays().


Pasos a seguir:

Dario: va a enviar un correo al creador de la librería RF24 preguntandole cual es la mas actualizada y si el ve algún problema en tener un Arduino a 8MHz en lugar de 16Mhz.


Roberto: se encarga de ver los sockets e intenta programar el ATMega con el cable FTDI

Todos: resolver el problema de los 8MHz. Comprar socket para los chips. Construir primer prototipo.

Pruebas del 3/10/2013

A veces el ATMega cuando envía un paquete imprime dos tipos de mensajes distintos:

  Now sending 22333...failed.
  Failed, response timed out.

o

  Now sending 23609...ok...Failed, response timed out.


La diferencia entre la primera linea y la segunda esta en este codigo (GettingStarted.pde):

  // First, stop listening so we can talk.
  radio.stopListening();
  
  // Take the time, and send it.  This will block until complete
  unsigned long time = millis();
  printf("Now sending %lu...",time);
  bool ok = radio.write( &time, sizeof(unsigned long) );
  if (ok)
    printf("ok...");
  else
    printf("failed.\n\r");


Pruebas hechas:

  • seteado SPI_CLOCK_DIV2 en RF24.cpp de la libreria RF24 de Maniacbug: NINGUN CAMBIO
  • comentado linea SPI_CLOCK_DIV en RF24.cpp: NINGUN CAMBIO
  • metiendo un condensador de 10 microF entre pin 3.3v y tierra: AHORA SOLO DA Now sending 23609...ok...Failed, response timed out.
  • metiendo 500 ms en lugar de 200 en esperar la respuesta: NINGUN CAMBIO
  • comprobado que los delay() y millis() sean buenos: son buenos (los segundos son segundos)

Al final hemos encontrado el problema: eran unos cables mal conectados entre el Arduino (no el Atmega) y la placa nfr (culpa de Roberto). CUIDADO con las librerías (maniacbug o mirf o Mike) que tienen distintos cableados !!!

Hemos quitado el condensador y la mitad de los paquetes se perdían.


Conclusiones:

  • la comunicación funciona entre los dos
  • meter condensador de 10microF es suficiente para no perder paquetes

Observacion: el modulo nrf24L01 tiene un oscilador a 16MHz, en una placa integrada se podría aprovechar también para el ATMega Para compartir el cristal, los CI tienen que estar cerca, un CI excita el crstal, mientras el otro solo "lee" la salida: http://forum.arduino.cc/index.php?topic=78584.0 .


Para la próxima: ir probando con baterías distintas, diseñar el prototipo


Pruebas 26/10/2013:

Hemos probado a alimentar el ATMega con 3.3 V -> no funciona. A partir de 4.5 parece que funcione. Hemos intentado quitandole el brownout como sugiere este post(settings a 0xFF), pero nada.


Pruebas 27/10/2013:

Bozo consigue enviar datos por serial a partir de una tensión de 2.3 voltios. Con 2.1 voltios funciona pero no arranca bien.

El circuito está hecho con una fuente regulable sin condensadores y un parpadeo de led. El .hex fue generado seleccionando "atmega 328 on bread board" como tipo de placa

  avrdude: Device signature = 0x1e950f
  avrdude: safemode: lfuse reads as E2
  avrdude: safemode: hfuse reads as D8
  avrdude: safemode: efuse reads as 7


Pruebas 28/10/2013:

Roberto también consigue hacerlo funcionar. No he identificado el problema del otro día. Probado con dos pilas AA a 2.8v sin ningún problema. Con la salida de 3.3v de Arduino también funciona.

El valor de los fuses utilizados son los que proponía kerry Wong sin modificar el brown-out que de momento no ha sido necesario.

  low_fuses=0xE2
  high_fuses=0xDA
  extended_fuses=0x05


Pruebas 23/11/2013:


Prueba de Dario para montar una placa integrada. Dado que no quiere esperar los zocalos se ha soldado PIC y chip radio todo juntos, como NO hay que hacer.


Conexión de pines:

ATMEGA -> NRF24L01:
PIN 4 PD2 -> PIN 8 NRF24 (IRQ)
PIN 15 PB1 -> PIN 3 NRF24 (CE)
PIN 16 PB2 -> PIN 4 NRF24 (CSN)
PIN 17 PB3 -> PIN 6 NRF24 (MOSI)
PIN 18 PB4 -> PIN 7 NRF24 (MISO)
PIN 19 PB5 -> PIN 5 NRF24 (SCK)


además hay que tener en consideración los pines de alimentación y para programar el PIC:

ATMEGA:
PIN 1 PC6 -> Reset
PIN 2 PD0 -> TX
PIN 3 PD1 -> RX
PIN 7 VCC -> 3V
PIN 8 GND -> GND
PIN 20 AVCC -> 3V (cuidado, es necesario cuanto el pin 7 !!)

NRF24:
PIN 1 GND -> GND
PIN 2 VCC -> 3V

Pruebas hechas:

  • con un sketch normal de Arduino (AnalogReadSerial) -> OK
  • con sketch de ejemplo de nrf24l01 (GettingStaretd) -> Parece que OK, me imprime:
  STATUS = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
  RX_ADDR_P0-1 = 0xf0f0f0f0d2 0xf0f0f0f0d2
  RX_ADDR_P2-5 = 0xc3 0xc4 0xc5 0xc6
  TX_ADDR = 0xf0f0f0f0d2
  RX_PW_P0-6 = 0x20 0x20 0x00 0x00 0x00 0x00
  EN_AA = 0x3f
  EN_RXADDR = 0x03
  RF_CH = 0x4c
  RF_SETUP = 0x07
  CONFIG = 0x0f
  DYNPD/FEATURE = 0x00 0x00
  Data Rate = 1MBPS
  Model = nRF24L01+
  CRC Length = 16 bits
  PA Power = PA_HIGH
  • con sketch scanner -> parece que OK, me imprime:
  RF24/examples/scanner/
  00000000000000001111111111111111222222222222222233333333333333334444444444444444555555555555555566666666666666667777777777777777
  0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
  0001345696647c456565200279978c7ba66777960000000000000000000101310000000000000000000000000000000000000000000000000000000000000000
  000025457b9774b883663002674ca9987799b6863000000000000000066761022110000000000000000000000000000000000000000000000000000000000000
  000225652886a777078500125b5755bb87d967762000000000000000010054330000000000000000000000000000000000000000000000000000000000000000

Pruebas del 11/12/2013 al 17/12/2013:

He hecho una larga serie de pruebas con distintas combinaciones inlcuyendo:

  • 1 placa casera con ATMega 328p + nrf24 y un Arduino sin PIC como lector serial
  • Arduino UNO con nrf24
  • Arduino Diecimila con nrf24
Segundo prototipo

Variables probadas:

  • alimentación del chip radio con el pin 3.3V de Arduino
  • alimentación del chip radio por 2 pilas AA
  • presencia (o no) de un condensador entre polos + y - como reserva de energía
  • canal de transmisión estandar y canal 100

ambos PICs programados con el sketch Getting Started de ManiacBug Los dos Arduinos estan conectados a mi ordenadro a traves de dos cables USB mientras puedo ver los puertos serie con un programita (RealTerm) abierto dos veces.


Resultados que he encontrado:

  • conviene siempre, por cada prueba, resetear los Arduinos para tenerlo todo listo
  • a veces, aunque el sketch Getting started debería empezar en modo 'R', en realidad no funciona, por lo cual es mejora pasar primero por una fase 'T' y luego volver a una fase 'R'
  • la alimentación 3.3V proporcionada por el Arduino no es suficiente, con un condensador mejora un poco, pero no del todo. Si alimentado con dos pilas funciona bien, incluso sin condensador (con pilas cargadas)
  • un condensador de capacidad mayor no mejora las cosas, de hecho las empeora

Pruebas del 23/1/2014:

Despues de haber perdido la cabeza con la librería de Maniacbug, y con la variante de Copeland, probé la librería de Mike y me funcionó perfectamente.

Hardware:

  • placa ad-hoc con Atmega328,NRF24 y un LED RGB conectado al PIC
  • Arduino Diecimila montado a un NRF24 y a tres potenciometros

Codigo:

  • la placa con LED se pone en escucha de 3 bytes en el pipe 1. Si los recibe modula los PWM de los tres colores del LED (analogWrite)
  • el Arduino con potenciometros lee el valor de ellos y si ha cambiado envía un paquete con 3 bytes con los tres valores correspondientes mapeados de 0 a 255

Resultado:

  • funciona ! practicamente se realiza un mando a distancia del LED, la placa ad-hoc funciona también estando completamente aislada, es decir con sus pilas y sin conectar a nada mas
  • analizando la traza en el puerto serie del Arduino se puede ver que alrededor de 1/3 o 1/4 de los paquetes no reciben ACK

Pruebas 26/1/2014:

Pruebas de Andrés (Andrew) tras recibir unos clones de Arduinos Pro Mini y módulos nRF24L01+ de China y soldar las conecciones entre los módulos y los Arduinos. Los Arduinos son de 5V y no tienen salidas 3.3V, se han usado 3 diodos en cada modulo para que este pueda ser alimentado con 5V, el voltaje baja unos 1.9V. Los demas pines del nRF24 son compatibles con 5V.

Despues de muchas pruebas consegui transmisiones entre dos placas conectadas a diferentes puertos USB de una maquina. El programa simula una coneccion serie - lo que recibe del puerto serie lo envía por radio, lo que recibe por radio lo devuelve por puerto serie. Por ahora no usa interrupciones, sin embargo usa la opcion "Dynamic Payload Size" del nRF24L01+ por lo que no es compatible con el chip nRF24L01 (sin el mas).

El programa lee los primeros 6 bytes del EEPROM del atmega, y usa los primeros 3 bytes como direccion propia, y los siguientes tres como direccion de destino de sus paquetes. Eso es porque al parecer ni los chips Atmega, ni los nRF24 contienen un numero serie unico que se pueda acceder desde el software, para asignar direcciones. Las direcciones las grabe al eeprom con el siguiente comando, en este ejemplo 73 6c 30 ("sl0" en ascii) es la direccion priopia y 73 6c 31 ("sl1") es la del destino,

  • $ avrdude -F -p atmega328p -P /dev/ttyUSB0 -c arduino -b 57600 -U eeprom:w:0x73,0x6c,0x30,0x73,0x6c,0x31:m

En otra ocasion corrijo algo en el codigo y empiezo a meter en github. El objetivo por ahora es que ese mismo codigo lo pueda usar el programa de arranque optiboot, para poder flashear las placas por radio.

Pruebas 4/2/2014:

Andres consigue subir por radio el programa "Blink" de los ejemplos de Arduino mediante una version modificada de optiboot. Importante: La comunicacion todavia no es fiable y la mayoria de la veces la programacion falla en algun momento del proceso.

El hardware consiste en dos placas Arduino con dos modulos nRF24L01+ a distancia de ~50cm y conectadas con adapadores serie-USB a una maquina. El arduino A corre un programa "flasher" que principalmente hace de adaptador entre puerto serie y el nRF24L01+. El arduino B corre una version modificada de optiboot. El portatil corre avrdude. Por ahora el arduino B lo estoy reiniciando dandole al boton RESET a mano para que inicie optiboot. Avrdude se comunica con la placa A, sus comandos se envian tal cual a la placa B por radio, donde el optiboot las interpreta, enviando respuestas a placa A.

Creados dos repositorios,

  • https://github.com/balrog-kun/optiboot contiene la version de optiboot con sus modificaciones. Realmente el codigo nuevo es poco, pero depurarlo me ha costado bastante. Ademas del soporte nRF24L01+ tiene alguna mejoras mas, aunque cada opcion cuesta un incremento del tamaño. Con todo habilitado pesa 1.4kB mientras que el optiboot estandar solo 450 bytes (aun sigue siendo menos que el bootloader Arduino por defecto, de 2kB).
  • https://github.com/balrog-kun/lights contiene los programas "flasher" y "passthrough", luego tambien podra tener la version "puro C" del controlador de los Sensor Nodes / controladores de luz y/o electrodomesticos. "passthrough" es simplemente una pasarela entre puerto serie y nRF - con dos placas puede simular una coneccion serie. "flasher" es casi lo mismo, con la diferencia que antes de enviar cualquier dato envia un paquete consitente de solamente un byte 0xff, que se supone que va a forzar la otra placa que se reinicie (eso tendra que hacerlo el programa que va a correr en esa placa porque), y despues de 100ms un paquete de 3 bytes que contienen la direccion nRF24 a la que el optiboot va a responder.

Falta investigar porque a veces se pierde algun paquete... jugando con las dos placas en modo "pasarela" nunca me pierde ningun paquete. Eso a distancia de 50cm.

El problema de resetear el controlador "remoto" es complicado porque cada programa subido tendra que tener la posibilidad de reiniciar la placa por radio. Si en algun momento se sube codigo que no lo puede hacer (p.e. se cuelga), habra que reiniciarlo posiblemente con el interruptor general de la luz, en caso de alimentacion de la red, y en caso de alimentacion por bateria, a mano. Se puede usar el Watchdog Timer del AVR para reducir la probabilidad de que pase eso.

Pruebas 5/2/2014:

He subido el codigo a mi repositorio github: https://github.com/dariosalvi78/Sensorino

1) he ido refinando y debugeando la libreria de base NRF24. La verdad es que me está costando, pero en cada avance que hago soluciono cosas que había. Me lo vais a agradecer.

2) he empezado a montar dos abstracciones de nivel superior: Sensorino y Base. Sensorino abstrae los medios de comunicación haciendo algunas asunciones que luego os explico. Base es lo mismo, solo que se supone que funciona como gateway hacia Internet y añade un protocolo por serial para hablar con el PC.

3) he empezado a montar protocolos de aplicación encima. De momento he montado y probado un protocolito para sincronizar el reloj de los Atmegas. Esto nos viene muy útil para marcar el tiempo en el cual se toman medidas, especialmente si tenemos que almacenar localmente medidas.

asunciones hechas:

  • las direcciones son de 4 bytes, tipo las IPs de IPv4
  • pipe 0 es de broadcast, es decir tiene una dirección compartida entre todos y no tiene ACKs
  • pipe 1 es el canal por defecto para comunicación unicast. Es un pipe compartido, por lo cual me ha sido necesario añadir una capa donde el remitente pueda especificar su dirección
  • los niveles superiores, o servicios, son identificados por un entero de 16 bits
  • hay tres tipos de "servicios": los en broadcast, para cuestiones de configuración de la red, los en unicast hacia la base, para enviar datos de medidas, y los unicast hacia los sensorinos, para activar cosas (tipo interruptores, actuadores etc.)

Pruebas 7/2/2014:

Corregidos los problemas con fallos aleatorios durante programacion de Arduino por nRF24L01 pero hace falta comprobar si otros modulos se comportan de la misma manera ya que uno de los errores era muy raro - ocurria siempre que habia exactamente tres transmisiones seguidas y luego recepcion.

He hecho unas pocas pruebas muy poco cientificas de la tasa de fallos intentando flashear el mismo programa a la placa remota con avrdude. El programa de prueba tiene 4900 bytes, con lo cual avrdude envia cerca de 5400 bytes y recibe otros 5100 bytes (ya que siempre despues de escribir la memoria flash la vuelve a leer). Se transmiten y reciben alrededor de 550 paquetes, de tamaños entre 1 y 32 bytes. El nRF24 estaba configurado para tasa 1Mbps y hasta 15 retransmisiones. No tuve en cuenta la orientacion y polarizacion de las antenas, aunque todas antenas son direccionales.

  • a distancias < 5m la comunicacion parecia fiable, se pudo programar la placa remota 20 seguidas sin fallo.
  • a 5m a traves de una pared fallaba la mayoria de veces, pero algunas programaba bien.
  • a 9m a traves de dos paredes fallaba en promedio en el momento de haber subido 30-40% del programa, que seria despues de haber transmitido ~150-200 paquetes sin fallo, o sea que la tasa de error todavia debe ser baja (~1%).

Pruebas 13/2/2014:

Estoy intentando poner el PIC a dormir y despertarlo con el watchdog o los interrupts externos.

Sleep: para el maximo ahorro se utiliza el SLEEP_MODE_PWR_DOWN

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();

En principio podríamos llegar a tener un consumo de solo 1uA !

Wakeup con watchdog: hay que programar una ISR

  ISR( WDT_vect ) {
  //codigo aquí
  }

y habilitar el watchdog y su interrupción y que no resetee el PIC:

  MCUSR &= ~(1 << WDRF);
  WDTCSR |= (1 << WDCE) | (1 << WDE);
  WDTCSR = (1<< WDP0) | (1 << WDP3);
  WDTCSR |= _BV(WDIE);

en la ISR conviene deshabilitar el watchdog para que no toque los huevos hasta que nos durmamos otra vez

Wakeup con interrupt en pin: una buena guía es esta. el problema es que si utilizamos el "External Interrupt Request" en modo sleep PWR_DOWN solo funciona cuando el pin va a LOW, además solo podemos utilizarlo para los pines 2 y 3. Entonces he utilizado los itnerrupts de pin change (PCINT0_vect, PCINT1_vect y PCINT2_vect), pero para programarlos hay que meter un poco de codigo porqué el API de Arduino no los incluye. Me he basado en esta documentación.

  //Programamos las ISR (no hacen nada y valen para todo tipo de pin)
  ISR(PCINT0_vect){
  }
  ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
  ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));

Luego es necesario identificar, en pase al pin en concreto, cual es el PCINT a utilizar en la mascara, para ello me he hecho una función:

  byte pinToInt(byte pin){
    if(pin <=7)
      return PCINT16 + pin;
    else if(pin >=8 && pin <=13)
      return PCINT0 + (pin-8);
    else return PCINT8 + (pin -14);
  }

Y el PCIE para habilitar el interrupt (se habilitan grupos de pines juntos, en 3 grupos):

  byte pinToIE(byte pin){
    if(pin <=7)
      return PCIE2;
    else if(pin >=8 && pin <=13)
      return PCIE0;
    else return PCIE1;
  }

y habilitamos:

  PCICR |= (1 << pinToIE(pinInt));
    if(pinInt <=7)
      PCMSK2 |= (1 << pinToInt(pinInt));
    else if(pinInt >=8 && pinInt <=13)
      PCMSK0 |= (1 << pinToInt(pinInt));
    else PCMSK1 |= (1 << pinToInt(pinInt));

Para una panoramica de como se nombran los pines y la correspondencia entre numero de pin, mascara, y registro para habilitarlos mirar este dibujo:

                      +-\/-+
   PCINT14      PC6  1|    |28  PC5 (AI 5)PCINT13
   PCINT16(D 0) PD0  2|    |27  PC4 (AI 4)PCINT12
   PCINT17(D 1) PD1  3|    |26  PC3 (AI 3)PCINT11
   PCINT18(D 2) PD2  4|    |25  PC2 (AI 2)PCINT10
   PCINT19(D 3) PD3  5|    |24  PC1 (AI 1)PCINT9
   PCINT20(D 4) PD4  6|    |23  PC0 (AI 0)PCINT8
                VCC  7|    |22  GND
                GND  8|    |21  AREF
   PCINT6       PB6  9|    |20  AVCC
   PCINT7       PB7 10|    |19  PB5 (D 13)PCINT5
   PCINT21(D 5) PD5 11|    |18  PB4 (D 12)PCINT4
   PCINT22(D 6) PD6 12|    |17  PB3 (D 11)PCINT3
   PCINT23(D 7) PD7 13|    |16  PB2 (D 10)PCINT2
   PCINT0 (D 8) PB0 14|    |15  PB1 (D 9) PCINT1
                      +----+
  
  pin  PICINT PCMSK
  0      16    2
  1      17    2
  2      18    2
  3      19    2
  4      20    2
  5      21    2
  6      22    2
  7      23    2
  8      0     0
  9      1     0
  10     2     0
  11     3     0
  12     4     0
  13     5     0
  PB5    6     0
  PB7    7     0
  A0(14) 8     1
  A1(15) 9     1
  A2(16) 10    1
  A3(17) 11    1
  A4(18) 12    1
  A5(19) 13    1
  PC6    14    1
  
  
  pin   -> PCINT -> PCMSK
  0-7   -> 16-23 ->  2
  8-13  -> 0-5   ->  0
  14-19 -> 8-13  ->  1