//****************************************************
// Android/fruitPhone Controlled WiFi Car
//****************************************************
// ESP 8266 NodeMcu V1.0 PWM Servo/ LM298 H-bridge Control Via WiFi UDP
//*************************************************
//*************************************************
// TEST SKETCH
// USING LM298's ONLY ONE CHANNEL
//*************************************************
//*************************************************
//****************************************************
// Using Free Android or iOS App "RoboRemo"
//****************************************************
//***************************************************************
// PLEASE VISIT
//****************************************************************
// My Blog : https://amkDiyProjects.BlogSpot.com
// My youtube Channel: https://youtube.com/CrazyGuyOfficial
//****************************************************************
//************************************************************
// Libraries Required for this to work
//************************************************************
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <Servo.h>
//************************************************************
// Settings for the WIFI:
//************************************************************
const char* ssid = "myWifi"; // Set WiFi Name of WiFi Router/AP/HotSpot AND Esp8266 will connect TO it
const char* password = "12345678"; // Set PassWord For WiFi
unsigned int localPort = 9876; // Set port number
//************************************************************
// Settings for Servo using Servo library
//************************************************************
const int chCount = 3;
Servo servoCh[chCount];
int chPin[] = {5, 4, 14};
int chVal[] = {1600, 1600, 1600}; // default value in microSeconds for PWM signals (middle)
int usMin = 700; // min pulse micro seconds
int usMax = 2600; // max pulse micro seconds
//***************************************************************
// LM298 H-bridge Motor Driver Pins and Settings for one channel
//***************************************************************
int enA = 12 ; // Pwm signal Pin used to control Motor Speed (Lm298)
int IN1 = 13 ; // IN1 IN2 pins on lm298 to control Motor's direction of rotation
int IN2 = 15 ;
int mid = 1024 ; // Default PWM value for enA pin
//***************************************************************
char cmd[40];
// Make a character array/C string to store the Contents Read from the received UDP Packet
// Cmd is the name of the UDP packet Buffer in which the command which is received is stored
unsigned long lastCmdTime = 60000;
unsigned long aliveSentTime = 0;
//***************************************************************
WiFiUDP port;
//************************************************************
// SETUP BEGINS
//************************************************************
void setup() {
pinMode(enA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
digitalWrite(enA, LOW);
digitalWrite(IN1, LOW);
digitalWrite (IN2, LOW);
delay(500);
Serial.begin(115200);
Serial.println("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA); //Set Esp8266 in Station mode
//it can work without this line but necessary for preventing Ap mode (happened in my case )
WiFi.begin(ssid, password); // Connect to WiFi network
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("trying to connect");
delay(500);
}
Serial.println("");
Serial.println("WiFi connected");
port.begin(localPort);
Serial.print("Use this Ip and Port in RoboRemoApp to connect: "); //Print the IP address and port number
Serial.print(WiFi.localIP());
Serial.print(":");
Serial.print(localPort);
}
//*************************************************
// SETUP END
//*************************************************
//************************************************************
// LOOP BEGINS
//************************************************************
void loop() {
//*************************************************
if (millis() - lastCmdTime > 500) {
//*************************************************
analogWrite(enA, 0); // Write 0 or turn off the pwm signal to the lm298's enA pin so motor stops
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
//*************************************************
for (int i = 0; i <= 2; ) {
servoCh[i].detach(); // Turns OFF all the Pins declared in servo library settings above
i++;
}
}
//*************************************************
int packetSize = port.parsePacket(); // Declaring an int for getting Packet Size
if (packetSize > 0) {
port.read(cmd, 40);
//Serial.print("CMD");
//Serial.println(cmd); // Print the recieved cmd
//*************************************************
if (cmd[0] == 'f' && cmd[1] == 'b') { // if the recieved command string contains the channel id "fb" (forward/backwards)
exeFB(); // execute the function "FB" so all the pins required to control lm298 can operate
}
if (cmd[0] == 'c' && cmd[1] == 'h') { // if the recieved command string contains the channel id "ch"
exeServo(); // execute the servo function
//so Pwm signals according to the channel number can be sent to servos
// via the GPIO pins declared above in the servo library settings
}
//*************************************************
if (millis() - aliveSentTime > 1000) {
// an "alive" signal is sent periodically to the phone to know the connection state
// in the RoboRemo app from "edit UI" option add an "led" or a "text log box" set the ID
// to "alive" or can be changed but for the text box if left empty the received text will be shown
exeReply(); // Execute exeReply function At the end of the loop
}
}
}
//*************************************************
// LOOP ENDS
//*************************************************
//************************************************************************************
// FUNCTIONS
//************************************************************************************
void exeFB() {
lastCmdTime = millis();
int fb = cmd[2] - '0' ;
int fbVl = 0;
int i = 4;
while ( isdigit( cmd[i] ) ) { // if the 4th character in the Array is a Number
//get the number and Calculate all the digits in to a single 4 digit number
fbVl = (fbVl * 10) + (cmd[i] - '0');
i++;
}
if (fb == 3 && cmd[3] == ' ' && fbVl > mid) { // forward motor direction
digitalWrite(IN1, HIGH); // set LM298 channel one motor direction
digitalWrite(IN2, LOW);
int FWD = map(fbVl, 1025, 2048, 0, 1024); // map the channel value number to a number between 0-1024
// for writing Pwm to pin enA
Serial.print("FWD :");
Serial.println(FWD);
analogWrite(enA, FWD); // write pwm
}
if (fb == 3 && cmd[3] == ' ' && fbVl < mid) { // backwards motor direction
digitalWrite(IN1, LOW); // set pins for reverse direction
digitalWrite(IN2, HIGH);
int BKW = map(fbVl, 1024, 0, 0, 1024); //map value
Serial.print("BKW :");
Serial.println(BKW);
analogWrite(enA, BKW); //write PWM
}
Serial.print ("fb Number : ");
Serial.println (fb);
Serial.print ("fbVl : ");
Serial.println (fbVl);
}
//******************************************************************************************
void exeServo() { // exeCmd function when called from the loop Reads the channel number and value and sends PWM signals
lastCmdTime = millis();
int ch = cmd[2] - '0' ; // Channel Number
int chVl = 0; // Channel Value temporarily store it in this and then move to Array chVal[] which is already
// declared at the top
int i = 4;
while ( isdigit( cmd[i] ) ) { // if the 4th character in the Array is a Number
//get the number and Calculate all the digits in to a single 4 digit number
chVl = (chVl * 10) + (cmd[i] - '0');
chVal[ch] = chVl;
i++;
}
if (ch >= 0 && ch <= 2 && cmd[3] == ' ') { //if channel value is between 0 and 9 send PWM Signals using Channel Value
if (!servoCh[ch].attached()) {
servoCh[ch].attach(chPin[ch], usMin, usMax);
}
servoCh[ch].writeMicroseconds(chVal[ch]);
}
Serial.print ("channel Number : ");
Serial.println (ch);
Serial.print ("chVl : ");
Serial.println (chVl);
}
//*********************************************************************************************
void exeReply() {
port.beginPacket(port.remoteIP(), localPort); // Send "Alive 1" back to the App to light up the "led" in the App GUI
// Alive is the "id" for "LED" and "1" is the On command /n" will be ignored
port.write("alive 1\n");
port.endPacket();
aliveSentTime = millis();
Serial.println("alive sent");
}