I have quite a few strings of LEDs with clips for photos. Never thought it was much more than a gimmick when i got the first one, but they do make for nice and personal decor with the photos attached.
So naturally the next step is to control them through the home automation system. The first one was easy, there were all LEDs in parallel and an ESP8266 module and a tranzistor was all that it took. But the next 2 gave me a bit more of a headache: the LEDs are connected in antiparallel across the string. This allows for the battery box to add a circuit that creates various blink patterns. This makes it a bit more difficult to control the string, even if all I want is simple dimming of all LEDs. Due to the antiparallel strings of LEDs, current must flow in both directions.
I swept around the parts box and looked for options to drive the string and found a HR8833 motor driver. The motor driver contains H-bridges, one of which is useful to control the current flow in both directions through the LEDs. The integrated driver also makes it easy to interface with the ESP32 which I will use for wireless control. The basic schematic is shown below:
The build looks quite simple: few wires and a small terminal block and hot glue. The 10R 0.25W resistor is what the original control box used, which is supplied at 4.5V. Considering the diode between the 5VUSB and the VCC of the ESP32 module, that works here for a comparable maximum current through the LEDs.
The H bridge truth table explains how the LEDs are driven
From the truth table we can understand that 3 lines in the table are useful: line 2 and 3 are driving the LEDs by making sure the outputs are in opposite states. Line 2 for example will power LEDs number 1, 3, 5 etc in the string and line 3 will power LEDs 2, 4, 6 etc. To turn off the LEDs, we can use either line 1 or 4, as these set the outputs to the same state, thus no current flowing. Here the fast or slow decay makes no difference, since there is no inductive load, the LEDs will pretty much turn off instantly as the bridge switches to this state. So how would the signal look like on the oscilloscope? Here is the figure while driving the H bridge is driving at low output brightness (small duty cycle) and no load, not even the LEDs connected
The Yellow and Blue show the outputs of the H bridge, while the purple trace shows the difference between the two. Thus, for a set PWM, we first have a positive pulse when half the LEDs are driven, then a negative pulse when the other half is driven and then a period when all LEDs are off. When trying to reach a maximum brightness, the waveforms look like below – here the positive pulse takes 50% of the time and the negative pulse the other 50% of the time, with no time for the off state.
To generate such control waveforms for the H-bridge, the EPS8266 cannot be used, unless we want to do this in software. This is possible, but i wanted to use the ESP32 because of its nice MCPWM block. As the name suggests, this block is designed for motor control and can actually generate these pulses if properly configured. Configuration for the block is shown below, using the Arduino IDE and some inspiration from here:
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, ledPin); mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, ledPin2); mcpwm_config_t pwm_config; pwm_config.frequency = 1000; //frequency = 1kHz pwm_config.cmpr_a = 50.0; pwm_config.cmpr_b = 50.0; pwm_config.counter_mode = MCPWM_UP_COUNTER; //Up/down counter pwm_config.duty_mode = MCPWM_DUTY_MODE_0; // Active high mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 0, 10); //10 (=1us) dead time required to avoid a prasitic pulse when the setting is zero. That pulse is enough to keep the LEDs on. mcpwm_sync_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SELECT_SYNC0 , 0); MCPWM0.timer.sync.out_sel = 1; //no fucking clue what this does delayMicroseconds(1000); MCPWM0.timer.sync.out_sel = 0; mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, 0); mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, 0);
Setting the two output PWMs:
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, set_level); //max set level is 50, PWM in complimentary mode mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, 100.0-set_level);
Intuitivelly, the maximum for each of the PWMs is 50%, otherwise we get into overlapping of the 1 period of the outputs which corresponds to the LEDs being off. Naturally, both antiparallel LEDs can be driven independently if some light effects are desired, but I did not care about that. I simply wanted a variable light level string of lights.