From 12a93e24f4d9f89ead7b2237f3e5c287644719ea Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Tue, 12 Mar 2024 23:45:40 +0530 Subject: [PATCH 01/32] add gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4cb6ea --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +src/.* \ No newline at end of file From 753355c2e5da9ea07eab91739f0f30d30ca0d344 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Tue, 12 Mar 2024 23:51:02 +0530 Subject: [PATCH 02/32] change pin names --- src/blite.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blite.cpp b/src/blite.cpp index 8a9d72c..7e82457 100644 --- a/src/blite.cpp +++ b/src/blite.cpp @@ -96,10 +96,10 @@ bool Blite::buttonPressed() { void Blite::setup(){ pinMode(SW1,INPUT_PULLUP); - pinMode(M12_A,OUTPUT); - pinMode(M12_B,OUTPUT); - pinMode(M34_A,OUTPUT); - pinMode(M34_B,OUTPUT); + pinMode(M1,OUTPUT); + pinMode(M2,OUTPUT); + pinMode(M3,OUTPUT); + pinMode(M4,OUTPUT); pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); this->defineM12(true); From 60b3e01f24ac16b440af4f1e781cfdc56f78a419 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Tue, 12 Mar 2024 23:52:27 +0530 Subject: [PATCH 03/32] gitignore add .vscode --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a4cb6ea..d41fe2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -src/.* \ No newline at end of file +src/.* +.vscode/** \ No newline at end of file From f0079896633242840412a2dd06d43ddaa4376a3d Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Wed, 13 Mar 2024 01:14:19 +0530 Subject: [PATCH 04/32] adding server to echo system --- src/blite.cpp | 21 ++++++++++++++++++++- src/blite.h | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/blite.cpp b/src/blite.cpp index 7e82457..2108c8b 100644 --- a/src/blite.cpp +++ b/src/blite.cpp @@ -130,4 +130,23 @@ void Blite::blinkLed(int c){ int Blite::readADC(){ return analogRead(ADC1); -} \ No newline at end of file +} + +void Blite::setupServer(String HTML_CONTENT) { + this->webServer.on("/", HTTP_GET, [&]() { + this->webServer.send(200, "text/html", HTML_CONTENT); + }); + this->webServer.begin(); + this->serverSetupDone = true; +} + +void Blite::renderServer() { + this->webServer.handleClient(); +} + +void Blite::smartRenderServer(String HTML_CONTENT){ + if (!this->serverSetupDone) { + this->setupServer(HTML_CONTENT); + } + this->renderServer(); +} diff --git a/src/blite.h b/src/blite.h index c781a0d..09378ae 100644 --- a/src/blite.h +++ b/src/blite.h @@ -42,8 +42,15 @@ void glowLed(bool s); void blinkLed(int c); int readADC(); +void setupServer(String HTML_CONTENT); +void renderServer(); +void smartRenderServer(String HTML_CONTENT); + private: int m1,m2,m3,m4,speed; +ESP8266WebServer webServer = ESP8266WebServer(80); +bool serverSetupDone = false; + void defineM12(bool polarity){ if (polarity){ this->m1 = M1; From b7940219c4dbabf1a8ad9d84faf4f5cdf1aa0173 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Wed, 13 Mar 2024 01:15:02 +0530 Subject: [PATCH 05/32] modifying 14 code --- examples/14_blite_rc_car/14_blite_rc_car.ino | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index 8c70a84..16f7273 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -2,21 +2,16 @@ #include "remote.h" Blite myBot; -ESP8266WebServer wifiRemoteControl(80); void setup(){ myBot.setup(); Serial.begin(115200); myBot.smartConnectWiFi(); - wifiRemoteControl.on("/", HTTP_GET, []() { - Serial.println("Web Server: received a web page request"); - String html = REMOTE_HTML_CONTENT; - wifiRemoteControl.send(200, "text/html", html); - }); - wifiRemoteControl.begin(); + } void loop(){ - wifiRemoteControl.handleClient(); + String html = REMOTE_HTML_CONTENT; + myBot.smartRenderServer(html); if (myBot.buttonPressed()){ myBot.blinkLed(2); } From 898bea1f7500bcf1a191b2c2ae2c53abad9fac54 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 14 Mar 2024 00:53:17 +0530 Subject: [PATCH 06/32] ota feature --- src/blite.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++- src/blite.h | 6 ++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/blite.cpp b/src/blite.cpp index 2108c8b..a7a52f1 100644 --- a/src/blite.cpp +++ b/src/blite.cpp @@ -104,6 +104,8 @@ void Blite::setup(){ digitalWrite(LED_BUILTIN, HIGH); this->defineM12(true); this->defineM34(true); + String newHostname = "buildybee"; + WiFi.hostname(newHostname.c_str()); WiFi.disconnect(); WiFi.mode(WIFI_OFF); @@ -133,15 +135,17 @@ int Blite::readADC(){ } void Blite::setupServer(String HTML_CONTENT) { - this->webServer.on("/", HTTP_GET, [&]() { + this->webServer.on("/", HTTP_GET, [=]() { this->webServer.send(200, "text/html", HTML_CONTENT); }); this->webServer.begin(); this->serverSetupDone = true; + this->otaSetup(); } void Blite::renderServer() { this->webServer.handleClient(); + this->otaLoop(); } void Blite::smartRenderServer(String HTML_CONTENT){ @@ -150,3 +154,43 @@ void Blite::smartRenderServer(String HTML_CONTENT){ } this->renderServer(); } + +void Blite::otaSetup(){ + + ArduinoOTA.onStart([]() { + String type; + if (ArduinoOTA.getCommand() == U_FLASH) { + type = "sketch"; + } else { // U_FS + type = "filesystem"; + } + + // NOTE: if updating FS this would be the place to unmount FS using FS.end() + Serial.println("Start updating " + type); + }); + ArduinoOTA.onEnd([]() { + Serial.println("\nEnd"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) { + Serial.println("Auth Failed"); + } else if (error == OTA_BEGIN_ERROR) { + Serial.println("Begin Failed"); + } else if (error == OTA_CONNECT_ERROR) { + Serial.println("Connect Failed"); + } else if (error == OTA_RECEIVE_ERROR) { + Serial.println("Receive Failed"); + } else if (error == OTA_END_ERROR) { + Serial.println("End Failed"); + } + }); + ArduinoOTA.begin(); +} + +void Blite::otaLoop(){ + ArduinoOTA.handle(); +} \ No newline at end of file diff --git a/src/blite.h b/src/blite.h index 09378ae..706619a 100644 --- a/src/blite.h +++ b/src/blite.h @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include class Blite { public: @@ -46,6 +49,9 @@ void setupServer(String HTML_CONTENT); void renderServer(); void smartRenderServer(String HTML_CONTENT); +void otaSetup(); +void otaLoop(); + private: int m1,m2,m3,m4,speed; ESP8266WebServer webServer = ESP8266WebServer(80); From 619fc3aa5a1e1508a0b91d5d919987fa14f7aa75 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 14 Mar 2024 10:43:23 +0530 Subject: [PATCH 07/32] add schematics to extra --- extras/schematic.pdf | Bin 0 -> 59568 bytes extras/test.html | 134 ------------------------------------------- 2 files changed, 134 deletions(-) create mode 100644 extras/schematic.pdf delete mode 100644 extras/test.html diff --git a/extras/schematic.pdf b/extras/schematic.pdf new file mode 100644 index 0000000000000000000000000000000000000000..29fa39d4884381217e6526d36b40ca2eaa46c4ea GIT binary patch literal 59568 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

xEMdO5rR)j1^JqMP8WxGw1Cx`LgvuS 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 ixo9!Un*Xh(r - - -Blite RC car control - - - - - -

Blite - RC car control

-
-
-
-
-
-
-
-
-
- - - \ No newline at end of file From 64884b76841029ee438f08bddace173d2188c21b Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 14 Mar 2024 10:43:51 +0530 Subject: [PATCH 08/32] ota implementation and stop motor func --- src/blite.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/blite.cpp b/src/blite.cpp index a7a52f1..c1a1b01 100644 --- a/src/blite.cpp +++ b/src/blite.cpp @@ -100,24 +100,14 @@ void Blite::setup(){ pinMode(M2,OUTPUT); pinMode(M3,OUTPUT); pinMode(M4,OUTPUT); - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, HIGH); + this->stopMotor(); this->defineM12(true); this->defineM34(true); String newHostname = "buildybee"; WiFi.hostname(newHostname.c_str()); WiFi.disconnect(); WiFi.mode(WIFI_OFF); - - -} - -void Blite::glowLed(bool s){ - if (s){ - digitalWrite(LED_BUILTIN, LOW); - } else{ - digitalWrite(LED_BUILTIN, HIGH); - } + this->otaSetup(); } @@ -140,7 +130,6 @@ void Blite::setupServer(String HTML_CONTENT) { }); this->webServer.begin(); this->serverSetupDone = true; - this->otaSetup(); } void Blite::renderServer() { @@ -193,4 +182,12 @@ void Blite::otaSetup(){ void Blite::otaLoop(){ ArduinoOTA.handle(); +} + +void Blite::stopMotor(){ + digitalWrite(M1,LOW); + digitalWrite(M2,LOW); + digitalWrite(M3,LOW); + digitalWrite(M4,LOW); + } \ No newline at end of file From 792345c6db2416717a191c3a88d018ecc9340733 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 14 Mar 2024 10:47:03 +0530 Subject: [PATCH 09/32] stop motor func --- src/blite.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blite.h b/src/blite.h index 706619a..c54db8f 100644 --- a/src/blite.h +++ b/src/blite.h @@ -34,6 +34,7 @@ void moveForward(); void moveBackward(); void turnRight(); void turnLeft(); +void stopMotor(); void setSpeed(int speed); void reversePolarityM12(); @@ -41,7 +42,6 @@ void reversePolarityM34(); int getIO(const char * io); bool buttonPressed(); -void glowLed(bool s); void blinkLed(int c); int readADC(); From 6ff03b4d90a36f6b43cbff05a7be95389efdb2a7 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 14 Mar 2024 10:49:29 +0530 Subject: [PATCH 10/32] examples 14-updated --- examples/14_blite_rc_car/14_blite_rc_car.ino | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index 16f7273..d85a36f 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -12,8 +12,4 @@ void setup(){ void loop(){ String html = REMOTE_HTML_CONTENT; myBot.smartRenderServer(html); - if (myBot.buttonPressed()){ - myBot.blinkLed(2); - } } - From 2283b12d179084f7ce0b9a58acbb192863418d11 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 14 Mar 2024 11:39:18 +0530 Subject: [PATCH 11/32] motor action corrections --- src/blite.cpp | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/blite.cpp b/src/blite.cpp index c1a1b01..9ee8188 100644 --- a/src/blite.cpp +++ b/src/blite.cpp @@ -20,28 +20,28 @@ void Blite::reversePolarityM34(){ this->defineM34(false); } void Blite::moveForward(){ - analogWrite(m1,speed); - digitalWrite(m2,LOW); - analogWrite(m4,speed); - digitalWrite(m3,LOW); + analogWrite(this->m1,this->speed); + digitalWrite(this->m2,LOW); + analogWrite(this->m4,this->speed); + digitalWrite(this->m3,LOW); } void Blite::moveBackward(){ - analogWrite(m2,speed); - digitalWrite(m1,LOW); - analogWrite(m3,speed); - digitalWrite(m4,LOW); + analogWrite(this->m2,this->speed); + digitalWrite(this->m1,LOW); + analogWrite(this->m3,this->speed); + digitalWrite(this->m4,LOW); } void Blite::turnRight(){ - digitalWrite(m1,LOW); - digitalWrite(m2,LOW); - analogWrite(m4,speed); - digitalWrite(m3,LOW); + digitalWrite(this->m1,LOW); + digitalWrite(this->m2,LOW); + analogWrite(this->m4,this->speed); + digitalWrite(this->m3,LOW); } void Blite::turnLeft(){ - analogWrite(m1,speed); - digitalWrite(m2,LOW); - digitalWrite(m3,LOW); - digitalWrite(m4,LOW); + analogWrite(this->m1,this->speed); + digitalWrite(this->m2,LOW); + digitalWrite(this->m3,LOW); + digitalWrite(this->m4,LOW); } void Blite::setSpeed(int s){ this->speed = s ; @@ -103,6 +103,7 @@ void Blite::setup(){ this->stopMotor(); this->defineM12(true); this->defineM34(true); + this->speed = 100; String newHostname = "buildybee"; WiFi.hostname(newHostname.c_str()); WiFi.disconnect(); From 36530758c4b50815bb3a0b91c185d15f0bde93c0 Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 14 Mar 2024 11:41:16 +0530 Subject: [PATCH 12/32] example-14 websocket and dependencies --- examples/14_blite_rc_car/14_blite_rc_car.ino | 58 ++++++++++++++++++++ library.properties | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index d85a36f..455c448 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -1,15 +1,73 @@ #include #include "remote.h" +#include + +#define CMD_STOP 0 +#define CMD_FORWARD 1 +#define CMD_BACKWARD 2 +#define CMD_LEFT 4 +#define CMD_RIGHT 8 Blite myBot; +WebSocketsServer webSocket = WebSocketsServer(81); void setup(){ myBot.setup(); Serial.begin(115200); myBot.smartConnectWiFi(); + webSocket.begin(); + webSocket.onEvent(webSocketEvent); } void loop(){ String html = REMOTE_HTML_CONTENT; myBot.smartRenderServer(html); + webSocket.loop(); } + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { + switch (type) { + case WStype_DISCONNECTED: + Serial.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]); + } + break; + case WStype_TEXT: + //Serial.printf("[%u] Received text: %s\n", num, payload); + String angle = String((char*)payload); + int command = angle.toInt(); + Serial.print("command: "); + Serial.println(command); + + switch (command) { + case CMD_STOP: + Serial.println("Stop"); + myBot.stopMotor(); + break; + case CMD_FORWARD: + Serial.println("Move Forward"); + myBot.moveForward(); + break; + case CMD_BACKWARD: + Serial.println("Move Backward"); + myBot.moveBackward(); + break; + case CMD_LEFT: + Serial.println("Turn Left"); + myBot.turnLeft(); + break; + case CMD_RIGHT: + Serial.println("Turn Right"); + myBot.turnRight(); + break; + default: + Serial.println("Unknown command"); + } + + break; + } +} \ No newline at end of file diff --git a/library.properties b/library.properties index ae611e8..45022a0 100644 --- a/library.properties +++ b/library.properties @@ -6,4 +6,4 @@ sentence=Dev kit for buildybee blite breakout board paragraph=Develop easily with buildybee devikits url=https://github.com/buildybee/blite.git architectures=esp8266 -depends=WiFiManager \ No newline at end of file +depends=WiFiManager,WebSocketsServer \ No newline at end of file From 1dc5716b688a9cc30219ff973c0bdc41ed5d8ab8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 14 Mar 2024 21:46:32 +0530 Subject: [PATCH 13/32] commited by Debasish Goswami --- examples/14_blite_rc_car/14_blite_rc_car.ino | 88 +++++++++++- examples/14_blite_rc_car/blite.cpp | 133 +++++++++++++++++++ examples/14_blite_rc_car/blite.h | 67 ++++++++++ examples/14_blite_rc_car/remote.h | 83 +++++++++++- 4 files changed, 363 insertions(+), 8 deletions(-) create mode 100644 examples/14_blite_rc_car/blite.cpp create mode 100644 examples/14_blite_rc_car/blite.h diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index 455c448..a234f8e 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -1,4 +1,6 @@ -#include +#include "blite.h" +#include +#include #include "remote.h" #include @@ -10,9 +12,77 @@ Blite myBot; WebSocketsServer webSocket = WebSocketsServer(81); +#define CMD_BACKWARD 2 +#define CMD_STOP 0 +#define CMD_FORWARD 1 +#define CMD_LEFT 4 +#define CMD_RIGHT 8 +#define CMD_PUSH 5 +#define CMD_SRVCLCK 6 +#define CMD_SRVACLCK 9 + + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { + switch (type) { + case WStype_DISCONNECTED: + Serial.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]); + } + break; + case WStype_TEXT: + //Serial.printf("[%u] Received text: %s\n", num, payload); + String angle = String((char*)payload); + int command = angle.toInt(); + Serial.print("command: "); + Serial.println(command); + + switch (command) { + case CMD_STOP: + Serial.println("Stop"); + // mybot.stop(); + break; + case CMD_FORWARD: + Serial.println("Move Forward"); + mybot.moveForward(); + break; + case CMD_BACKWARD: + Serial.println("Move Backward"); + mybot.moveBackward(); + break; + case CMD_LEFT: + Serial.println("Turn Left"); + mybot.turnLeft(); + break; + case CMD_RIGHT: + Serial.println("Turn Right"); + mybot.turnRight(); + break; + case CMD_PUSH: + Serial.println("Push button pressed"); + //mybot.push(); + break; + case CMD_SRVCLCK: + Serial.println("Turn servo clockwise"); + //mybot.turnServoClockwise(); + break; + case CMD_SRVACLCK: + Serial.println("Turn servo anti-clockwise"); + //mybot.turnServoAntiClockwise(); + break; + default: + Serial.println("Unknown command"); + } + + break; + } +} + void setup(){ - myBot.setup(); Serial.begin(115200); myBot.smartConnectWiFi(); webSocket.begin(); @@ -70,4 +140,16 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) break; } -} \ No newline at end of file +} + Serial.println("Web Server: received a web page request"); + wifiRemoteControl.on("/", HTTP_GET, []() { + wifiRemoteControl.on("/", HTTP_GET, []() { + wifiRemoteControl.on("/", HTTP_GET, []() { + wifiRemoteControl.send(200, "text/html", html); + }); + wifiRemoteControl.begin(); +} +void loop(){ + wifiRemoteControl.handleClient(); + webSocket.loop(); +} diff --git a/examples/14_blite_rc_car/blite.cpp b/examples/14_blite_rc_car/blite.cpp new file mode 100644 index 0000000..0a9bb70 --- /dev/null +++ b/examples/14_blite_rc_car/blite.cpp @@ -0,0 +1,133 @@ +#include "blite.h" + +int Blite::getIO(const char * io){ + if (io == "io1") { + return IO1; + } else if (io =="io2") { + return IO2; + } else if (io == "scl") { + return I2C_SCL; + } else if (io == "sda"){ + return I2C_SDA; + } + return -1; + +} +void Blite::reversePolarityM12(){ + this->defineM12(false); +} +void Blite::reversePolarityM34(){ + this->defineM34(false); +} +void Blite::moveForward(){ + analogWrite(m1,speed); + digitalWrite(m2,LOW); + analogWrite(m4,speed); + digitalWrite(m3,LOW); +} +void Blite::moveBackward(){ + analogWrite(m2,speed); + digitalWrite(m1,LOW); + analogWrite(m3,speed); + digitalWrite(m4,LOW); +} +void Blite::turnRight(){ + digitalWrite(m1,LOW); + digitalWrite(m2,LOW); + analogWrite(m4,speed); + digitalWrite(m3,LOW); +} +void Blite::turnLeft(){ + analogWrite(m1,speed); + digitalWrite(m2,LOW); + digitalWrite(m3,LOW); + digitalWrite(m4,LOW); +} +void Blite::setSpeed(int s){ + this->speed = s ; +} +bool Blite::connectWiFi(const char *username, const char *password){ + WiFi.disconnect(); + WiFi.mode(WIFI_STA); + int retry = 0; + while (retry <= 20) { + if (WiFi.status() != WL_CONNECTED) { + Serial.println("Trying to connect to wifi"); + WiFi.begin(username,password); + WiFi.waitForConnectResult(); + delay(1000); + } else { + Serial.println("connected to wifi"); + Serial.println(WiFi.localIP()); + return 1; + } + retry++; + } + return 0; +} +bool Blite::smartConnectWiFi(){ + WiFi.disconnect(); + WiFi.mode(WIFI_STA); + WiFiManager wm; + bool res; + res = wm.autoConnect("Buildybee-smart-config","buildybee"); // password protected ap + return res; +} + +bool Blite::APServer() { + if (WiFi.isConnected()){ + if (WiFi.disconnect()){ + return 0; + } + } + const char* ssid = "buidybee_rc_car"; + IPAddress local_IP(192, 168, 4, 1); + // We set a Gateway IP address + IPAddress gateway(192, 168, 4, 1); + IPAddress subnet(255, 255, 255, 0); + // Connecting WiFi + WiFi.softAPConfig(local_IP,gateway,subnet); + WiFi.mode(WIFI_AP); + return WiFi.softAP(ssid); +} +bool Blite::buttonPressed() { + return !digitalRead(SW1); +} + +void Blite::setup(){ + pinMode(SW1,INPUT_PULLUP); + pinMode(M1,OUTPUT); + pinMode(M2,OUTPUT); + pinMode(M3,OUTPUT); + pinMode(M4,OUTPUT); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + this->defineM12(true); + this->defineM34(true); + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); + + +} + +void Blite::glowLed(bool s){ + if (s){ + digitalWrite(LED_BUILTIN, LOW); + } else{ + digitalWrite(LED_BUILTIN, HIGH); + } + +} + +void Blite::blinkLed(int c){ + for (int i=0;i +#include +#include + +class Blite { +public: +void setup(); +bool APServer(); +bool connectWiFi(const char * username, const char * password); +bool smartConnectWiFi(); +void moveForward(); +void moveBackward(); +void turnRight(); +void turnLeft(); +void setSpeed(int speed); + +void reversePolarityM12(); +void reversePolarityM34(); + +int getIO(const char * io); +bool buttonPressed(); +void glowLed(bool s); +void blinkLed(int c); +int readADC(); + +private: +int m1,m2,m3,m4,speed; +void defineM12(bool polarity){ + if (polarity){ + this->m1 = M1; + this->m2 = M2; + } else { + this->m2 = M1; + this->m1 = M2; + } +}; +void defineM34(bool polarity){ + if (polarity){ + this->m3 = M3; + this->m4 = M4; + } else { + this->m4 = M3; + this->m3 = M4; + } +}; +}; +#endif +#endif \ No newline at end of file diff --git a/examples/14_blite_rc_car/remote.h b/examples/14_blite_rc_car/remote.h index 149a560..e4a6fa7 100644 --- a/examples/14_blite_rc_car/remote.h +++ b/examples/14_blite_rc_car/remote.h @@ -11,15 +11,28 @@ button { text-align: center; font-size: 24px;} #container { margin-right: auto; margin-left: auto; - width: 400px; + width: 400px; height: 400px; position: relative; margin-bottom: 10px; } +#container2 { + margin-right: 400px; + margin-left: 0px; + width: 100px; + height: 100px; + position: relative; + margin-bottom: 10px; +} + div[class^='button'] { position: absolute; } .button_up, .button_down { width:214px; height:104px;} .button_left, .button_right { width:104px; height:214px;} .button_stop { width:178px; height:178px;} +.button_push { width:80px; height:40px;} +.button_servo_c { width:122px; height:32px;} +.button_servo_a { width:160px; height:35px;} +.button_servo { width:82px; height:23px;} .button_up { background: url('https://newbiely.com/images/tutorial/up_inactive.png') no-repeat; background-size: contain; @@ -59,6 +72,41 @@ div[class^='button'] { position: absolute; } top: 200px; transform: translate(-50%, -50%); } + +.button_push { + background: none no-repeat; + background-size: contain; + left:-62px; + top: 200px; + transform: translate(-50%, -50%); +} + +.button_servo_c { + background: none no-repeat; + background-size: contain; + right:-214px; + top: 230px; + transform: translate(-50%, -50%); +} + +.button_servo_a { + background: none no-repeat; + background-size: contain; + right:-270px; + top: 170px; + transform: translate(-50%, -50%); +} + +.button_servo { + background: none no-repeat; + background-size: contain; + right:-150px; + top: 200px; + transform: translate(-50%, -50%); +} + + + - - -

ESP8266 - RC Car via Web

-
-
-
-
-
-
-
-

-WebSocket : closed
-

- -
-
- - - -)====="; \ No newline at end of file From da3c9c202c9d52cd0b5f278c25bf9f232585133b Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 18 Mar 2024 21:08:01 +0530 Subject: [PATCH 20/32] updated the html file with more buttons --- blite | 1 + 1 file changed, 1 insertion(+) create mode 160000 blite diff --git a/blite b/blite new file mode 160000 index 0000000..8b27afd --- /dev/null +++ b/blite @@ -0,0 +1 @@ +Subproject commit 8b27afd245858a26cc0c139a7b03fc9ecd59eca6 From 130fbd25a5682180b15e5b34be6e75429c1b3b7a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 14 Mar 2024 21:46:32 +0530 Subject: [PATCH 21/32] commited by Debasish Goswami --- examples/14_blite_rc_car/14_blite_rc_car.ino | 88 +++++++++++- examples/14_blite_rc_car/blite.cpp | 133 +++++++++++++++++++ examples/14_blite_rc_car/blite.h | 67 ++++++++++ examples/14_blite_rc_car/remote.h | 83 +++++++++++- 4 files changed, 363 insertions(+), 8 deletions(-) create mode 100644 examples/14_blite_rc_car/blite.cpp create mode 100644 examples/14_blite_rc_car/blite.h diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index 455c448..a234f8e 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -1,4 +1,6 @@ -#include +#include "blite.h" +#include +#include #include "remote.h" #include @@ -10,9 +12,77 @@ Blite myBot; WebSocketsServer webSocket = WebSocketsServer(81); +#define CMD_BACKWARD 2 +#define CMD_STOP 0 +#define CMD_FORWARD 1 +#define CMD_LEFT 4 +#define CMD_RIGHT 8 +#define CMD_PUSH 5 +#define CMD_SRVCLCK 6 +#define CMD_SRVACLCK 9 + + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { + switch (type) { + case WStype_DISCONNECTED: + Serial.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]); + } + break; + case WStype_TEXT: + //Serial.printf("[%u] Received text: %s\n", num, payload); + String angle = String((char*)payload); + int command = angle.toInt(); + Serial.print("command: "); + Serial.println(command); + + switch (command) { + case CMD_STOP: + Serial.println("Stop"); + // mybot.stop(); + break; + case CMD_FORWARD: + Serial.println("Move Forward"); + mybot.moveForward(); + break; + case CMD_BACKWARD: + Serial.println("Move Backward"); + mybot.moveBackward(); + break; + case CMD_LEFT: + Serial.println("Turn Left"); + mybot.turnLeft(); + break; + case CMD_RIGHT: + Serial.println("Turn Right"); + mybot.turnRight(); + break; + case CMD_PUSH: + Serial.println("Push button pressed"); + //mybot.push(); + break; + case CMD_SRVCLCK: + Serial.println("Turn servo clockwise"); + //mybot.turnServoClockwise(); + break; + case CMD_SRVACLCK: + Serial.println("Turn servo anti-clockwise"); + //mybot.turnServoAntiClockwise(); + break; + default: + Serial.println("Unknown command"); + } + + break; + } +} + void setup(){ - myBot.setup(); Serial.begin(115200); myBot.smartConnectWiFi(); webSocket.begin(); @@ -70,4 +140,16 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) break; } -} \ No newline at end of file +} + Serial.println("Web Server: received a web page request"); + wifiRemoteControl.on("/", HTTP_GET, []() { + wifiRemoteControl.on("/", HTTP_GET, []() { + wifiRemoteControl.on("/", HTTP_GET, []() { + wifiRemoteControl.send(200, "text/html", html); + }); + wifiRemoteControl.begin(); +} +void loop(){ + wifiRemoteControl.handleClient(); + webSocket.loop(); +} diff --git a/examples/14_blite_rc_car/blite.cpp b/examples/14_blite_rc_car/blite.cpp new file mode 100644 index 0000000..0a9bb70 --- /dev/null +++ b/examples/14_blite_rc_car/blite.cpp @@ -0,0 +1,133 @@ +#include "blite.h" + +int Blite::getIO(const char * io){ + if (io == "io1") { + return IO1; + } else if (io =="io2") { + return IO2; + } else if (io == "scl") { + return I2C_SCL; + } else if (io == "sda"){ + return I2C_SDA; + } + return -1; + +} +void Blite::reversePolarityM12(){ + this->defineM12(false); +} +void Blite::reversePolarityM34(){ + this->defineM34(false); +} +void Blite::moveForward(){ + analogWrite(m1,speed); + digitalWrite(m2,LOW); + analogWrite(m4,speed); + digitalWrite(m3,LOW); +} +void Blite::moveBackward(){ + analogWrite(m2,speed); + digitalWrite(m1,LOW); + analogWrite(m3,speed); + digitalWrite(m4,LOW); +} +void Blite::turnRight(){ + digitalWrite(m1,LOW); + digitalWrite(m2,LOW); + analogWrite(m4,speed); + digitalWrite(m3,LOW); +} +void Blite::turnLeft(){ + analogWrite(m1,speed); + digitalWrite(m2,LOW); + digitalWrite(m3,LOW); + digitalWrite(m4,LOW); +} +void Blite::setSpeed(int s){ + this->speed = s ; +} +bool Blite::connectWiFi(const char *username, const char *password){ + WiFi.disconnect(); + WiFi.mode(WIFI_STA); + int retry = 0; + while (retry <= 20) { + if (WiFi.status() != WL_CONNECTED) { + Serial.println("Trying to connect to wifi"); + WiFi.begin(username,password); + WiFi.waitForConnectResult(); + delay(1000); + } else { + Serial.println("connected to wifi"); + Serial.println(WiFi.localIP()); + return 1; + } + retry++; + } + return 0; +} +bool Blite::smartConnectWiFi(){ + WiFi.disconnect(); + WiFi.mode(WIFI_STA); + WiFiManager wm; + bool res; + res = wm.autoConnect("Buildybee-smart-config","buildybee"); // password protected ap + return res; +} + +bool Blite::APServer() { + if (WiFi.isConnected()){ + if (WiFi.disconnect()){ + return 0; + } + } + const char* ssid = "buidybee_rc_car"; + IPAddress local_IP(192, 168, 4, 1); + // We set a Gateway IP address + IPAddress gateway(192, 168, 4, 1); + IPAddress subnet(255, 255, 255, 0); + // Connecting WiFi + WiFi.softAPConfig(local_IP,gateway,subnet); + WiFi.mode(WIFI_AP); + return WiFi.softAP(ssid); +} +bool Blite::buttonPressed() { + return !digitalRead(SW1); +} + +void Blite::setup(){ + pinMode(SW1,INPUT_PULLUP); + pinMode(M1,OUTPUT); + pinMode(M2,OUTPUT); + pinMode(M3,OUTPUT); + pinMode(M4,OUTPUT); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + this->defineM12(true); + this->defineM34(true); + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); + + +} + +void Blite::glowLed(bool s){ + if (s){ + digitalWrite(LED_BUILTIN, LOW); + } else{ + digitalWrite(LED_BUILTIN, HIGH); + } + +} + +void Blite::blinkLed(int c){ + for (int i=0;i +#include +#include + +class Blite { +public: +void setup(); +bool APServer(); +bool connectWiFi(const char * username, const char * password); +bool smartConnectWiFi(); +void moveForward(); +void moveBackward(); +void turnRight(); +void turnLeft(); +void setSpeed(int speed); + +void reversePolarityM12(); +void reversePolarityM34(); + +int getIO(const char * io); +bool buttonPressed(); +void glowLed(bool s); +void blinkLed(int c); +int readADC(); + +private: +int m1,m2,m3,m4,speed; +void defineM12(bool polarity){ + if (polarity){ + this->m1 = M1; + this->m2 = M2; + } else { + this->m2 = M1; + this->m1 = M2; + } +}; +void defineM34(bool polarity){ + if (polarity){ + this->m3 = M3; + this->m4 = M4; + } else { + this->m4 = M3; + this->m3 = M4; + } +}; +}; +#endif +#endif \ No newline at end of file diff --git a/examples/14_blite_rc_car/remote.h b/examples/14_blite_rc_car/remote.h index 149a560..e4a6fa7 100644 --- a/examples/14_blite_rc_car/remote.h +++ b/examples/14_blite_rc_car/remote.h @@ -11,15 +11,28 @@ button { text-align: center; font-size: 24px;} #container { margin-right: auto; margin-left: auto; - width: 400px; + width: 400px; height: 400px; position: relative; margin-bottom: 10px; } +#container2 { + margin-right: 400px; + margin-left: 0px; + width: 100px; + height: 100px; + position: relative; + margin-bottom: 10px; +} + div[class^='button'] { position: absolute; } .button_up, .button_down { width:214px; height:104px;} .button_left, .button_right { width:104px; height:214px;} .button_stop { width:178px; height:178px;} +.button_push { width:80px; height:40px;} +.button_servo_c { width:122px; height:32px;} +.button_servo_a { width:160px; height:35px;} +.button_servo { width:82px; height:23px;} .button_up { background: url('https://newbiely.com/images/tutorial/up_inactive.png') no-repeat; background-size: contain; @@ -59,6 +72,41 @@ div[class^='button'] { position: absolute; } top: 200px; transform: translate(-50%, -50%); } + +.button_push { + background: none no-repeat; + background-size: contain; + left:-62px; + top: 200px; + transform: translate(-50%, -50%); +} + +.button_servo_c { + background: none no-repeat; + background-size: contain; + right:-214px; + top: 230px; + transform: translate(-50%, -50%); +} + +.button_servo_a { + background: none no-repeat; + background-size: contain; + right:-270px; + top: 170px; + transform: translate(-50%, -50%); +} + +.button_servo { + background: none no-repeat; + background-size: contain; + right:-150px; + top: 200px; + transform: translate(-50%, -50%); +} + + + + + +

ESP8266 - RC Car via Web

+
+
+
+
+
+
+
+

+WebSocket : closed
+

+ +
+
+ + + +)====="; \ No newline at end of file From de6b929ad83317b30bef61470fc6e8344ddf7bed Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Mar 2024 17:38:42 +0530 Subject: [PATCH 27/32] without the use of smartRenderServer --- examples/14_blite_rc_car/14_blite_rc_car.ino | 25 ++++++++++++++------ examples/14_blite_rc_car/remote.h | 6 ++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index 9dd9e4f..2ed64ef 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -1,9 +1,8 @@ #include -#include "remote.h" +#include #include +#include "remote.h" -Blite myBot; -WebSocketsServer webSocket = WebSocketsServer(81); #define CMD_BACKWARD 2 #define CMD_STOP 0 #define CMD_FORWARD 1 @@ -14,6 +13,11 @@ WebSocketsServer webSocket = WebSocketsServer(81); #define CMD_SRVACLCK 9 +Blite mybot; +ESP8266WebServer wifiRemoteControl(80); +WebSocketsServer webSocket = WebSocketsServer(81); // WebSocket server on port 81 +int puchButtonCounter = 0; + void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { switch (type) { case WStype_DISCONNECTED: @@ -35,7 +39,7 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) switch (command) { case CMD_STOP: Serial.println("Stop"); - // mybot.stop(); + mybot.stopMotor(); break; case CMD_FORWARD: Serial.println("Move Forward"); @@ -75,14 +79,21 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) void setup(){ + mybot.setup(); Serial.begin(115200); - myBot.smartConnectWiFi(); + mybot.smartConnectWiFi(); webSocket.begin(); webSocket.onEvent(webSocketEvent); + wifiRemoteControl.on("/", HTTP_GET, []() { + Serial.println("Web Server: received a web page request"); + String html = REMOTE_HTML_CONTENT; + wifiRemoteControl.send(200, "text/html", html); + }); + wifiRemoteControl.begin(); } void loop(){ - String html = REMOTE_HTML_CONTENT; - myBot.smartRenderServer(html); + wifiRemoteControl.handleClient(); webSocket.loop(); + } diff --git a/examples/14_blite_rc_car/remote.h b/examples/14_blite_rc_car/remote.h index e4a6fa7..2cae2e8 100644 --- a/examples/14_blite_rc_car/remote.h +++ b/examples/14_blite_rc_car/remote.h @@ -198,15 +198,15 @@ function mouse_up(event) function wc_onclickpush() { - send_command(CMD_PUSH); + send_command(5); } function wc_onclickservclck() { - send_command(CMD_SRVCLCK); + send_command(6); } function wc_onclickservaclck() { - send_command(CMD_SRVACLCK); + send_command(9); } function send_command(cmd) From df9e6ed84c5972214fab771f80caf3f88fcee53d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Mar 2024 18:18:21 +0530 Subject: [PATCH 28/32] without using smartServerRender service --- .../01_wifi_controlled_bot.ino | 178 ++++++++++++++++++ .../02_auto_obstruction_detection/readme.md | 18 ++ examples/05_rgb_control/05_rgb_control.ino | 31 +++ examples/05_rgb_control/blite.h | 24 +++ examples/06_iot_water_pump/readme.md | 18 ++ .../08_distance_measuring.ino | 14 +- examples/08_distance_measuring/blite.h | 28 +++ .../11_line_follower/11_line_follower.ino | 96 +++++++--- .../12_car_control_from_web.ino | 149 +++++++++++++++ .../index.h} | 10 +- examples/14_blite_rc_car/14_blite_rc_car.ino | 65 ------- examples/14_blite_rc_car/remote.h | 12 -- 12 files changed, 526 insertions(+), 117 deletions(-) create mode 100644 examples/01_wifi_controlled_bot/01_wifi_controlled_bot.ino create mode 100644 examples/02_auto_obstruction_detection/readme.md create mode 100644 examples/05_rgb_control/05_rgb_control.ino create mode 100644 examples/05_rgb_control/blite.h create mode 100644 examples/06_iot_water_pump/readme.md create mode 100644 examples/08_distance_measuring/blite.h create mode 100644 examples/12_car_control_from_web/12_car_control_from_web.ino rename examples/{14_blite_rc_car/test.h => 12_car_control_from_web/index.h} (98%) diff --git a/examples/01_wifi_controlled_bot/01_wifi_controlled_bot.ino b/examples/01_wifi_controlled_bot/01_wifi_controlled_bot.ino new file mode 100644 index 0000000..586a15c --- /dev/null +++ b/examples/01_wifi_controlled_bot/01_wifi_controlled_bot.ino @@ -0,0 +1,178 @@ +#include +#include +#include +// Motor pins(2 X 2-pin connector) connected to DRR8833 +#define m1_pin1 0 +#define m1_pin2 2 +#define m2_pin1 14 +#define m2_pin2 15 + +// programmable I/O (2 X 3-pin connector) 5v bi-directional level shifted driving at BAT level +#define io1 12 +#define io2 13 + +// Analog input pin (1 X 3-pin connector) driving at 3.3V +#define analog_sensor A0 + +// I2C/HC-SR04 pin (1X 4-pin connector) driving at BAT level +#define i2c_tx 4 +#define i2c_rx 5 + + +String command; //String to store app command state. +int speedCar = 800; // 400 - 1023. +int speed_Coeff = 3; +int wither = 0; +const int IN_1 = m1_pin2; +const int IN_2 = m1_pin1; +const int IN_3 = m2_pin2; +const int IN_4 = m2_pin1; + +const char* ssid = "buidybee_rc_car"; +ESP8266WebServer server(80); + +void setup() { + pinMode(IN_1, OUTPUT); + pinMode(IN_2, OUTPUT); + pinMode(IN_3, OUTPUT); + pinMode(IN_4, OUTPUT); + + Serial.begin(115200); + // We set a Static IP address + IPAddress local_IP(192, 168, 4, 1); + // We set a Gateway IP address + IPAddress gateway(192, 168, 4, 1); + IPAddress subnet(255, 255, 255, 0); +// Connecting WiFi + WiFi.softAPConfig(local_IP,gateway,subnet); + WiFi.mode(WIFI_AP); + WiFi.softAP(ssid); + + IPAddress myIP = WiFi.softAPIP(); + // should print the same ip set above + Serial.print("AP IP address: "); + Serial.println(myIP); + + // Starting WEB-server + server.on ( "move", HTTP_handleRoot ); + server.onNotFound ( HTTP_handleRoot ); + server.begin(); +} + +void goAhead(){ + + digitalWrite(IN_1, LOW); + analogWrite(IN_2, speedCar); + + digitalWrite(IN_3, LOW); + analogWrite(IN_4, speedCar); + + } + +void goBack(){ + + analogWrite(IN_1, speedCar); + digitalWrite(IN_2, LOW); + + analogWrite(IN_3, speedCar); + digitalWrite(IN_4, LOW); + } + +void goRight(){ + + analogWrite(IN_1, speedCar); + digitalWrite(IN_2, LOW); + + digitalWrite(IN_3, LOW); + analogWrite(IN_4, speedCar); + } + +void goLeft(){ + + digitalWrite(IN_1, LOW); + analogWrite(IN_2, speedCar); + + analogWrite(IN_3, speedCar); + digitalWrite(IN_4, LOW); + } + +void goAheadRight(){ + + digitalWrite(IN_1, LOW); + analogWrite(IN_2, speedCar/speed_Coeff); + + digitalWrite(IN_3, LOW); + analogWrite(IN_4, speedCar); + } + +void goAheadLeft(){ + + digitalWrite(IN_1, LOW); + analogWrite(IN_2, speedCar); + + digitalWrite(IN_3, LOW); + analogWrite(IN_4, speedCar/speed_Coeff); + } + +void goBackRight(){ + + analogWrite(IN_1, speedCar/speed_Coeff); + digitalWrite(IN_2, LOW); + + analogWrite(IN_3, speedCar); + digitalWrite(IN_4, LOW); + } + +void goBackLeft(){ + + analogWrite(IN_1, speedCar); + digitalWrite(IN_2, LOW); + + analogWrite(IN_3, speedCar/speed_Coeff); + digitalWrite(IN_4, LOW); + } + +void stopRobot(){ + + digitalWrite(IN_1, LOW); + digitalWrite(IN_2, LOW); + + digitalWrite(IN_3, LOW); + digitalWrite(IN_4, LOW); + } + +void loop() { + server.handleClient(); + + command = server.arg("dir"); + if (command == "F") goAhead(); + else if (command == "B") goBack(); + else if (command == "L") goLeft(); + else if (command == "R") goRight(); + else if (command == "I") goAheadRight(); + else if (command == "G") goAheadLeft(); + else if (command == "J") goBackRight(); + else if (command == "H") goBackLeft(); + else if (command == "0") speedCar = 400; + else if (command == "1") speedCar = 470; + else if (command == "2") speedCar = 540; + else if (command == "3") speedCar = 610; + else if (command == "4") speedCar = 680; + else if (command == "5") speedCar = 750; + else if (command == "6") speedCar = 820; + else if (command == "7") speedCar = 890; + else if (command == "8") speedCar = 960; + else if (command == "9") speedCar = 1023; + else if (command == "S") stopRobot(); + +} + +void HTTP_handleRoot(void) { + +if( server.hasArg("dir") ){ + Serial.println(server.arg("dir")); + server.send ( 200, "text/html", "" ); + wither = 0; + } + delay(1); +} diff --git a/examples/02_auto_obstruction_detection/readme.md b/examples/02_auto_obstruction_detection/readme.md new file mode 100644 index 0000000..994d622 --- /dev/null +++ b/examples/02_auto_obstruction_detection/readme.md @@ -0,0 +1,18 @@ +The bot will detect any obstruction and stop without even crashing into it +To enable the kit add the below code on top: + +``` +#define m1_pin1 2; +#define m1_pin2 10; +#define m2_pin1 0; +#define m2_pin2 14; + +#define io1 12; +#define io2 13; + +#define analog_sensor A0; + +#define i2c_tx 4; +#define i2c_rx 5; + +``` \ No newline at end of file diff --git a/examples/05_rgb_control/05_rgb_control.ino b/examples/05_rgb_control/05_rgb_control.ino new file mode 100644 index 0000000..1c4801b --- /dev/null +++ b/examples/05_rgb_control/05_rgb_control.ino @@ -0,0 +1,31 @@ +#include +#include "blite.h" +#define NUMPIXELS 16 + +const int PIN = io2; + +Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); + +void setup() { + pixels.begin(); +} + +void loop() { +set_all(0,0,255); +delay(5000); +set_all(0,255,0); +delay(5000); +set_all(255,255,0); +delay(5000); +set_all(255,255,255); +delay(5000); + +} +void set_all(int r, int g, int b) { + for(int i=0;i - -//conncet the utrasonic sensor with 4 pin sensor +#include "blite.h" //define sound velocity in cm/uS #define SOUND_VELOCITY 0.034 #define CM_TO_INCH 0.393701 -Blite myBot; -int trigPin = myBot.getIO("io3"); -int echoPin = myBot.getIO("io4"); - long duration; float distanceCm; float distanceInch; +#define echoPin D1 +#define trigPin D2 + void setup() { - myBot.setup(); - myBot.smartConnectWiFi(); Serial.begin(115200); // Starts the serial communication pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output pinMode(echoPin, INPUT); // Sets the echoPin as an Input } void loop() { - myBot.otaLoop(); // Clears the trigPin digitalWrite(trigPin, LOW); delayMicroseconds(2); diff --git a/examples/08_distance_measuring/blite.h b/examples/08_distance_measuring/blite.h new file mode 100644 index 0000000..6025e02 --- /dev/null +++ b/examples/08_distance_measuring/blite.h @@ -0,0 +1,28 @@ +#ifndef BLITE_H +#define BLITE_H + +// Motor pins(2 X 2-pin connector) connected to DRR8833 +#define m1_pin1 0 +#define m1_pin2 2 +#define m2_pin1 14 +#define m2_pin2 15 + +// programmable I/O (2 X 3-pin connector) 5v bi-directional level shifted +#define io1 12 +#define io2 13 + +// Analog input pin (1 X 3-pin connector) driving at 5v +#define analog_sensor A0 + +// I2C pin (1X 4-pin connector) driving at 3.3 level +#define i2c_tx 4 +#define i2c_rx 5 + +//when used with sensors like HC-SR04 +#define io3 4 +#define io4 5 + +// pre-soldered components in board +#define thermistor 16 + +#endif \ No newline at end of file diff --git a/examples/11_line_follower/11_line_follower.ino b/examples/11_line_follower/11_line_follower.ino index c743934..7f1e86d 100644 --- a/examples/11_line_follower/11_line_follower.ino +++ b/examples/11_line_follower/11_line_follower.ino @@ -1,46 +1,92 @@ -#include -Blite myBot; -int irLeft = myBot.getIO("io1"); -int irRight = myBot.getIO("io2"); -bool lineFollwerMode = false; +#define IN_1 D1 // Right Connector motor +#define IN_2 D2 +#define IN_3 D3 // Left Connector motor +#define IN_4 D4 + + +#define S1 D6 +#define S2 D7 + +int speedCar = 400; //0-1024 void setup() { // Debug console - Serial.begin(115200); - myBot.setup(); - myBot.smartConnectWiFi(); + Serial.begin(9600); + pinMode(IN_1,OUTPUT); + pinMode(IN_2,OUTPUT); + pinMode(IN_3,OUTPUT); + pinMode(IN_4,OUTPUT); + + pinMode(S1,INPUT); + pinMode(S2,INPUT); + + digitalWrite(IN_1,LOW); + digitalWrite(IN_2,LOW); + digitalWrite(IN_3,LOW); + digitalWrite(IN_4,LOW); } -void loop() -{ - myBot.otaLoop(); +void goAhead(){ + + digitalWrite(IN_1, LOW); + analogWrite(IN_2, speedCar); + + digitalWrite(IN_3, LOW); + analogWrite(IN_4, speedCar); + + } + +void goRight(){ + + analogWrite(IN_1, speedCar); + digitalWrite(IN_2, LOW); - if(myBot.buttonPressed()) { - lineFollwerMode = !lineFollwerMode; + digitalWrite(IN_3, LOW); + analogWrite(IN_4, speedCar); } - if (lineFollwerMode){ + +void goLeft(){ + + digitalWrite(IN_1, LOW); + analogWrite(IN_2, speedCar); + + analogWrite(IN_3, speedCar); + digitalWrite(IN_4, LOW); + } + +void stopRobot(){ + + digitalWrite(IN_1, LOW); + digitalWrite(IN_2, LOW); + + digitalWrite(IN_3, LOW); + digitalWrite(IN_4, LOW); + } + +void loop() +{ + // if(digitalRead(switch)==HIGH) + // { Serial.print("Line Follower Mode"); - if(digitalRead(irLeft)==LOW && digitalRead(irRight)==LOW) + if(digitalRead(S1)==LOW && digitalRead(S2)==LOW) { - myBot.moveForward(); + goAhead(); } - if(digitalRead(irLeft)==HIGH && digitalRead(irRight)==LOW) + if(digitalRead(S1)==HIGH && digitalRead(S2)==LOW) { - myBot.turnRight(); + goRight(); } - if(digitalRead(irLeft)==LOW && digitalRead(irRight)==HIGH) + if(digitalRead(S1)==LOW && digitalRead(S2)==HIGH) { - myBot.turnLeft(); + goLeft(); } - if(digitalRead(irLeft)==HIGH && digitalRead(irRight)==HIGH) + if(digitalRead(S1)==HIGH && digitalRead(S2)==HIGH) { - myBot.stopMotor(); - lineFollwerMode = false; - + stopRobot(); } - } + // } } \ No newline at end of file diff --git a/examples/12_car_control_from_web/12_car_control_from_web.ino b/examples/12_car_control_from_web/12_car_control_from_web.ino new file mode 100644 index 0000000..34e97af --- /dev/null +++ b/examples/12_car_control_from_web/12_car_control_from_web.ino @@ -0,0 +1,149 @@ + +#include +#include +#include +#include "index.h" + +#define CMD_STOP 0 +#define CMD_FORWARD 1 +#define CMD_BACKWARD 2 +#define CMD_LEFT 4 +#define CMD_RIGHT 8 + + +#define IN1_PIN D2 // The ESP8266 pin connected to the IN1 pin DRV8833 +#define IN2_PIN D5 // The ESP8266 pin connected to the IN2 pin DRV8833 +#define IN3_PIN D6 // The ESP8266 pin connected to the IN3 pin DRV8833 +#define IN4_PIN D7 // The ESP8266 pin connected to the IN4 pin DRV8833 + + +const char* ssid = "SSID"; // CHANGE IT +const char* password = "PASSWORD"; // CHANGE IT + +ESP8266WebServer server(80); // Web server on port 80 +WebSocketsServer webSocket = WebSocketsServer(81); // WebSocket server on port 81 + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { + switch (type) { + case WStype_DISCONNECTED: + Serial.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]); + } + break; + case WStype_TEXT: + //Serial.printf("[%u] Received text: %s\n", num, payload); + String angle = String((char*)payload); + int command = angle.toInt(); + Serial.print("command: "); + Serial.println(command); + + switch (command) { + case CMD_STOP: + Serial.println("Stop"); + CAR_stop(); + break; + case CMD_FORWARD: + Serial.println("Move Forward"); + CAR_moveForward(); + break; + case CMD_BACKWARD: + Serial.println("Move Backward"); + CAR_moveBackward(); + break; + case CMD_LEFT: + Serial.println("Turn Left"); + CAR_turnLeft(); + break; + case CMD_RIGHT: + Serial.println("Turn Right"); + CAR_turnRight(); + break; + default: + Serial.println("Unknown command"); + } + + break; + } +} + +void setup() { + Serial.begin(9600); + + pinMode(IN1_PIN, OUTPUT); + pinMode(IN2_PIN, OUTPUT); + pinMode(IN3_PIN, OUTPUT); + pinMode(IN4_PIN, OUTPUT); + + // Connect to Wi-Fi + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(1000); + Serial.println("Connecting to WiFi..."); + } + Serial.println("Connected to WiFi"); + + // Initialize WebSocket server + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + // Serve a basic HTML page with JavaScript to create the WebSocket connection + server.on("/", HTTP_GET, []() { + Serial.println("Web Server: received a web page request"); + String html = HTML_CONTENT; // Use the HTML content from the servo_html.h file + server.send(200, "text/html", html); + }); + + server.begin(); + Serial.print("ESP8266 Web Server's IP address: "); + Serial.println(WiFi.localIP()); +} + +void loop() { + // Handle client requests + server.handleClient(); + + // Handle WebSocket events + webSocket.loop(); + + // TO DO: Your code here +} + +void CAR_moveForward() { + digitalWrite(IN1_PIN, HIGH); + digitalWrite(IN2_PIN, LOW); + digitalWrite(IN3_PIN, HIGH); + digitalWrite(IN4_PIN, LOW); +} + +void CAR_moveBackward() { + digitalWrite(IN1_PIN, LOW); + digitalWrite(IN2_PIN, HIGH); + digitalWrite(IN3_PIN, LOW); + digitalWrite(IN4_PIN, HIGH); +} + +void CAR_turnLeft() { + digitalWrite(IN1_PIN, HIGH); + digitalWrite(IN2_PIN, LOW); + digitalWrite(IN3_PIN, LOW); + digitalWrite(IN4_PIN, LOW); +} + +void CAR_turnRight() { + digitalWrite(IN1_PIN, LOW); + digitalWrite(IN2_PIN, LOW); + digitalWrite(IN3_PIN, HIGH); + digitalWrite(IN4_PIN, LOW); +} + +void CAR_stop() { + digitalWrite(IN1_PIN, LOW); + digitalWrite(IN2_PIN, LOW); + digitalWrite(IN3_PIN, LOW); + digitalWrite(IN4_PIN, LOW); +} \ No newline at end of file diff --git a/examples/14_blite_rc_car/test.h b/examples/12_car_control_from_web/index.h similarity index 98% rename from examples/14_blite_rc_car/test.h rename to examples/12_car_control_from_web/index.h index 6decf8d..a5b06ca 100644 --- a/examples/14_blite_rc_car/test.h +++ b/examples/12_car_control_from_web/index.h @@ -1,5 +1,5 @@ -const char *TEST_HTML_CONTENT = R"=====( +const char *HTML_CONTENT = R"=====( @@ -11,7 +11,7 @@ button { text-align: center; font-size: 24px;} #container { margin-right: auto; margin-left: auto; - width: 400px; + width: 400px; height: 400px; position: relative; margin-bottom: 10px; @@ -77,7 +77,7 @@ var ws = null; function init() { - + var container = document.querySelector("#container"); container.addEventListener("touchstart", mouse_down); container.addEventListener("touchend", mouse_up); @@ -89,7 +89,7 @@ function init() function ws_onmessage(e_msg) { e_msg = e_msg || window.event; // MessageEvent - + //alert("msg : " + e_msg.data); } function ws_onopen() @@ -113,7 +113,7 @@ function wc_onclick() { ws = new WebSocket("ws://" + window.location.host + ":81"); document.getElementById("ws_state").innerHTML = "CONNECTING"; - + ws.onopen = ws_onopen; ws.onclose = ws_onclose; ws.onmessage = ws_onmessage; diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index e63dd7b..1e71f3f 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -1,17 +1,8 @@ #include -<<<<<<< HEAD #include -======= -#include "remote.h" -#include "test.h" ->>>>>>> af1e3ca0525205fa8100360e129f9d18ad6427cb #include #include "remote.h" -<<<<<<< HEAD -======= - ->>>>>>> af1e3ca0525205fa8100360e129f9d18ad6427cb #define CMD_BACKWARD 2 #define CMD_STOP 0 #define CMD_FORWARD 1 @@ -21,34 +12,10 @@ #define CMD_SRVCLCK 6 #define CMD_SRVACLCK 9 -<<<<<<< HEAD Blite mybot; ESP8266WebServer wifiRemoteControl(80); WebSocketsServer webSocket = WebSocketsServer(81); // WebSocket server on port 81 -int puchButtonCounter = 0; -======= -Blite myBot; -WebSocketsServer webSocket = WebSocketsServer(81); -// String html = REMOTE_HTML_CONTENT; -String html = TEST_HTML_CONTENT; - - -void setup(){ - myBot.setup(); - Serial.begin(115200); - myBot.smartConnectWiFi(); - webSocket.begin(); - webSocket.onEvent(webSocketEvent); - -} -void loop(){ - myBot.smartRenderServer(html); - webSocket.loop(); -} ->>>>>>> af1e3ca0525205fa8100360e129f9d18ad6427cb - - void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { switch (type) { case WStype_DISCONNECTED: @@ -70,7 +37,6 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) switch (command) { case CMD_STOP: Serial.println("Stop"); -<<<<<<< HEAD mybot.stopMotor(); break; case CMD_FORWARD: @@ -100,37 +66,6 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) case CMD_SRVACLCK: Serial.println("Turn servo anti-clockwise"); //mybot.turnServoAntiClockwise(); -======= - // myBot.stop(); - break; - case CMD_FORWARD: - Serial.println("Move Forward"); - myBot.moveForward(); - break; - case CMD_BACKWARD: - Serial.println("Move Backward"); - myBot.moveBackward(); - break; - case CMD_LEFT: - Serial.println("Turn Left"); - myBot.turnLeft(); - break; - case CMD_RIGHT: - Serial.println("Turn Right"); - myBot.turnRight(); - break; - case CMD_PUSH: - Serial.println("Push button pressed"); - //myBot.push(); - break; - case CMD_SRVCLCK: - Serial.println("Turn servo clockwise"); - //myBot.turnServoClockwise(); - break; - case CMD_SRVACLCK: - Serial.println("Turn servo anti-clockwise"); - //myBot.turnServoAntiClockwise(); ->>>>>>> af1e3ca0525205fa8100360e129f9d18ad6427cb break; default: Serial.println("Unknown command"); diff --git a/examples/14_blite_rc_car/remote.h b/examples/14_blite_rc_car/remote.h index a2f3aa3..e4a6fa7 100644 --- a/examples/14_blite_rc_car/remote.h +++ b/examples/14_blite_rc_car/remote.h @@ -198,17 +198,6 @@ function mouse_up(event) function wc_onclickpush() { -<<<<<<< HEAD - send_command(5); -} -function wc_onclickservclck() -{ - send_command(6); -} -function wc_onclickservaclck() -{ - send_command(9); -======= send_command(CMD_PUSH); } function wc_onclickservclck() @@ -218,7 +207,6 @@ function wc_onclickservclck() function wc_onclickservaclck() { send_command(CMD_SRVACLCK); ->>>>>>> af1e3ca0525205fa8100360e129f9d18ad6427cb } function send_command(cmd) From 652946c16d59ab7a09d8aa6a7aba8cf92d9062db Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 21 Mar 2024 17:17:22 +0530 Subject: [PATCH 29/32] web_page string to be passesd by referance --- src/blite.cpp | 8 ++++---- src/blite.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/blite.cpp b/src/blite.cpp index 63a9b28..4152789 100644 --- a/src/blite.cpp +++ b/src/blite.cpp @@ -129,9 +129,9 @@ int Blite::readADC(){ return analogRead(ADC1); } -void Blite::setupServer(String HTML_CONTENT) { +void Blite::setupServer(String &html_content) { this->webServer.on("/", HTTP_GET, [=]() { - this->webServer.send(200, "text/html", HTML_CONTENT); + this->webServer.send(200, "text/html", html_content); }); this->webServer.begin(); this->serverSetupDone = true; @@ -142,9 +142,9 @@ void Blite::renderServer() { this->otaLoop(); } -void Blite::smartRenderServer(String HTML_CONTENT){ +void Blite::smartRenderServer(String &html_content){ if (!this->serverSetupDone) { - this->setupServer(HTML_CONTENT); + this->setupServer(html_content); } this->renderServer(); } diff --git a/src/blite.h b/src/blite.h index c54db8f..3eee2d7 100644 --- a/src/blite.h +++ b/src/blite.h @@ -45,9 +45,9 @@ bool buttonPressed(); void blinkLed(int c); int readADC(); -void setupServer(String HTML_CONTENT); +void setupServer(String &html_content); void renderServer(); -void smartRenderServer(String HTML_CONTENT); +void smartRenderServer(String &html_content); void otaSetup(); void otaLoop(); From d0e1b48518e47ee37b228ce8f227158d53c772ff Mon Sep 17 00:00:00 2001 From: Sayan Paul Date: Thu, 21 Mar 2024 17:18:22 +0530 Subject: [PATCH 30/32] working example of webpage with custom scripts --- examples/14_blite_rc_car/14_blite_rc_car.ino | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index 1e71f3f..0e74720 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -1,5 +1,4 @@ #include -#include #include #include "remote.h" @@ -14,8 +13,9 @@ Blite mybot; -ESP8266WebServer wifiRemoteControl(80); WebSocketsServer webSocket = WebSocketsServer(81); // WebSocket server on port 81 +String html_string = REMOTE_HTML_CONTENT; + void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { switch (type) { case WStype_DISCONNECTED: @@ -79,19 +79,11 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) void setup(){ mybot.setup(); Serial.begin(115200); - mybot.smartConnectWiFi(); + mybot.APServer(); webSocket.begin(); webSocket.onEvent(webSocketEvent); - wifiRemoteControl.on("/", HTTP_GET, []() { - Serial.println("Web Server: received a web page request"); - String html = REMOTE_HTML_CONTENT; - wifiRemoteControl.send(200, "text/html", html); - }); - wifiRemoteControl.begin(); - } void loop(){ - wifiRemoteControl.handleClient(); + mybot.smartRenderServer(html_string); webSocket.loop(); - } From 60ae880768d93193bc4005e521d421e533b6b071 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 5 Apr 2024 20:53:29 +0530 Subject: [PATCH 31/32] control neo led lights using websocket --- .../13_rgb_web_control/13_rgb_web_control.ino | 164 ++++-------------- examples/13_rgb_web_control/lighting.h | 75 ++++++++ 2 files changed, 108 insertions(+), 131 deletions(-) create mode 100644 examples/13_rgb_web_control/lighting.h diff --git a/examples/13_rgb_web_control/13_rgb_web_control.ino b/examples/13_rgb_web_control/13_rgb_web_control.ino index 94897cd..34065d1 100644 --- a/examples/13_rgb_web_control/13_rgb_web_control.ino +++ b/examples/13_rgb_web_control/13_rgb_web_control.ino @@ -1,150 +1,52 @@ -#include -#include +#include #include -#include +#include +#include "lighting.h" #include - -// Webserver Config -const char *ssid = "ZTE_2.4G_6eZrkS"; -const char *password = "ijF3kXh4"; -ESP8266WebServer server ( 80 ); - // Neopixel Config -#define NeoPIN D4 +#define NeoPIN 2 #define NUM_LEDS 16 + int brightness = 150; Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, NeoPIN, NEO_RGB + NEO_KHZ800); - - +Blite mybot; +ESP8266WebServer wifiRemoteControl(80); +WebSocketsServer webSocket = WebSocketsServer(81); // WebSocket server on port 81 const int led = 13; -void setup ( void ) { - - Serial.begin ( 115200 ); - - // ############## - // NeoPixel start - Serial.println(); - strip.setBrightness(brightness); - strip.begin(); - strip.show(); - delay(50); - Serial.println("NeoPixel started"); - - // ######### - // Webserver - pinMode ( led, OUTPUT ); - digitalWrite ( led, 0 ); - - WiFi.begin ( ssid, password ); - Serial.println ( "" ); - - // Wait for connection - while ( WiFi.status() != WL_CONNECTED ) { - delay ( 500 ); - Serial.print ( "." ); - } - - Serial.println ( "" ); - Serial.print ( "Connected to " ); - Serial.println ( ssid ); - Serial.print ( "IP address: " ); - Serial.println ( WiFi.localIP() ); - - if ( MDNS.begin ( "esp8266" ) ) { - Serial.println ( "MDNS responder started" ); - } - - // what to do with requests - server.on ( "/", handleRoot ); - server.onNotFound ( handleNotFound ); - server.begin(); - - - - Serial.println ( "HTTP server started" ); -} - -void loop ( void ) { - // waiting fo a client - server.handleClient(); +void setup(){ + mybot.setup(); + Serial.begin(115200); + mybot.smartConnectWiFi(); + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + wifiRemoteControl.on("/", HTTP_GET, []() { + Serial.println("Web Server: received a web page request"); + String html = HTML_LIGHT; + wifiRemoteControl.send(200, "text/html", html); + }); + wifiRemoteControl.begin(); + } - -void handleRoot() { - Serial.println("Client connected"); - digitalWrite ( led, 1 ); - - // data from the colorpicker (e.g. #FF00FF) - String color = server.arg("c"); - Serial.println("Color: " + color); - // setting the color to the strip - setNeoColor(color); - - // building a website - char temp[5000]; - int sec = millis() / 1000; - int min = sec / 60; - int hr = min / 60; - char clr [7]; - color.toCharArray(clr, 7); - snprintf ( temp, 5000, - -"\n\n\ - \n\ - wifi pixel\n\ - \n\ - \n\ - \n\ - \n\ -

Buildybee

\n\ -

pick the color!

\n\ - \n\ -
\n\ - \n\ -   \n\ -
\n\ - \n\ - \ -", - - hr, min % 60, sec % 60, clr - ); - server.send ( 200, "text/html", temp ); - digitalWrite ( led, 0 ); +void loop(){ + wifiRemoteControl.handleClient(); + webSocket.loop(); + } -void handleNotFound() { - digitalWrite ( led, 1 ); - String message = "File Not Found\n\n"; - message += "URI: "; - message += server.uri(); - message += "\nMethod: "; - message += ( server.method() == HTTP_GET ) ? "GET" : "POST"; - message += "\nArguments: "; - message += server.args(); - message += "\n"; - - for ( uint8_t i = 0; i < server.args(); i++ ) { - message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n"; - } - - server.send ( 404, "text/plain", message ); - digitalWrite ( led, 0 ); +void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { + String command = String((char*)payload); + Serial.print("command: "); + Serial.println(command); + // setting the color to the strip + setNeoColor(command); } - - void setNeoColor(String value){ Serial.print("Setting Neopixel..."); // converting Hex to Int - int number = (int) strtol( &value[1], NULL, 16); - + int number = (int) strtol( &value[1], NULL, 16); // splitting into three parts int r = number >> 16; int g = number >> 8 & 0xFF; @@ -167,4 +69,4 @@ void setNeoColor(String value){ strip.show(); Serial.println("on."); -} \ No newline at end of file +} diff --git a/examples/13_rgb_web_control/lighting.h b/examples/13_rgb_web_control/lighting.h new file mode 100644 index 0000000..a3a3765 --- /dev/null +++ b/examples/13_rgb_web_control/lighting.h @@ -0,0 +1,75 @@ +const char *HTML_LIGHT = R"=====( + + + + wifi pixel + + + + + +

Buildybee

+

pick the color!

+

+   +

+

+ WebSocket : closed +
+

+ + +)====="; \ No newline at end of file From c3076a2990a268eca6f55d2ab7aa8e42007f114a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 9 Apr 2024 17:07:07 +0530 Subject: [PATCH 32/32] added HTML default buttons in ex14 --- .../08_distance_measuring.ino | 2 +- examples/14_blite_rc_car/14_blite_rc_car.ino | 16 +- examples/14_blite_rc_car/remote.h | 139 +++++++++--------- 3 files changed, 83 insertions(+), 74 deletions(-) diff --git a/examples/08_distance_measuring/08_distance_measuring.ino b/examples/08_distance_measuring/08_distance_measuring.ino index 31b76bb..6a1b10e 100644 --- a/examples/08_distance_measuring/08_distance_measuring.ino +++ b/examples/08_distance_measuring/08_distance_measuring.ino @@ -1,4 +1,4 @@ -#include "blite.h" +#include //define sound velocity in cm/uS #define SOUND_VELOCITY 0.034 diff --git a/examples/14_blite_rc_car/14_blite_rc_car.ino b/examples/14_blite_rc_car/14_blite_rc_car.ino index 0e74720..1e71f3f 100644 --- a/examples/14_blite_rc_car/14_blite_rc_car.ino +++ b/examples/14_blite_rc_car/14_blite_rc_car.ino @@ -1,4 +1,5 @@ #include +#include #include #include "remote.h" @@ -13,9 +14,8 @@ Blite mybot; +ESP8266WebServer wifiRemoteControl(80); WebSocketsServer webSocket = WebSocketsServer(81); // WebSocket server on port 81 -String html_string = REMOTE_HTML_CONTENT; - void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) { switch (type) { case WStype_DISCONNECTED: @@ -79,11 +79,19 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) void setup(){ mybot.setup(); Serial.begin(115200); - mybot.APServer(); + mybot.smartConnectWiFi(); webSocket.begin(); webSocket.onEvent(webSocketEvent); + wifiRemoteControl.on("/", HTTP_GET, []() { + Serial.println("Web Server: received a web page request"); + String html = REMOTE_HTML_CONTENT; + wifiRemoteControl.send(200, "text/html", html); + }); + wifiRemoteControl.begin(); + } void loop(){ - mybot.smartRenderServer(html_string); + wifiRemoteControl.handleClient(); webSocket.loop(); + } diff --git a/examples/14_blite_rc_car/remote.h b/examples/14_blite_rc_car/remote.h index e4a6fa7..bccb1ff 100644 --- a/examples/14_blite_rc_car/remote.h +++ b/examples/14_blite_rc_car/remote.h @@ -8,70 +8,77 @@ const char *REMOTE_HTML_CONTENT = R"=====(