Interface and Application Programming

Interfaces for Wireless Communication

Group Assignment

This week’s group assignment explores creating visual user interfaces through creative coding toolkits. We created an interface using p5.js, Processing and Openframeworks and used it to communicate with an ESP32 board through RestAPI and serial communication.

Building a Network Interface with Jquery and Bootstrap

We start by downloading jQuery. Save as… into a filepath called “BootstrapREST>lib>jquery”

Then download Bootstrap. It is an open source CSS framework, that is used for easily styling webpages. It can create responsive webpages for both mobile and desktop browsers.

We download and unzip into the file “BootstrapREST>lib” and change the name of the unzipped folder to “bootstrap”.

After that, we open “BootstrapREST” in Vscode. We create index.html, give it a title and some text.

Then we add the jQuery library to our code, such as in the code snippet below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
</head>
<body>
    Hello world!

    <script src="lib/jquery/jquery-3.6.4.min.js"></script>
</body>
</html>

When writing javascript, we use the $ sign to write commands. For example, we can execute the function below to write a console log. Console logs allow us to debug processes, just like Serial.print in Arduino.

1
2
3
4
5
    <script>
      $(document).ready(function(){
        console.log("Document has loaded!")
      });
    </script>

Here is a few tricks on how we can customize the styling of our html page using jQuery. For example, we can change the color of all “h1” or all “p” elements, or single them out using an “id” tag. We can also style individual “div” elements using classes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
</head>
<body>
    <h1>Hello world!</h1>
    <p>This paragraph is generic.</p>
    <p id="specific-paragraph">This paragraph has a specific id.</p>

    <div class="para-a">
      <p>This is a div with the "para-a" class</p>
    </div>
    
    <div class="para-b">
      <p>And this is a div with the "para-b" class</p>
    </div>

    <script src="lib/jquery/jquery-3.6.4.min.js"></script>
    <script>
      $(document).ready(function(){
        console.log("Document has loaded!");

        $("h1").css("color", "green");
        $("p").css("color","red");
        $("#specific-paragraph").css("color", "blue");
        $(".para-a").css("background", "lightgreen");
        $(".para-b").css("background", "yellow");
      });
    </script>
  </body>
</html>

Connecting the XIAO ESP32C

Then we move on to connecting the XIAO to this setup. To do this, we use the WiFi functionality of the XIAO ESP32C. We built the code below on the networking week.

Here is how we can continously check for an API call. If the server does not get a response from the XIAO, it prints API call failed.

CODE: jQuery
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
</head>
<body>

    <div>API Status: <span id="status.api">Undefined</span></div>
    <h1>Hello world!</h1>

    <script src="lib/jquery/jquery-3.6.4.min.js"></script>
    <script>
      function checkAPIStatus(){
        $.ajax({
          url:"http://193.167.5.175/",
          timeout: 5000
        })
        .done(function(){
          console.log("API call successfull");
        })
        .fail(function(){
          console.log("API call failed");
        })
      }
      $(document).ready(function(){
        console.log("Document has loaded!");

        setInterval(checkAPIStatus,2000);
      });
    </script>
  </body>
</html>
CODE: Arduino IDE
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebSrv.h>

// Some variables we will need along the way
const char* ssid     = "Fablab";
const char* password = "Fabricationlab1"; 
const char* PARAM_MESSAGE = "message"; 
int webServerPort = 80;
int led = D1;

// Setting up our webserver
AsyncWebServer server(webServerPort);

// This function will be called when human will try to access undefined endpoint
void notFound(AsyncWebServerRequest *request) {
  AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", "Not found");
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}

void sendResponse(AsyncWebServerRequest *request, String message){
  AsyncWebServerResponse *response = request -> beginResponse(200, "text/plain", message);
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}

