PCA9685 + ESP8266 -> PWM-light wave not working


#1

Hello!
I built an artwork made of glass and want to illuminate this with several LEDs in the background. For this I got the pca9685 pwm module to drive enough leds with my esp8266. then I wrote a program out of the pca9685 -demofile and want to turn on the light with a switch.
This formula is used:
val = (((float) sin (millis () / y + i / x * 2.0 * PI) + 1) * z);

The formula controls the LEDs in a kind of wave. The 3 variables x, y and z change the wave in terms of illuminance, length and peaks …
The problem I have is, that the pca9685-demo program actually works well without the inclusion of cayenne (of course without the ability to set the variables). Now, with the involvement of Cayenne, the program works, but always only ONE value is written out on all 16 pwm outputs, then the values stuck and after a short brake the next value is written on all 16 outputs (eg value1= 0, value2= 0, value3= 0, value4= 0, … value16= 0) instead of describing a wave (it should be like this -> value1:= 0, value2= 100, value3= 500, value4= 1000, … value8= 2000, value9= 1500, value10= 1000 … value16= 0)

I hope that I have described my problem so that people can understand what i mean and ask for any help to realize my project.

Thank you!

#define CAYENNE_PRINT Serial
#include <CayenneMQTTESP8266.h>

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();


char ssid[] = "";
char wifiPassword[] = "";

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
char username[] = "";
char password[] = "";
char clientID[] = "";


#define VIRTUAL_CHANNEL 1
#define VIRTUAL_CHANNELx 2
#define VIRTUAL_CHANNELy 3
#define VIRTUAL_CHANNELz 4

int switch1=0;
int x=8;
int y=900;
int z=2048;

void setup() {
	Serial.begin(115200);
	Cayenne.begin(username, password, clientID, ssid, wifiPassword);

  Serial.println("16 channel PWM test!");

  pwm.begin();
  pwm.setPWMFreq(1600);  // This is the maximum PWM frequency

  // if you want to really speed stuff up, you can go into 'fast 400khz I2C' mode
  // some i2c devices dont like this so much so if you're sharing the bus, watch
  // out for this!
  Wire.setClock(400000);
}

void loop() {
	Cayenne.loop();

Serial.println(switch1);
if (switch1==1){
for (uint8_t i = 0; i < 16; i++) {
    int val = (((float)sin(millis() / y + i / x * 2.0 * PI) + 1) * z);
  
      val = map(val,0,500, 0, 300);   //max min Wert festlegen
      val = map(val,501,4096, 301, 4096);
      pwm.setPWM(i, 0, val);
      Serial.println(val);
      Serial.println(x);
      Serial.println(y);
      Serial.println(z);
      
    }
    
#ifdef ESP8266
    yield();  // take a breather, required for ESP8266
#endif
  }
else{Serial.println("Stop");
  for (uint8_t i = 0; i < 16; i++) { 
    pwm.setPWM(i, 0, LOW);
  }
}
}

CAYENNE_IN(VIRTUAL_CHANNEL)
{
 switch1 = getValue.asInt();
}

CAYENNE_IN(VIRTUAL_CHANNELx)
{
 x = getValue.asInt();
}

CAYENNE_IN(VIRTUAL_CHANNELy)
{
 y = getValue.asInt();
}

CAYENNE_IN(VIRTUAL_CHANNELz)
{
 z = getValue.asInt();
}

#2

your code looks fine and should work but still i have made some changes try with that.
First change x,y and z value from the dashboard and then turn ON Switch.

#define CAYENNE_PRINT Serial
#include <CayenneMQTTESP8266.h>

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();


char ssid[] = "";
char wifiPassword[] = "";

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
char username[] = "";
char password[] = "";
char clientID[] = "";


#define VIRTUAL_CHANNEL 1
#define VIRTUAL_CHANNELx 2
#define VIRTUAL_CHANNELy 3
#define VIRTUAL_CHANNELz 4

int switch1=0;
int x=8;
int y=900;
int z=2048;

void setup() {
	Serial.begin(115200);
	Cayenne.begin(username, password, clientID, ssid, wifiPassword);

  Serial.println("16 channel PWM test!");

  pwm.begin();
  pwm.setPWMFreq(1600);  // This is the maximum PWM frequency

  // if you want to really speed stuff up, you can go into 'fast 400khz I2C' mode
  // some i2c devices dont like this so much so if you're sharing the bus, watch
  // out for this!
  Wire.setClock(400000);
}

void loop() {
	Cayenne.loop();
}

CAYENNE_IN(VIRTUAL_CHANNEL)
{
 switch1 = getValue.asInt();
Serial.println(switch1);
Serial.println(x);
Serial.println(y);
Serial.println(z);
if (switch1==1){
for (uint8_t i = 0; i < 16; i++) {
    int val = (((float)sin(millis() / y + i / x * 2.0 * PI) + 1) * z);
  
      val = map(val,0,500, 0, 300);   //max min Wert festlegen
      val = map(val,501,4096, 301, 4096);
      pwm.setPWM(i, 0, val);
      Serial.println(val);
      Serial.println(x);
      Serial.println(y);
      Serial.println(z);
      
    }
    
#ifdef ESP8266
    yield();  // take a breather, required for ESP8266
#endif
  }
else{Serial.println("Stop");
  for (uint8_t i = 0; i < 16; i++) { 
    pwm.setPWM(i, 0, LOW);
  }
}
}

CAYENNE_IN(VIRTUAL_CHANNELx)
{
 x = getValue.asInt();
}

CAYENNE_IN(VIRTUAL_CHANNELy)
{
 y = getValue.asInt();
}

