Go Down

Topic: Controller des strip LED avec commande wifi à distance (NRF24L01) et FastLED (Read 294 times) previous topic - next topic

zembois73

Bonjour à tous,

J'ai actuellement pour projet de remplacer mes vieilles guirlandes guinguette à ampoule par une guirlande fabriquée maison avec des cabochons de 60mm avec Leds et puce UCS2903.
L'idée est d'avoir 8 guirlandes de 40 led. ( chaque guirlande a son arduino Uno qui sert de contrôlleur) et que ces 8 guirlandes soient synchronysées bien qu'espacées de 100m.

Pour le moment, chaque guirlande fonctionne individuellement avec le programme joint (programme avec librairie Fastled et 8 animations qui changent à chaque impulsion de bouton).

La partie qui me bloque est au niveau de la synchronisaition de ces guirlandes entre elles. Je doit utiliser un autre arduino qui possède un bouton qui switchera le programme sur les guirlandes (pour remplacer la pression du bouton sur la guirlande elle même). Pour cela, chaque arduino est montée avec le NRF24L01.
Mes antennes fonctionnent bien avec l'exemple de base pour la communication WIFI :

Voici mon code pour l'emetteur (avec bouton ou délai pour changement automatique des programmes)

Code: [Select]

/*
* Arduino Wireless Communication Tutorial
*     Example 1 - Transmitter Code
*               
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#define button 2
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7, 8); // CE, CSN
const byte address[6] = "00001";
boolean buttonState = LOW;

void setup() {
  pinMode(button, INPUT);
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
  pinMode(5,OUTPUT); // LED output
}

void loop() {
 
  static unsigned char ledState = LOW;
  static unsigned char buttonState = LOW;
  static unsigned char lastButtonState = LOW;
  static unsigned long ledCameOn = 0;
 
  // If the LED has been on for at least 5 seconds then turn it off.
 
    if(millis()-ledCameOn > 2000)
    {
      digitalWrite(5,LOW);
      ledState = LOW;
    }
 

  // If the button's state has changed, then turn the LED on IF it is not on already.
  buttonState = digitalRead(2);
  if(buttonState != lastButtonState || millis()-ledCameOn > 2000)
  {
    lastButtonState = buttonState;
    if((buttonState == HIGH) && (ledState == HIGH)|| millis()-ledCameOn > 2000)
    {
      digitalWrite(5,HIGH);
      ledState = LOW;
       
  radio.write(&ledState, sizeof(ledState));

      delay(15);
      ledState = HIGH;
      digitalWrite(5,LOW);
      ledCameOn = millis();
    }
  }
 
  radio.write(&ledState, sizeof(ledState));
  delay(100);

}


Côté recepteur,c 'est plus la galère, j'ai essayé de rajouter dans le programme"test complet" les elements dans le setup et le loop MAIS rien n'y fait, l'arduino lit pendant une fraction de secondes les infos envoyées puis plus rien.
A mon niveau et vu ce que j'ai lu sur les arduino, rien ne me choque vu que celle-ci n'est pas multitâche, elle peut soit lire, soit donner les infos pour allumer les LEDs.
Or j'ai vu des vidéos, notamment celle-ci, ou le principe que je souhaite mettre en place fonctionne
https://www.youtube.com/watch?v=9dU5IgLrXLQ

Ansi je me tourne vers la communauté pour me débloquer là dessus


lesept

Mais pourquoi ne pas utiliser qu'un seul Arduino et des fils plus longs pour le relier aux 8 guirlandes de LEDs ? 320 LEDs, c'est faisable avec un seul Arduino, et ça simplifierait la synchro.
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

zembois73

Parceque mes guirlandes seront espacées de 200m chacunes et que c'est une installation montée/démontée régulièrement...


hbachetti

Salut

Il faudrait poster le code avec les modifications visant à intégrer la réception NRF24L01.

Je dirais à première vue que pendant l'exécution des routines de pilotage des LEDs, le processeur ne fait rien d'autre.

Il faudrait recevoir les requêtes sous interruption.

Je n'ai jamais implémenté de réception NRF24L01 sous interruption, mais cela doit être possible, puisqu'il possède une broche dédiée.

Pet être ICI.

Essaie ces deux sketches sans les modifier, afin de vérifier leur fonctionnement.
Ensuite tu pourras intégrer les lignes nécessaires dans ton propre récepteur.
N'oublie pas de câbler la broche IRQ du NRF24L01 sur la pin 3 de l'ARDUINO.

@+
Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

zembois73

Merci pour ta réponse,
j'ai essayé avec le code donné en exemple, et je reçoit bien  les paquets en temps réel,
Or, lorsque j'ajoute un effet à jouer( ici colorwipe) selon ce code :

Code: [Select]


#include <Arduino.h>
#include "FastLED.h"
#if FASTLED_VERSION < 3001000
#error "Requires FastLED 3.1 or later; check github for latest code."
#endif
#include <EEPROM.h>
#define NUM_LEDS 38   // défini le nombre de led

CRGB leds[NUM_LEDS];
#define PIN 2

#define FRAMES_PER_SECOND  120     // défini le nombre de frames par seconde

int const RPot = A0;
int const GPot = A1;
int const BPot = A2;

#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <avr/sleep.h> //library needed to use AVR based sleep API

const int pinCE = 7; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 8; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
byte gotByte = 0; //used to store payload from transmit module
volatile int count = 0; //tracks the number of interrupts from IRQ
int pCount = 0; //tracks what last count value was so know when count has been updated
RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI)
const uint64_t pAddress = 0xB00B1E5000LL; //Create a pipe addresses for the 2 nodes to communicate over, the "LL" is for LongLong type

void setup() {
 wirelessSPI.begin(); //Start the nRF24 module
 wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload
 wirelessSPI.enableAckPayload(); //allows you to include payload on ack packet
 wirelessSPI.maskIRQ(1,1,0); //mask all IRQ triggers except for receive (1 is mask, 0 is no mask)
 wirelessSPI.setPALevel(RF24_PA_LOW); //Set power level to low, won't work well at higher levels (interfer with receiver)
 wirelessSPI.openReadingPipe(1,pAddress); //open pipe o for recieving meassages with pipe address
 wirelessSPI.startListening(); // Start listening for messages
 attachInterrupt(1, interruptFunction, FALLING); //Create interrupt: 0 for pin 2 or 1 for pin 3, the name of the interrupt function or ISR, and condition to trigger interrupt
 FastLED.addLeds<UCS2903, PIN, RGB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
}



void showStrip() {
 #ifdef ADAFRUIT_NEOPIXEL_H
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   FastLED.show();
 #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
 #ifdef ADAFRUIT_NEOPIXEL_H
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
 #endif
}

void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue);
  }
  showStrip();
}

void colorWipe(byte red, byte green, byte blue, int SpeedDelay) {
  for(uint16_t i=0; i<NUM_LEDS; i++) {
      setPixel(i, red, green, blue);
      showStrip();
      delay(SpeedDelay);
  }
}


void loop() {

if(pCount < count) { //If this is true it means count was interated and another interrupt occurred
 Serial.begin(57600); //start serial to communicate process
 Serial.print("Receive packet number ");
 Serial.print(count);
 Serial.print(" has content: ");
 Serial.println(gotByte);
 Serial.end(); //have to end serial since it uses interrupts
 pCount = count;

 colorWipe(0xff,0x00,0x00, 100);
  colorWipe(0xff,0xff,0xff, 100);
 }
}

//This is the function called when the interrupt occurs (pin 2 goes high)
//this is often referred to as the interrupt service routine or ISR
//This cannot take any input arguments or return anything
void interruptFunction() {
 count++; //up the receive counter
 while(wirelessSPI.available()) { //get data sent from transmit
 wirelessSPI.read( &gotByte, 1 ); //read one byte of data and store it in gotByte variable
 }
}


les lumières suivent bien le programme prévu, mais c'est du côté de la réception que ça se gâte :
Receive packet number 5 has content: 238
Receive packet number 8 has content: 42
Receive packet number 13 has content: 211
Receive packet number 14 has content: 117
Receive packet number 17 has content: 167
Receive packet number 23 has content: 125
Receive packet number 25 has content: 63
etc...,

la réception ne se fait qu'à chaque fin de programme (colorwipe), j'ai donc bon nombre de paquets de données non lues...



lesept

Il faut inclure la lecture des paquets DANS l'animation, dans la boucle for(uint16_t i=0; i<NUM_LEDS; i++).
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

zembois73

Mille Merci !
J'arrive bien à recevoir tout les paquets de données et l'animation se joue parfaitement, à moi désormais de bien adapter ça à mon programme ;D  ;D  ;D

hbachetti

Quote
la réception ne se fait qu'à chaque fin de programme (colorwipe), j'ai donc bon nombre de paquets de données non lues...

Receive packet number 5 has content: 238
Receive packet number 8 has content: 42
Receive packet number 13 has content: 211
Receive packet number 14 has content: 117
Receive packet number 17 has content: 167
Receive packet number 23 has content: 125
Receive packet number 25 has content: 63

Les données ont été toutes lues, contrairement à ce que tu crois. Simplement, ta boucle loop() ne parvient pas à traiter suffisamment vite ce qui est reçu par la routine d'interruption.

En lisant les paquets dans l'animation, tu perds tout l'intérêt de la gestion par interruption, et tu sera obligé d'inclure une lecture de paquet dans chaque boucle de ton programme, et cela la ralentira.
Pas TOP.

@+

Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

hbachetti

Ce qu'il faut avant tout décider, c'est quelle doit être la réaction de ton animation si tu reçois une nouvelle commande.
1) soit tu interromps l'animation pour passer à la suivante.
2) soit tu la laisse se terminer avant de passer à la suivante.

Dans le premier cas, il faut que dans ta boucle tu surveilles les changements de valeur de la commande, et tu interromps la boucle si la valeur change.

Dans le deuxième cas, soit tu stockes les commandes reçues pendant une animation, soit non.
Dans le cas où tu stockes, il faut le faire dans un buffer. Quand l'animation se termine, tu prends la prochaine commande dans le buffer.

Et ainsi de suite.

@+
Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

Go Up