void setup() {
  pinMode(led, OUTPUT);

  Serial.begin(115200);
  delay(10);

  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");

  // We want to know the IP address so we can send commands from our computer to the device
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Greet human when it tries to access the root / endpoint.
  // This is a good place to send some documentation about other calls available if you wish.
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    sendResponse(request, "Hello!");
  });

  // Usage IP_ADDRESS/led?state=1 where /led is our endpoint and ?state=on is a variable definition.
  // You can also chain variables like this: /led?sate=off&color=blue
  server.on("/led", HTTP_GET, [](AsyncWebServerRequest *request){
    bool state; // LED state 
    if (request->hasParam("state")) {
      // The incoming params are Strings
      String param = request->getParam("state")->value();
      // .. so we have to interpret or cast them
      state = ((param == "on") ? HIGH : LOW); // Look up Terary Operator (e.g. https://www.programiz.com/cpp-programming/ternary-operator)
    } else {
      state = LOW;
    }

    // Send back message to human
    String stateString = state ? "on" : "off";
    String responseJSON = "{\"ledState\":\"" + stateString + "\"}";
    sendResponse(request, responseJSON);

    // Operate LED
    digitalWrite(led, state);
  });

  server.on("/params", HTTP_GET, [](AsyncWebServerRequest *request){
    int param1 = random(100);
    int param2 = random(100);
    int param3 = random(100);
    int param4 = random(100);

    String responseJSON = "{";
      responseJSON += "\"param1\":" + String(param1) + ",";
      responseJSON += "\"param2\":" + String(param2) + ",";
      responseJSON += "\"param3\":" + String(param3) + ",";
      responseJSON += "\"param4\":" + String(param4) + ",";
      responseJSON += "}";

      sendResponse(request,responseJSON);
            
  });

  // If human tries endpoint no exist, exec this function
  server.onNotFound(notFound);

  Serial.print("Starting web server on port ");
  Serial.println(webServerPort);
  server.begin();
} 

void loop() {
  // Nothing needed here at the moment
}