CAYENNE_IN(VIRTUAL_CHANNELz)
{
 z = getValue.asInt();
}

#3

Thank you for your help!

I tried your code, but now the for-loop runs only 1 time … i made a “while (switch1==1)” instead of the “if(switch1==1)” and now it runs endless…
but on each output-pin are always the same values (over a loop pass of 16 times) instead of describing a wave… which I meant before -> describing a wave - for example it should look like this -> value1 on output1:= 0, value2 on output2= 100, value3 on output3 = 500, value4 on output4= 1000, and so on … over a loop pass of 16 times. if it would do that it looks like a scrolling wave :slight_smile:
and i don´t understand this because of the millis() function in the formula every “i” should be an other value…so it shouldn´t be that every output has the same value!?
. without cayenne its working well :confused: ??


#4

can you share the code which works without cayenne.


#5

ok now i found a failure of my code x,y must be float and now the wave works nearly perfekt.

But how to get the values in a continuous flow so that the wave lights up not only 16 values … so i did a while loop like in the code below. If the switch1 is ON … the wave runs endless and no switch or slider is responding anymore.

while (switch1==1){
  for (uint8_t i = 0; i < 16; i++) {
    int val = (((float)sin(millis() / y + i / x * 2.0 * PI) + 1) * z);
           .......................
           ...............
    }
    #ifdef ESP8266
    yield();  // take a breather, required for ESP8266
    #endif
    }

while (switch1==0){
   Serial.println("Stop");
   for (uint8_t i = 0; i < 16; i++) { 
    pwm.setPWM(i, 0, LOW);
  }

#6

this is because it is in while true loop and it will stay in that loop forever.


#7

yes I understand this. But how do you get that into an endless loop that can be switched ON and OFF and during the loop the variables can be changed?

normally I would put this in the void loop ().


#8

Putting in void loop also won’t work. Can you share the entire code you used to work without cayenne.


#9

ok this is the basic code:

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

void setup() {
  Serial.begin(115200);
  Serial.println("16 channel PWM test!");

  pwm.begin();
  pwm.setPWMFreq(1600);  
  Wire.setClock(400000);
}

void loop() {
  // Drive each PWM in a 'wave'
  for (uint8_t i = 0; i < 16; i++) {
    int val = (((float)sin(millis() / 900.0 + i / 8.0 * 2.0 * PI) + 1) * 2048);
  
      val = map(val,0,500, 0, 300);   //max min 
      val = map(val,501,4096, 301, 4096);
      pwm.setPWM(i, 0, val);
      Serial.println(val);
    }
#ifdef ESP8266
    yield();  // take a breather, required for ESP8266
#endif
  }

#10

So what is the output from this code and why did you use x,y,z?


#11

The output is: to make a lightwave over the 16 outputs of the pca9685 pwm module with LEDs.
The parameters x,y,z should be used to modify the wave while the leds are on.


#12

Are you using this code? https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/blob/master/examples/pwmtest/pwmtest.ino


#13

yes, this is the demo-code i used!


#14

from where you got this code? and is it working as you want?


#15

this formula works very well for my purposes. I got it from this site https://timodenk.com/blog/shiftregister-pwm-library/ and optimized it for my Wave


#16

i optimized the program as it should work like:
the only problem is, that the wave values change only every second (a loop pass of 16 times) and not continuously.

here is the code->

#define CAYENNE_PRINT Serial
#include <CayenneMQTTESP8266.h>
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

char ssid[] = 
char wifiPassword[] =
char username[] = 
char password[] =
char clientID[] = 

#define VIRTUAL_CHANNEL 1
#define VIRTUAL_CHANNELx 2
#define VIRTUAL_CHANNELy 3
#define VIRTUAL_CHANNELz 4

int switch1=0;
float x=8.0;
float y=900.0;
int z=2048;

void setup() {
  Serial.begin(115200);
  Cayenne.begin(username, password, clientID, ssid, wifiPassword);
  Serial.println("16 channel PWM test!");
  pwm.begin();
  pwm.setPWMFreq(1600);  // This is the maximum PWM frequency
  Wire.setClock(400000);
}

void loop() {
  Cayenne.loop();

  if (switch1==1){
    light();
  }
    else {
      stopp();
    }  
}


void light(){
for (uint8_t i = 0; i < 16; i++) {
    int val = (((float)sin(millis() / y + i / x * 2.0 * PI) + 1) * z);
  
      val = map(val,0,500, 0, 300);   //max min Wert festlegen
      val = map(val,501,4096, 301, 4096);
      pwm.setPWM(i, 0, val);
      Serial.println(val);    
    }
    
#ifdef ESP8266
    yield();  // take a breather, required for ESP8266
#endif
}

void stopp(){
Serial.println("Stop");
  for (uint8_t i = 0; i < 16; i++) { 
    pwm.setPWM(i, 0, LOW);
  }
}

CAYENNE_IN(VIRTUAL_CHANNEL)
{
 switch1 = getValue.asInt();
}

CAYENNE_IN(VIRTUAL_CHANNELx)
{
   x=getValue.asDouble();
}

CAYENNE_IN(VIRTUAL_CHANNELy)
{
    y=getValue.asDouble();
}

CAYENNE_IN(VIRTUAL_CHANNELz)
{
    z = getValue.asInt();
}

#17

That could be due to the default yield time for the Cayenne.loop() function being one second. You could try lowering it by specifying the yield time in milliseconds like this: Cayenne.loop(10).


#18

Yes! Thanx jburhenn !!! now it works like a charm!!!


#19

Great! Glad that’s working now.