L293D motor shield for NodeMCU ESP-12E. Note green 5V jumper supplying test power to L293D IC. Click on the picture for youtube video. |
L293D motor control shield |
Tony Kambourakis has an excellent writeup on the L293D, so please head over there for details.
While testing the program I set the 5V USB power to drive the L293D by shorting VM to VIN, and connecting VIN to my Android smartphone charger. Try not to use the laptop/desktop USB port- it minimizes the damage should things go wrong. Be sure to remove the Vin-VM short when you progress to 12-36VDC. VIN still needs to be at 5VDC.
I only had to make a slight change to my program: the ULN2003 used different pins for PWM and added 2 GPIO pins for motor direction control. We do not really need to change the fan rotation direction, plus reversing power to LED lights may cause them not to turn on, so the additional GPIO pins are used to lock in the output pin assignments on the shield.
The http interface allows for easy webhooking into Google Home/Google Assistant for Voice Control.
#include <ESP8266WiFi.h>
const char* ssid = "YourAccessPoint";
const char* password = "YourPassword";
IPAddress staticIP(123,45,67,89); // Change IP to suit your AP
IPAddress gateway(123,45,67,1);
IPAddress subnet(255,255,255,0);
WiFiServer server (8080);
uint8_t PWMpin1 = D1;
uint8_t PWMpin2 = D2;
uint8_t Dirpin1 = D3;
uint8_t Dirpin2 = D4;
void setup ()
{
delay (10);
Serial.begin (115200);
analogWrite(PWMpin1, 0); /* set initial duty cycle */
analogWrite(PWMpin2, 0); /* set initial duty cycle */
pinMode(Dirpin1, OUTPUT);
pinMode(Dirpin2, OUTPUT);
digitalWrite(Dirpin1, 1); // 2019-05-15 set output polarities to conform to shield
digitalWrite(Dirpin2, 1);
analogWriteFreq(10); /* default is 1KHz range is 1Hz-1000kHz */
// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_OFF); // wifi reconnect workaround
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
WiFi.config(staticIP, gateway, subnet); // Static IP. Not required for dhcp
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Static IP address: ");
Serial.println(WiFi.localIP());
// Start the server
server.begin();
delay(50);
}
static int dots = 0;
static int val = 0; // 2018-10-25
static int val2 = 0; // 2019-04-19
static int commas = 0;
static unsigned long timeNow = 0; // 2019-03-17
static unsigned long timeLast = 0;
static int seconds = 0;
uint16_t dutycycle = 0; /* Set PWM duty cycle */
void loop() {
// 2019-03-17
timeNow = millis()/1000; // the number of milliseconds that have passed since boot
if (timeLast==0 || timeNow<timeLast)
timeLast = timeNow; // Start with time since bootup. Note millis() number overflows after 50 days
seconds = timeNow - timeLast;//the number of seconds that have passed since the last time 60 seconds was reached.
// 2019-02-24 Check for broken wifi links
if (WiFi.status() != WL_CONNECTED) {
delay(10);
// Connect to WiFi network
Serial.println();
delay(10);
Serial.print("Reconnecting WiFi ");
delay(10);
WiFi.mode(WIFI_OFF); // 2018-10-09 workaround
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
WiFi.config(staticIP, gateway, subnet); // Static IP. Not required for dhcp
while (WiFi.status() != WL_CONNECTED) {
Serial.print("x");
delay(500);
dots++;
if (dots >= 1000)
{
dots = 0;
Serial.println("Giving up! Waiting for WDT reset ...");
delay(20);
while (1)
{}
}
}
Serial.println("");
delay(10);
Serial.println("WiFi reconnected");
// Start the server
server.begin();
Serial.println("Server restarted");
delay(10);
// Print the IP address
Serial.print("IP address: ");
delay(10);
Serial.println(WiFi.localIP());
delay(10);
}
// Check if a client has connected
WiFiClient client = server.available();
if ( ! client ) {
return;
}
//Wait until the client sends some data
while ( ! client.available () )
{
delay (10); // 2019-02-28 reduced from 100
Serial.print(".");
if (commas++ > 5) {
commas = 0;
Serial.println("Terminating client connecting without reading");
delay(20);
client.stop();
return;
}
}
client.setTimeout(3000); // 2019-03-19
Serial.println("new client connect, waiting for data ");
// Read the first line of the request
String req = client.readStringUntil ('\r');
client.flush ();
// Match the request
String s = "";
// Prepare the response
if (req.indexOf ("/pwm1") != -1) // first channel
{
Serial.println("Command for PWM1 received");
int value_index = req.indexOf("/pwm1/");// Here you get the index to split the response string where it says "pwm1"
String value_string = req.substring(value_index);
String value = value_string.substring(6, value_string.indexOf("\r"));// Skip first 6 characters, get value
Serial.println(value);
s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nValue entered ";
s += value;
s += "\n\r";
val = value.toInt();
if (val < 0) // 2019-04-19 value must be 0-100%
val = 0;
else if (val > 100)
val = 100;
s += "Duty cycle (percent) used is: ";
s += String(val);
s += "\n\r</html>\n";
client.print (s);
Serial.print("Duty cycle (percent) used is: ");
Serial.println(val);
dutycycle = (val * 1023) / 100; // Convert to 16-bit unsigned
analogWrite(PWMpin1, dutycycle);
} else if (req.indexOf ("/pwm2") != -1) {
Serial.println("PWM2 command received");
int value_index = req.indexOf("/pwm2/");// Here you get the index to split the response string where it says "pwm1"
String value_string = req.substring(value_index);
String value = value_string.substring(6, value_string.indexOf("\r"));// Skip first 6 characters, get value
Serial.println(value);
s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nValue entered ";
s += value;
s += "\n\r";
val2 = value.toInt();
if (val2 < 0) // 2019-04-19 value must be 0-100%
val2 = 0;
else if (val2 > 100)
val2 = 100;
s += "Duty cycle (percent) used is: ";
s += String(val2);
s += "\n\r</html>\n";
client.print (s);
Serial.print("Duty cycle (percent) used is: ");
Serial.println(val2);
dutycycle = (val2 * 1023) / 100; // Convert to 16-bit unsigned
analogWrite(PWMpin2, dutycycle);
} else if (req.indexOf("/index.html") != -1) {
s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nStatus: PWM1 ";
s += String(val);
s += "% PWM2 ";
s += String(val2);
s += "%\r\n</html>\n";
client.print (s);
Serial.println("Status command received");
}
else {
Serial.println("invalid request");
s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nInvalid request</html>\n";
client.print (s);
}
delay (10);
client.stop(); // 2019-02-24
Serial.println("Client disonnected");
delay(10);
}
And that was it- it worked first time. Having said that do test your system is stages: at first with no power to the L293D while you worked out the bugs in the program, and then with low (5V current-limited) power from the USB power supply. You can check the motor output pins with a voltmeter for polarity before connecting up the fan and LED lamp.
I also use a 60W 230Vac mains isolation transformer for safety - it limits mains power in case of catastrophic failures.
On the smartphone browser, I used:
http://123.45.67.89:8080/pwm1/100
On my Linux desktop I used
lynx -dump -read_timeout=5 -cmd_script=./lynx_script http://123.45.67.89:8080/pwm2/0
Make sure the phone or desktop is logged into the same WiFi access point. Notice the IP address here does not really work- they are not even Class C (ie your AP will not use them) so substitute your own.
There you have it: IoT PWM DC fan and LED lamp controller, all set for Google Home Voice Control. It cost me only RM30 (about USD8) per unit. Making electronics stuff can be cheap, fun and fast.
Happy Trails.
No comments:
Post a Comment