A note about JSON: There are \ accross the code above. We use \ to escape the double quote("). When the program sees the \, it knows that whatever comes after it should not be recognized as a special character.

String responseJSON = "{\"ledState\":\"" + stateString + "\"}";

Controlling an LED

Then we continue with adding more functionality to the webpage. We first check if the XIAO is connected to the API. Now we will create a button that will allow us to turn the LED on and off.

CODE: jQuery
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
</head>
<body>

    <div>API Status: <span id="status-api">Undefined</span></div>
    <h1>Hello world!</h1>

    <div id="indicator-led">LED</div>

    <button id="button-led">Trigger LED</button>

    <script src="lib/jquery/jquery-3.6.4.min.js"></script>
    <script>
      function checkAPIStatus(){
        $.ajax({
          url:"http://193.167.5.175/",
          timeout: 5000
        })
        .done(function(){
          $("#status-api").text("Connected");
        })
        .fail(function(){
          $("#status-api").text("Not connected");
        })
      }

      function triggerLED(){
        $.ajax({
          url:"http://193.167.5.175/led",
          data: {
            state: "on"
          },
          timeout: 5000
        })
        .done(function(response){
          console.log(response);
          $("#indicator-led").css("background", "yellow");
        })
        .fail(function(){
          console.log("LED trigger call failed.")
        })
      }

      $(document).ready(function(){
        console.log("Document has loaded!");

        setInterval(checkAPIStatus,2000);

        $("#button-led").click(triggerLED);
      });
    </script>
  </body>
</html>

We have created our button with the code above. Now, we need to use this button to send a call and receive a response from the XIAO. We create an ledState variable under led. Depending on what response the system receives, it sets the ledState to “on” or “off”. Which turns the physical LED on/ off and changes the background of the button.

CODE: jQuery
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
</head>
<body>

    <div>API Status: <span id="status-api">Undefined</span></div>
    <h1>Hello world!</h1>

    <div id="led">LED</div>

    <button id="button-led">Trigger LED</button>

    <script src="lib/jquery/jquery-3.6.4.min.js"></script>
    <script>
      function checkAPIStatus(){
        $.ajax({
          url:"http://193.167.5.175/",
          timeout: 5000
        })
        .done(function(){
          $("#status-api").text("Connected");
        })
        .fail(function(){
          $("#status-api").text("Not connected");
        })
      }

      function triggerLED(){
        $.ajax({
          url:"http://193.167.5.175/led",
          data: {
            state: $("#led").data("ledState")
          },
          timeout: 5000
        })
        .done(function(response){
          const responseJSON = JSON.parse(response); //we convert this into an object so that:
           if (responseJSON.ledState == "on"){
            $("#led").data("ledState", "off");
            $("#led").css("background", "yellow");
           }else{
            $("#led").data("ledState", "on");
            $("#led").css("background", "gray");
           }
        })
        .fail(function(){
          console.log("LED trigger call failed.")
        })
      }

      $(document).ready(function(){
        console.log("Document has loaded!");

        setInterval(checkAPIStatus,2000);

        $("#button-led").click(triggerLED);
      });
    </script>
  </body>
</html>

Using Bootstrap

To use Bootstrap in our jQuery code, we need to make the following additions:

Here is how the modified header looks like:

1
2
3
4
5
    <header class="container mt-3">
      <div id="status-bar" class="alert alert-light" role="alert">
        API Status: <span id="status-api">Undefined</span>
      </div>
    </header>

Then in the checkAPIStatus function, we modify it as,

1
2
3
4
5
6
7
        .done(function(){
          $("#status-api").text("Connected");
          if($("#status-bar").hasClass("alert-light")){
            $("#status-bar").removeClass("alert-light");
          }
          $("#status-bar").addClass("alert-success");
        })
CODE: Modified jQuery with Bootstrap
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
  <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
</head>
<body>

    <header class="container mt-3">
      <div id="status-bar" class="alert alert-light" role="alert">
        API Status: <span id="status-api">Undefined</span>
      </div>
    </header>

    <main class="container">
      <h1>Hello world!</h1>
      <div id="led">LED</div>
      <button id="button-led" class="btn btn-info">Trigger LED</button>

    </main>
    
    <script src="lib/bootstrap/js/bootstrap.bundle.min.js"></script>
    <script src="lib/jquery/jquery-3.6.4.min.js"></script>
    <script>
      function checkAPIStatus(){
        $.ajax({
          url:"http://193.167.5.175/",
          timeout: 5000
        })
        .done(function(){
          $("#status-api").text("Connected");
          if($("#status-bar").hasClass("alert-light")){
            $("#status-bar").removeClass("alert-light");
          }
          $("#status-bar").addClass("alert-success");
        })
        .fail(function(){
          $("#status-api").text("Not connected");
        })
      }

      function triggerLED(){
        $.ajax({
          url:"http://193.167.5.175/led",
          data: {
            state: $("#led").data("ledState")
          },
          timeout: 5000
        })
        .done(function(response){
          const responseJSON = JSON.parse(response); //we convert this into an object so that:
           if (responseJSON.ledState == "on"){
            $("#led").data("ledState", "off");
            $("#led").css("background", "yellow");
           }else{
            $("#led").data("ledState", "on");
            $("#led").css("background", "gray");
           }
        })
        .fail(function(){
          console.log("LED trigger call failed.")
        })
      }

      $(document).ready(function(){
        console.log("Document has loaded!");

        setInterval(checkAPIStatus,2000);

        $("#button-led").click(triggerLED);
      });
    </script>
  </body>
</html>

Controlling LED through network interface

Modifying for Motor Turn - First Attempt

After going through the tutorial with the steps above, I decided to modify this weeks assignment to use in my final project. I needed a network interface to control the movement of servo motors. I modified the code step by step, making additions along the way.

Below is the first iteration I went through.

CODE: Arduino IDE v1 [WORKING]

Arduino Code v1 [WORKING]

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebSrv.h>

// Some variables we will need along the way
const char* ssid     = "Fablab";
const char* password = "Fabricationlab1"; 
const char* PARAM_MESSAGE = "message"; 
int webServerPort = 80;

int DIR_PIN = D5;
int STEP_PIN = D4;
int ENABLE_PIN = D6;
int POT_PIN = D1;
int potValue;
int adjustPot;
int prevAdjustPot=0;
int interval=3000;

// Setting up our webserver
AsyncWebServer server(webServerPort);

// This function will be called when human will try to access undefined endpoint
void notFound(AsyncWebServerRequest *request) {
  AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", "Not found");
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}

void sendResponse(AsyncWebServerRequest *request, String message){
  AsyncWebServerResponse *response = request -> beginResponse(200, "text/plain", message);
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}

void setup() {
  
  pinMode(STEP_PIN,OUTPUT);
  pinMode(DIR_PIN,OUTPUT);
  pinMode(ENABLE_PIN,OUTPUT);
  pinMode(POT_PIN, INPUT);
  digitalWrite(ENABLE_PIN, LOW);  

  Serial.begin(19200);
  delay(10);  

  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");

  // We want to know the IP address so we can send commands from our computer to the device
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Greet human when it tries to access the root / endpoint.
  // This is a good place to send some documentation about other calls available if you wish.
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    sendResponse(request, "Hello!");
  });

  // Usage IP_ADDRESS/led?state=1 where /led is our endpoint and ?state=on is a variable definition.
  // You can also chain variables like this: /led?sate=off&color=blue
  server.on("/motor", HTTP_GET, [](AsyncWebServerRequest *request){
    bool state; // LED state 
    if (request->hasParam("state")) {
      // The incoming params are Strings
      String param = request->getParam("state")->value();
      // .. so we have to interpret or cast them
      state = ((param == "on") ? HIGH : LOW); // Look up Terary Operator (e.g. https://www.programiz.com/cpp-programming/ternary-operator)
    } else {
      state = LOW;
    }

    // Send back message to human
    String stateString = state ? "on" : "off";
    String responseJSON = "{\"motorState\":\"" + stateString + "\"}";
    sendResponse(request, responseJSON);

    // Operate LED
   // digitalWrite(led, state);

    if (state==HIGH){
      Serial.println("Turning motor");
    }
  });

  server.on("/params", HTTP_GET, [](AsyncWebServerRequest *request){
    int param1 = random(100);
    int param2 = random(100);
    int param3 = random(100);
    int param4 = random(100);

    String responseJSON = "{";
      responseJSON += "\"param1\":" + String(param1) + ",";
      responseJSON += "\"param2\":" + String(param2) + ",";
      responseJSON += "\"param3\":" + String(param3) + ",";
      responseJSON += "\"param4\":" + String(param4) + ",";
      responseJSON += "}";

      sendResponse(request,responseJSON);
            
  });

  // If human tries endpoint no exist, exec this function
  server.onNotFound(notFound);

  Serial.print("Starting web server on port ");
  Serial.println(webServerPort);
  server.begin();  
  
}

