i need to use an esp8266 to control multiple dimming circuits that use PWM. For now i will have 1 esp to control 3 different PWM outputs. Each output will have a different duty cycle. i would also like to use the esp as a web server so im worried about blocking code. Is there a better approach to this, like a PWM generator or some sorts that i can wire to the ESP and control the multiple PWM signals from the esp?
Have a look at PCA9685 (servo) breakout boards.
Each board is up to 16 PWM channels, and multiple boards can be daisy-chained.
Uses two ESP pins (I2C).
Currently working on a new version of this board.
ESP8266 underneath, PCA9685 in the middle, Pololu 3.3volt buck next to the cap, and 16 ~10watt CC LED drivers.
It only needs a single 24volt (or 12volt) supply, and the LEDs. Nothing else.
Sketch uses the Brunnels PCA9685 library (Github) for 12-bit dimming in 256 equal (IEC) steps,
with broadcast UDP links (several of those boards) to a master ESP.
Leo..
From what i could gather the ESP can generate more than 1 PWM signal. Would it acceptable to use the ESP8266 to generate 3 different PWM signals with different duty cycles? each signal would be at the same FREQ. The web server wouldn't have to be extremely responsive or anything as i would just be using it to change the PWM values. Also i might just use UDP socket to change PWM signals.
notsolowki:
i would also like to use the esp as a web server so im worried about blocking code. Is there a better approach to this, like a PWM generator or some sorts...
The PCA9685 is a stand-alone PWM chip.
It only needs one command (set and forget) per channel, or groups of channels.
No communication with the ESP after that any more.
I have read that if you let the ESP8266 generate PWM, you could get flicker/drop-outs.
Because the single core of the 8266 has to share resources with WiFi.
An ESP32, with two cores, could be the better option if you don't want to use a separate PWM chip.
Leo..
I used the ESP8266 to generate a single PWM signal and that worked great. I just ordered a bunch of the PCA9685 breakout boards they were pretty cheap and that is the route i will probably ultimately go with. in the mean time i wonder how i could generate the 3 signals with the ESP8266 by itself and test it on my scope to see how it behaves.
Right now this is the simple code i can use to generate the freq and duty cycle i want. Could i just duplicated this code for 2 more lights and test it?
uint8_t Light1 = 5;
uint16_t dutycycle = 666;
//set ESP to 600Hz for performace
void setup() {
Serial.begin(115200);
analogWriteFreq(600);
analogWrite(Light1, 1023);
}
void loop() {
analogWrite(Light1, dutycycle);
}
Just try with one more, before adding the third one.
Note that you can't use all ESP pins, because of double functions.
Did you also try the single channel with the board connected to WiFi.
That's where the drop-outs could become visible.
Leo..
Wawa:
Just try with one more, before adding the third one.
Note that you can't use all ESP pins, because of double functions.Did you also try the single channel with the board connected to WiFi.
That's where the drop-outs could become visible.
Leo..
I'm going to give it a go. i will try without WIFI first and then again with UDP socket and WiFI. Thank you!
@Wawa how can i go back to a post i made about dimming a meanwell driver with the ESP8266. i think it was you that told me to use the BC548 transistor but i don't remember how i wired it lol.
This is the test code I currently run on ESP8266 slave #1 (have currently two connected).
The slave can auto-detect another three PCA9685 sub boards, for up to 64 LED channels per ESP8266.
Overkill for just a few channels, but it might give you some ideas about dimming possibilities.
I use request/actual/speed variables to get to the final dim level,
and multiple UDP sentences for single channels or groups of channels.
Each board and sub board also has a temp sensor that the master can query.
The master/controller sketch with web page/dashboard/web-sockets is still 'under construction', but works.
LAN only (no internet dependency), AP fall-back, and auto switch-over between the two.
Leo..
const byte clientNumber = 1; // each client must have a unique 8-bit address, but not 0
#include <PCA9685.h> // https://github.com/brunnels/PCA9685 change Wire/Wire.h to Wire.h in line 12 of PCA9685.h
PCA9685 PCA_0(0x40); // main PCA9685 board
PCA9685 PCA_1(0x41); // optional slaveboard1
PCA9685 PCA_2(0x42); // optional slaveboard2
PCA9685 PCA_3(0x43); // optional slaveboard3
#include <ESP8266WiFi.h>
const char* SSID = "Aurora"; // controller <> clients network name
const char* PASS = "Zealandis"; // password
IPAddress broadcastIP (192, 168, 4, 255);
#include <WiFiUDP.h>
WiFiUDP UDP;
const unsigned int localPort = 8888;
byte channels = 16; // assume main board only, channel 0-15
unsigned long currentMillis, previousMillis[64]; // timing, assume four 16-channel boards
byte requestDim[64], actualDim[64], fadeStepTime[64];
byte data[8], packetSize; // incoming data
byte temp[5]; // clientNumber and 1-4 'twos complement' raw tempC integers, for four boards
void setup() {
Serial.begin(74880); // default boot baud rate for most ESP8266 modules
Serial.printf("\r\nESP-12 chip ID: %d\r\n", ESP.getChipId()); // module data
Wire.begin(2, 14); // default I2C pins
Wire.setClock(400000); // high speed I2C
PCA_0.begin(); // PCA9685 on main board
PCA_0.setFrequency(800); // default PWM frequency is 200, max is 1600
Wire.beginTransmission(0x41); // auto detect slaveboard1
if (!Wire.endTransmission()) { // if there
channels = 32; // update number of PWM channels
PCA_1.begin(); // PCA9685 on slaveboard1
PCA_1.setFrequency(800); // each 16-channel board can be set to a different PWM frequency
}
Wire.beginTransmission(0x42); // auto detect slaveboard2
if (!Wire.endTransmission()) {
channels = 48;
PCA_2.begin();
PCA_2.setFrequency(800);
}
Wire.beginTransmission(0x43); // auto detect slaveboard3
if (!Wire.endTransmission()) {
channels = 64;
PCA_3.begin();
PCA_3.setFrequency(800);
}
for (byte i = 0; i < channels; i++) { // make sure all lights are off on reboot
requestDim[i] = 0; // reset
actualDim[i] = 0; // reset
fadeStepTime[i] = 1; // default fast fade speed
switch (i >> 4) { // channels to board(s)
case 0: PCA_0.setChannel8bit(i, 0); break; // write to master
case 1: PCA_1.setChannel8bit(i - 16, 0); break; // if there, write to slaveboard1
case 2: PCA_2.setChannel8bit(i - 32, 0); break; // if there, write to slaveboard2
case 3: PCA_3.setChannel8bit(i - 48, 0); break; // if there, write to slaveboard3
}
}
WiFi.mode(WIFI_STA); // station only, hopefully one day with automatic DTIM modemsleep
WiFi.begin(SSID, PASS); // controller credentials
UDP.begin(localPort);
Serial.printf("Client number: %d UDP port %d\r\nGo...", clientNumber, localPort);
}
void loop() {
currentMillis = millis();
fade(); // execute fade function
packetSize = UDP.parsePacket();
if (packetSize) { // if there is UDP data
UDP.read(data, 6); // max size <= defined array
switch (packetSize) { // number of received bytes
case 2: presets(); break; // client/all, preset
case 3: all(); break; // client/all, value, speed
case 4: single(); break; // client, channel, value, speed
case 5: multiDim(); break; // client, startchannel, endchannel, value, speed
case 6: stepDim(); break; // client/all, startchannel, endchannel, startvalue, endvalue, speed
}
}
}
void fade() {
for (byte i = 0; i < channels; i++) { // step through all available channels
if (actualDim[i] != requestDim[i] && currentMillis - previousMillis[i] > sq(fadeStepTime[i])) { // if different, and time to fade
if (actualDim[i] < requestDim[i]) actualDim[i]++; // if too low
else actualDim[i]--; // if too high
switch (i >> 4) { // channels to board(s), fall-through mode
case 0: PCA_0.setChannel8bit(i, actualDim[i]); // write the value of actuallDim 0-15 to master
case 1: PCA_1.setChannel8bit(i - 16, actualDim[i]); // write the value of actuallDim 16-31 to slaveboard1
case 2: PCA_2.setChannel8bit(i - 32, actualDim[i]);
case 3: PCA_3.setChannel8bit(i - 48, actualDim[i]);
}
previousMillis[i] = currentMillis; // update
if (actualDim[i] == requestDim[i] && fadeStepTime[i] != 1) fadeStepTime[i] = 1; // reset dim speed when fading is done
}
}
}
void presets() { // client/all, preset
if (data[0] == clientNumber) { // presets for this client
if (data[1] == 0) { // controller requests temps
temp[0] = clientNumber; // first byte
for (byte i = 0; i < channels >> 4; i++) { // boards
switch (i) { // fall-through mode
case 0: Wire.requestFrom(0x48, 1), temp[1] = Wire.read();
case 1: Wire.requestFrom(0x49, 1), temp[2] = Wire.read();
case 2: Wire.requestFrom(0x4A, 1), temp[3] = Wire.read();
case 3: Wire.requestFrom(0x4B, 1), temp[4] = Wire.read();
}
}
UDP.beginPacket(broadcastIP, localPort);
UDP.write(temp, 1 + (channels >> 4)); // client + boards
UDP.endPacket();
}
//if (data[1] == 1); // other preset(s) for this client
}
if (data[0] == 0) { // presets for all clients
//if (data[1] == 1); // other preset(s) for all
if (data[1] == 13) ESP.restart(); // remote reboot by controller
}
}
void all() { // client/all, value, speed
if (data[0] == clientNumber || data[0] == 0) { // this client, or all clients
for (byte i = 0; i < channels; i++) { // set connected channels to
requestDim[i] = data[1]; // value
fadeStepTime[i] = data[2]; // speed
}
}
}
void single() { // client, channel, value, speed
if (data[0] == clientNumber && data[1] < channels) { // this client, and in range
requestDim[data[1]] = data[2]; // value
fadeStepTime[data[1]] = data[3]; // speed
}
}
void multiDim() { // client, startchannel, endchannel, value, speed
if (data[0] == clientNumber && data[1] <= data[2] && data[2] < channels) { // this client, same or in increasing order, and in range
for (byte i = data[1]; i <= data[2]; i++) { // set connected channels to
requestDim[i] = data[3]; // value
fadeStepTime[i] = data[4]; // speed
}
}
}
void stepDim() { // client/all, startchannel, endchannel, startvalue, endvalue, speed
if (data[0] == clientNumber || data[0] == 0 && data[1] <= data[2]) { // this client, or all clients, and same or increasing order
for (byte i = data[1]; i <= data[2]; i++) { // set selected channel(s) to
if (actualDim[i] < data[3] && data[3] < data[4] || actualDim[i] > data[3] && data[3] > data[4]) actualDim[i] = data[3]; // test if step is allowed
requestDim[i] = data[4]; // endvalue
fadeStepTime[i] = data[5]; // speed
}
}
}
Wow thankyou that should really help me out with all of this. also i found that post where you told me how to wire the transistor and i see again that you even mentioned the PCA9685 back then. thanks a lot
notsolowki:
@Wawa how can i go back to a post i made about dimming a meanwell driver with the ESP8266. i think it was you that told me to use the BC548 transistor but i don't remember how i wired it lol.
Yep, guilty as charged.
Click on the picture below your forum name, and select "show Posts".
I do recommend opto couplers with mains-powered Meanwell LED drivers (safety),
but a BC548 or similar, with 10k base current limiter, will also work.
Leo..
test 1, i have set the 3 outputs without wifi code. i set the duty cycle as follows,
output1=30%@600hz
output2=60%@600hz
output3=90%@600hz
The scope seems to be showing rather stable readings with only the PWM generating code. Now i need to test with sockets and WIFI. I might not be able to get around to it tonight. I know the WIFI code is blocking, i can deal with a dropout every know and then when i contact the socket server. However i would not be able to deal with flickering. I'm curious to see how the WIFI code interferes with the PWM code
Can anyone tell me how many PWM signals the esp8266 can generate at the same freq but different duty cycles at the same time? from what i can tell the esp8266 has 11 PWM capable pins on a nodemcu to generate PWM signals but can i use all 9 simultaneously at different duty cycles? they would all be running the same freq i don't know if that matters or not?
My second attempt to use multiple '3' PWM outputs to generate a signal with WIFI and UDP were successful
As long as you can access the PCA9685 address pins, and set them High or Low, you can have 62 of them on per bus 62 * 16... Thats 992 PWM. Thats alot of power draw. All the PCA9685 boards I have seen, do not have an option to alter addresses of the modules without cutting traces or desoldering and pulling up address pins to set them to specific addresses. It works, but all the wirewrap looks messy.
I hope any new boards made are fully addressable!
japreja:
As long as you can access the PCA9685 address pins, and set them High or Low, you can have 62 of them on per bus 62 * 16... Thats 992 PWM. Thats alot of power draw. All the PCA9685 boards I have seen, do not have an option to alter addresses of the modules without cutting traces or desoldering and pulling up address pins to set them to specific addresses. It works, but all the wirewrap looks messy.I hope any new boards made are fully addressable!
but how many pwm signals at different duty cycles can a nodemcu output at the same time without the PCA9685? 8? 9?
Well i tried 6 pwm signals with udp and wifi and it worked flawlessly. I incremented the pwm value through udp from my udp client in python, nothing like 4kw worth of LEDS strobe in your face lol. i also tried different duty cycles for all of them and i had no problems. i will still have to use the PCA9685 because i need to control 13 PWM signals but yea, successful
japreja:
As long as you can access the PCA9685 address pins, and set them High or Low, you can have 62 of them on per bus 62 * 16... Thats 992 PWM. Thats alot of power draw. All the PCA9685 boards I have seen, do not have an option to alter addresses of the modules without cutting traces or desoldering and pulling up address pins to set them to specific addresses. It works, but all the wirewrap looks messy.I hope any new boards made are fully addressable!
That's in theory of course.
Many on a bus, with the needed wires, could increase bus capacitance too much.
More of a problem if you use 400kHz I2C.
I just saw stackable PCA9685 boards for the WeMosD1 mini (same size) online.
That could be an easy option.
@notsolowki
Well done, but how low did/could you dim down, and at what PWM frequency.
The code I gave you has a dim ratio of 1:2000 (the IEC equal brightness per step table).
Seems not possible with mains powered MeanWell dimmers, but I don't know your application.
4Kw of LEDs is a lot.
Leo..
Wawa:
That's in theory of course.
Many on a bus, with the needed wires, could increase bus capacitance too much.
More of a problem if you use 400kHz I2C.I just saw stackable PCA9685 boards for the WeMosD1 mini (same size) online.
That could be an easy option.@notsolowki
Well done, but how low did/could you dim down, and at what PWM frequency.
The code I gave you has a dim ratio of 1:2000 (the IEC equal brightness per step table).
Seems not possible with mains powered MeanWell dimmers, but I don't know your application.
4Kw of LEDs is a lot.
Leo..
I have the esp running 9 PWM signals to 9 different MeanWell drivers that run 640watts of LED each. it seems the driver has some smoothing of its own. They each come with a 10k POT for dimming and i can dim them just as much as the POT did. fully on and fully off. i dont notice any flickering. and the udp server dont do much blocking because it only blocks when i send a command to set PWM. I'm still going to use the PCA9685 boards when they show up because 9 PWM is not enough.
also i was not sure how to use the optocoupler in the circuit so at random i chose to use 10kohm reisitors at the GPIO pins and a 10k between the ground of the PWM and the esp8266.
notsolowki:
also i was not sure how to use the optocoupler in the circuit
The Arduino side of the opto is just a LED. It only needs a (1k) resistor in series, to limit LED current.
Leo..