void loop() {
}

void moveClock(int steps){
  digitalWrite(DIR_PIN, LOW);
  for(int i=0; i<steps; i++){
    digitalWrite(STEP_PIN, HIGH);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(interval);
  }
}

void moveCounterClock(int steps){
  digitalWrite(DIR_PIN, HIGH);
  for(int i=0; i<steps; i++){
    digitalWrite(STEP_PIN, HIGH);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(interval);
  }
}
CODE: jQuery v1 [WORKING]

jQuery Code v1 [WORKING]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
  <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
</head>
<body>

    <header class="container mt-3">
      <div id="status-bar" class="alert alert-light" role="alert">
        API Status: <span id="status-api">Undefined</span>
      </div>
    </header>

    <main class="container">
      <h1>stepper control test</h1>
      <div id="motor-sign">Motor Rotation</div>
      <button id="button-counter-clockwise" class="btn btn-warning">Turn Counter-clockwise</button>
      <button id="button-clockwise" class="btn btn-info">Turn Clockwise</button>
    </main>
    
    <script src="lib/bootstrap/js/bootstrap.bundle.min.js"></script>
    <script src="lib/jquery/jquery-3.6.4.min.js"></script>
    <script>
      function checkAPIStatus(){
        $.ajax({
          url:"http://193.167.5.175/",
          timeout: 5000
        })
        .done(function(){
          $("#status-api").text("Connected");
          if($("#status-bar").hasClass("alert-light")){
            $("#status-bar").removeClass("alert-light");
          }
          $("#status-bar").addClass("alert-success");
        })
        .fail(function(){
          $("#status-api").text("Not connected");
        })
      }

      function triggerClockwise(){
        const myState = $("#motor-sign").data("motorState");
        console.log(myState); 
        $.ajax({
          url:"http://193.167.5.175/motor",
          data: {
            state: myState
          },
          timeout: 5000
        })
        .done(function(response){
          const responseJSON = JSON.parse(response); //we convert this into an object so that:
           if (responseJSON.motorState == "on"){
            $("#motor-sign").data("motorState", "off");
            $("#motor-sign").css("background", "yellow");
            
           }else{
            $("#motor-sign").data("motorState", "on");
            $("#motor-sign").css("background", "gray");
           }
        })
        .fail(function(){
          console.log("motor trigger call failed.");
        })
      }

      $(document).ready(function(){
        console.log("Document has loaded!");

        setInterval(checkAPIStatus,2000);

        $("#button-clockwise").click(triggerClockwise);
      });
    </script>
  </body>
</html>

Second Attempt

The issue with the first iteration was that it had only one variable that was either “on” or “off”. I wanted to have something different than bool variable because my servos will have more than two states. So I used int instead.

First, I tried to use a “switch” statement but it did not work, so I used an “if else” statement.

In the code below, we send “motorState” data to XIAO. It is either “Cturn” or “CWturn”. Then XIAO takes this parameter, if it’s “Cturn” it sets “state =2”. If it’s “CWturn” it sets “state =3”, if none, it sets “state = 0”.

According to the states, XIAO builds the string. The string is either “Cturn”, “CWturn” or “notTurning”. Then, it sends these back to the network interface as strings.

CODE: Arduino IDE v2 [WORKING]

Arduino Code v2 [WORKING]

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebSrv.h>

// Some variables we will need along the way
const char* ssid     = "Fablab";
const char* password = "Fabricationlab1"; 
const char* PARAM_MESSAGE = "message"; 
int webServerPort = 80;

int DIR_PIN = D5;
int STEP_PIN = D4;
int ENABLE_PIN = D6;
int POT_PIN = D1;
int potValue;
int adjustPot;
int prevAdjustPot=0;
int interval=3000;

// Setting up our webserver
AsyncWebServer server(webServerPort);

// This function will be called when human will try to access undefined endpoint
void notFound(AsyncWebServerRequest *request) {
  AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", "Not found");
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}

void sendResponse(AsyncWebServerRequest *request, String message){
  AsyncWebServerResponse *response = request -> beginResponse(200, "text/plain", message);
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}

void setup() {
  
  pinMode(STEP_PIN,OUTPUT);
  pinMode(DIR_PIN,OUTPUT);
  pinMode(ENABLE_PIN,OUTPUT);
  pinMode(POT_PIN, INPUT);
  digitalWrite(ENABLE_PIN, LOW);  

  Serial.begin(19200);
  delay(10);  

  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");

  // We want to know the IP address so we can send commands from our computer to the device
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Greet human when it tries to access the root / endpoint.
  // This is a good place to send some documentation about other calls available if you wish.
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    sendResponse(request, "Hello!");
  });

  // Usage IP_ADDRESS/led?state=1 where /led is our endpoint and ?state=on is a variable definition.
  // You can also chain variables like this: /led?sate=off&color=blue
  server.on("/motor", HTTP_GET, [](AsyncWebServerRequest *request){
    int state; // motor state 
    if (request->hasParam("state")) {
      // The incoming params are Strings
      String param = request->getParam("state")->value();
      // .. so we have to interpret or cast them
      //state = ((param == "on") ? HIGH : LOW); // Look up Terary Operator (e.g. https://www.programiz.com/cpp-programming/ternary-operator)
      if (param == "Cturn") {
      state = 2;
       } else if (param == "CWturn") {
          state = 3;
        } else {
          state =0;
        }
    } else {
      state = 0;
    }

    // Send back message to human
    
   String stateString; // Declare the variable outside the if statement

    if(state == 2){
      stateString = "Cturn";      
    }else if (state == 3){  
      stateString = "CWturn";      
    }
     else{
      stateString = "notTurning";      
    } 
    
    
    //String stateString = state ? "turningC" : "turningCW";
    String responseJSON = "{\"motorState\":\"" + stateString + "\"}";
    sendResponse(request, responseJSON);

    // Operate LED
   // digitalWrite(led, state);

    if (state==HIGH){
      Serial.println("Turning motor");
    }
  });

  server.on("/params", HTTP_GET, [](AsyncWebServerRequest *request){
    int param1 = random(100);
    int param2 = random(100);
    int param3 = random(100);
    int param4 = random(100);

    String responseJSON = "{";
      responseJSON += "\"param1\":" + String(param1) + ",";
      responseJSON += "\"param2\":" + String(param2) + ",";
      responseJSON += "\"param3\":" + String(param3) + ",";
      responseJSON += "\"param4\":" + String(param4) + ",";
      responseJSON += "}";

      sendResponse(request,responseJSON);
            
  });

  // If human tries endpoint no exist, exec this function
  server.onNotFound(notFound);

  Serial.print("Starting web server on port ");
  Serial.println(webServerPort);
  server.begin();  
  
}

void loop() {
}

void moveClock(int steps){
  digitalWrite(DIR_PIN, LOW);
  for(int i=0; i<steps; i++){
    digitalWrite(STEP_PIN, HIGH);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(interval);
  }
}

void moveCounterClock(int steps){
  digitalWrite(DIR_PIN, HIGH);
  for(int i=0; i<steps; i++){
    digitalWrite(STEP_PIN, HIGH);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(interval);
  }
}
CODE: jQuery v2 [WORKING]

jQuery v2 [WORKING]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
  <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
</head>
<body>

    <header class="container mt-3">
      <div id="status-bar" class="alert alert-light" role="alert">
        API Status: <span id="status-api">Undefined</span>
      </div>
    </header>

    <main class="container">
      <h1>stepper control test</h1>
      <div id="motor-sign">Motor Rotation</div>
      <button id="button-counter-clockwise" class="btn btn-warning">Turn Counter-clockwise</button>
      <button id="button-clockwise" class="btn btn-info">Turn Clockwise</button>
    </main>
    
    <script src="lib/bootstrap/js/bootstrap.bundle.min.js"></script>
    <script src="lib/jquery/jquery-3.6.4.min.js"></script>
    <script>
      function checkAPIStatus(){
        $.ajax({
          url:"http://193.167.5.175/",
          timeout: 5000
        })
        .done(function(){
          $("#status-api").text("Connected");
          if($("#status-bar").hasClass("alert-light")){
            $("#status-bar").removeClass("alert-light");
          }
          $("#status-bar").addClass("alert-success");
        })
        .fail(function(){
          $("#status-api").text("Not connected");
        })
      }

      function triggerClockwise(){
        const myState = $("#motor-sign").data("motorState");
        console.log("sent: " + myState); 
        $.ajax({
          url:"http://193.167.5.175/motor",
          data: {
            state: myState
          },
          timeout: 5000
        })
        .done(function(response){
          const responseJSON = JSON.parse(response); //we convert this into an object so that:
          console.log("received: " + responseJSON.motorState);
          if (responseJSON.motorState == "Cturn"){
            $("#motor-sign").data("motorState", "CWturn");
            $("#motor-sign").css("background", "yellow");
            
           }else{
            $("#motor-sign").data("motorState", "Cturn");
            $("#motor-sign").css("background", "gray");
           }
        })
        .fail(function(){
          console.log("motor trigger call failed.");
        })
      }

      $(document).ready(function(){
        console.log("Document has loaded!");

        setInterval(checkAPIStatus,2000);

        $("#button-clockwise").click(triggerClockwise);
      });
    </script>
  </body>
</html>

Third Attempt

In my second attempt, I configured the buttons the way I wanted them. Only issue was, the .click function was not executing. I asked this to ChatGPT, which gave me the reply:

chatgpt reply

I made the necessary changes, and it worked.

CODE: Arduino IDE v3 [WORKING]

Arduino Code v3 [WORKING]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
  <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
</head>

<body>

  <header class="container mt-3">
    <div id="status-bar" class="alert alert-light" role="alert">
      API Status: <span id="status-api">Undefined</span>
    </div>
  </header>

  <main class="container">
    <h1>stepper control test</h1>
    <div id="motor-sign">Motor Rotation</div>
    <button id="button-counter-clockwise" class="btn btn-warning">Turn Counter-clockwise</button>
    <button id="button-clockwise" class="btn btn-info">Turn Clockwise</button>
  </main>

  <script src="lib/bootstrap/js/bootstrap.bundle.min.js"></script>
  <script src="lib/jquery/jquery-3.6.4.min.js"></script>
  <script>
    function checkAPIStatus() {
      $.ajax({
        url: "http://193.167.5.175/",
        timeout: 5000
      })
        .done(function () {
          $("#status-api").text("Connected");
          if ($("#status-bar").hasClass("alert-light")) {
            $("#status-bar").removeClass("alert-light");
          }
          $("#status-bar").addClass("alert-success");
        })
        .fail(function () {
          $("#status-api").text("Not connected");
        })
    }

    function triggerTurn(direction) {
      if (direction == 2) {
        $("#motor-sign").data("motorState", "CWturn");
      } else if (direction == 3) {
        $("#motor-sign").data("motorState", "CCWturn");
      } else {
        $("#motor-sign").data("motorState", "noAction");
      }

      const myState = $("#motor-sign").data("motorState");
      console.log("sent: " + myState);

      $.ajax({
        url: "http://193.167.5.175/motor",
        data: {
          state: myState
        },
        timeout: 5000
      })
        .done(function (response) {
          const responseJSON = JSON.parse(response); //we convert this into an object so that:
          console.log("received: " + responseJSON.motorState);
          if (responseJSON.motorState == "isTurningCW") {
            $("#motor-sign").css("background", "cyan");
          } else if (responseJSON.motorState == "isTurningCCW") {
            $("#motor-sign").css("background", "orange");
          } else {
            $("#motor-sign").css("background", "gray");
          }
        })
        .fail(function () {
          console.log("motor trigger call failed.");
        })
    }

    $(document).ready(function () {
      console.log("Document has loaded!");

      setInterval(checkAPIStatus, 2000);

      $("#button-clockwise").click(function () {
        triggerTurn(2);
      });

      $("#button-counter-clockwise").click(function () {
        triggerTurn(3);
      });
    });
  </script>
</body>

</html>

Final Working Example

To finalize the code, I made some adjustments and cleaned it up. Here is how it looks like when the system is working. I am able to send commands to the servos through the network interface, which executes the necessary actions, and sends back strings as a response.

CODE: Arduino IDE v4 [WORKING]

Arduino Code v4 [WORKING]

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebSrv.h>

// Some variables we will need along the way
const char *ssid = "Fablab";
const char *password = "Fabricationlab1";
const char *PARAM_MESSAGE = "message";
int webServerPort = 80;

int DIR_PIN = D5;
int STEP_PIN = D4;
int ENABLE_PIN = D6;
int POT_PIN = D1;
int potValue;
int adjustPot;
int prevAdjustPot = 0;
int interval = 3000;

// Setting up our webserver
AsyncWebServer server(webServerPort);

// This function will be called when human will try to access undefined endpoint
void notFound(AsyncWebServerRequest *request) {
  AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", "Not found");
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}

void sendResponse(AsyncWebServerRequest *request, String message) {
  AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", message);
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}

void setup() {

  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(ENABLE_PIN, OUTPUT);
  pinMode(POT_PIN, INPUT);
  digitalWrite(ENABLE_PIN, LOW);

  Serial.begin(19200);
  delay(10);

  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");

  // We want to know the IP address so we can send commands from our computer to the device
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Greet human when it tries to access the root / endpoint.
  // This is a good place to send some documentation about other calls available if you wish.
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    sendResponse(request, "Hello!");
  });

  server.on("/motor", HTTP_GET, [](AsyncWebServerRequest *request) {
    int state;  // motor state
    if (request->hasParam("state")) {
      // The incoming params are Strings
      String param = request->getParam("state")->value();
      // .. so we have to interpret or cast them
      if (param == "CWturn") {
        state = 2;
      } else if (param == "CCWturn") {
        state = 3;
      } else {
        state = 0;
      }
    } else {
      state = 0;
    }

    // Send back message to human

    String stateString;  // Declare the variable outside the if statement

    if (state == 2) {
      Serial.println("turningcw");
      moveClock(20);
      stateString = "isTurningCW";
    } else if (state == 3) {
      Serial.println("turningCCW");
      moveCounterClock(20);
      stateString = "isTurningCCW";
    } else {
      stateString = "notTurning";
    }

    String responseJSON = "{\"motorState\":\"" + stateString + "\"}";
    sendResponse(request, responseJSON);

  });

  server.on("/params", HTTP_GET, [](AsyncWebServerRequest *request) {
    int param1 = random(100);
    int param2 = random(100);
    int param3 = random(100);
    int param4 = random(100);

    String responseJSON = "{";
    responseJSON += "\"param1\":" + String(param1) + ",";
    responseJSON += "\"param2\":" + String(param2) + ",";
    responseJSON += "\"param3\":" + String(param3) + ",";
    responseJSON += "\"param4\":" + String(param4) + ",";
    responseJSON += "}";

    sendResponse(request, responseJSON);
  });

  // If human tries endpoint no exist, exec this function
  server.onNotFound(notFound);

  Serial.print("Starting web server on port ");
  Serial.println(webServerPort);
  server.begin();
}

void loop() {

}

void moveClock(int steps) {
  digitalWrite(DIR_PIN, LOW);
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN, HIGH);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(interval);
  }
}

void moveCounterClock(int steps) {
  digitalWrite(DIR_PIN, HIGH);
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN, HIGH);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(interval);
  }
}
CODE: jQuery v4 [WORKING]

jQuery Code v4 [WORKING]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BootStrap Rest DEMO</title>
  <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
</head>

<body>

  <header class="container mt-3">
    <div id="status-bar" class="alert alert-light" role="alert">
      API Status: <span id="status-api">Undefined</span>
    </div>
  </header>

  <main class="container">
    <h1>stepper control test</h1>
    <div id="motor-sign">Motor Rotation</div>
    <button id="button-counter-clockwise" class="btn btn-warning">Turn Counter-clockwise</button>
    <button id="button-clockwise" class="btn btn-info">Turn Clockwise</button>
  </main>

  <script src="lib/bootstrap/js/bootstrap.bundle.min.js"></script>
  <script src="lib/jquery/jquery-3.6.4.min.js"></script>
  <script>
    function checkAPIStatus() {
      $.ajax({
        url: "http://193.167.5.175/",
        timeout: 5000
      })
        .done(function () {
          $("#status-api").text("Connected");
          if ($("#status-bar").hasClass("alert-light")) {
            $("#status-bar").removeClass("alert-light");
          }
          $("#status-bar").addClass("alert-success");
        })
        .fail(function () {
          $("#status-api").text("Not connected");
        })
    }

    function triggerTurn(direction) {
      if (direction == 2) {
        $("#motor-sign").data("motorState", "CWturn");
      } else if (direction == 3) {
        $("#motor-sign").data("motorState", "CCWturn");
      } else {
        $("#motor-sign").data("motorState", "noAction");
      }

      const myState = $("#motor-sign").data("motorState");
      console.log("sent: " + myState);

      $.ajax({
        url: "http://193.167.5.175/motor",
        data: {
          state: myState
        },
        timeout: 5000
      })
        .done(function (response) {
          const responseJSON = JSON.parse(response); //we convert this into an object so that:
          console.log("received: " + responseJSON.motorState);
          if (responseJSON.motorState == "isTurningCW") {
            $("#motor-sign").css("background", "cyan");
          } else if (responseJSON.motorState == "isTurningCCW") {
            $("#motor-sign").css("background", "orange");
          } else {
            $("#motor-sign").css("background", "gray");
          }
        })
        .fail(function () {
          console.log("motor trigger call failed.");
        })
    }

    $(document).ready(function () {
      console.log("Document has loaded!");

      setInterval(checkAPIStatus, 2000);

      $("#button-clockwise").click(function () {
        triggerTurn(2);
      });

      $("#button-counter-clockwise").click(function () {
        triggerTurn(3);
      });
    });
  </script>
</body>

</html>

Motor Turn Interface

Motor Turning Hardware