Skip to content

14. Interface and application programming

Group assignment:

Compare as many tool options as possible

Individual assignment:

Write an application that interfaces a user with an input &/or output device that you made

Have you answered these questions?

  • Linked to the group assignment page and reflected what you learned individually of the group assignment.
  • Documented your process
  • Explained the UI that you made and how you did it
  • Outlined problems and how you fixed them
  • Included original code (or a screenshot of the app code if that’s not possible)
  • Included a ‘hero shot/video’ of your application running with your board

What I’ve done this week

  • Create UI and UX with HTML, CSS, and JavaScript

Group assignment:

Individual assignment:

Overview

I created a tester to adjust the distance of the stepping motor.



When the slider is moved, the stepping motor rotates according to the value, and the base of the trash separator moves.

The following image is an idea of final project sketch, and it serves as a tester to ensure that the trash boxes can be placed in the proper position.

Create UI and UX with HTML, CSS, and JS

This time, I built a web server with ESP32, and then built an interface with HTML, CSS, and JavaScript

I used the following site as a reference in creating the slider

  • Create UI and UX with HTML, CSS, and JS on ESP32 board

The board used in this assignment was created in week10.

Code

#include <WiFi.h>
#include <AccelStepper.h>

#define motorInterfaceType 1
const int dirPin1 = 22;
const int stepPin1 = 23;
const int stepSpeed = 1700;
const int stepsPerRevolution = 200;
AccelStepper myStepper1(motorInterfaceType, stepPin1, dirPin1);

const char* ssid     = "Env_WiFi";
const char* password = "Env_Pass";

WiFiServer server(80);

String header;

String valueString = String(5);
int pos1 = 0;
int pos2 = 0;

unsigned long currentTime = millis();

unsigned long previousTime = 0; 

const long timeoutTime = 2000;

void setup() {
  Serial.begin(115200);


  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.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   

  if (client) {                            
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");
    String currentLine = "";
    while (client.connected() && currentTime - previousTime <= timeoutTime) { 
      currentTime = millis();
      if (client.available()) { 
        char c = client.read(); 
        Serial.write(c); 
        header += c;
        if (c == '\n') {   

          if (currentLine.length() == 0) {

            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");


            client.println("<style>body { text-align: center; font-family: fantasy; Arial; margin-left:auto; margin-right:auto;}");
            client.println(".slider { width: 340px; }</style>");
            client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");


            client.println("</head><body><h1>Stepper Test</h1>");
            client.println("<h2>Stepper Position: <span id=\"stepperPos\"></span></h2>");          
            client.println("<input type=\"range\" class=\"input-range\" min=\"0\" max=\"3000\" class=\"slider\" id=\"stepperSlider\" onchange=\"stepper(this.value)\" value=\""+valueString+"\"/>");

            client.println("<script>var slider = document.getElementById(\"stepperSlider\");");
            client.println("var stepperP = document.getElementById(\"stepperPos\"); stepperP.innerHTML = slider.value;");
            client.println("slider.oninput = function() { slider.value = this.value; stepperP.innerHTML = this.value; }");
            client.println("$.ajaxSetup({timeout:1000}); function stepper(pos) { ");
            client.println("$.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>");

            client.println("</body></html>");     

            if(header.indexOf("GET /?value=")>=0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);

              myStepper1.setMaxSpeed(1000);
              myStepper1.setAcceleration(50);
              myStepper1.setSpeed(200);
              myStepper1.moveTo(valueString.toInt());
              myStepper1.runToPosition();
            }         

            client.println();

            break;
          } else { 
            currentLine = "";
          }
        } else if (c != '\r') {  
          currentLine += c; 
        }
      }
    }

    header = "";

    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Explanation of the codes

Use these libraries

#include <WiFi.h>
#include <AccelStepper.h>

Setting stepper motor parameter and create an instance for each stepper motor

#define motorInterfaceType 1
const int dirPin1 = 22;
const int stepPin1 = 23;
const int stepSpeed = 1700;
const int stepsPerRevolution = 200;
AccelStepper myStepper1(motorInterfaceType, stepPin1, dirPin1);

Setting Wifi and Password

const char* ssid     = "Env_WiFi";
const char* password = "Env_Pass";

Create an instance of the web server to process the web server with the name server. The port number is set to the common port 80

WiFiServer server(80);

Define a variable header of type String to store header information

String header;

Create a couple of variables that will be used to extract the slider position from the HTTP request.

String valueString = String(5);
int pos1 = 0;
int pos2 = 0;

Define current time, previous time and timeout. Variables needed to handle related “no response” in time when the server is overloaded

unsigned long currentTime = millis(); // Current time
unsigned long previousTime = 0; // Previous time
const long timeoutTime = 2000; // Define timeout time in milliseconds (example: 2000ms = 2s)

The data transfer rate for serial communication was specified at 115200 bps

Connect to Wi-Fi network with SSID and password

Continue to output a period to serial communication every 0.5 seconds until WiFi.status() becomes WL_CONNECTED.

void setup() {
  Serial.begin(115200);

  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
}

Print local IP address and start web server

  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();

Listen for incoming clients

If a new client connects, WiFiClient object is obtained by server.available().

WiFiClient client = server.available();
if (client) {
}

millis() record the time in ms since the microcontroller was started.

currentTime is the “current time” and previousTime is the “time when the connection was started.

Use these two variables to make a timeout later.

currentTime = millis();
previousTime = currentTime;

Print a message out in the serial port

Serial.println("New Client.");

Create a string variable to store the current row

String currentLine = ""; 

Processing is performed when the client is connected and the “current time” minus the “time the connection was initiated” is less than or equal to the timeout time.

while (client.connected() && currentTime - previousTime <= timeoutTime) {

Updating the “current time” increase the time taken for processing.

currentTime = millis();

If the string can be read from the client object, processing proceeds

if (client.available()) {

Reads one character from the client object, displays it on the serial monitor, and appends it to the end of the header string.

char c = client.read();
Serial.write(c);                              
header += c;

If the length of the currentLine string is 0 when a newline code is read, the process starts sending headers and HTML as an HTTP response.

HTTP requests and responses are delimited and terminated by sending an empty newline, so headers and HTML are delimited by sending an empty newline client.println();.

The HTTP response is terminated by sending an empty newline client.println();.

if (c == '\n') {  
    if (currentLine.length() == 0) {
        client.println("HTTP/1.1 200 OK");
        client.println("Content-type:text/html");
        client.println("Connection: close");
        client.println();

~~~~ HTML PART ~~~~

        client.println();

Display the web page Need to write HTML tags in client.println()

client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");


client.println("<style>body { text-align: center; font-family: fantasy; Arial; margin-left:auto; margin-right:auto;}");
client.println(".slider { width: 340px; }</style>");
client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");


client.println("</head><body><h1>Stepper Test</h1>");
client.println("<h2>Stepper Position: <span id=\"stepperPos\"></span></h2>");          
client.println("<input type=\"range\" class=\"input-range\" min=\"0\" max=\"3000\" class=\"slider\" id=\"stepperSlider\" onchange=\"stepper(this.value)\" value=\""+valueString+"\"/>");

client.println("<script>var slider = document.getElementById(\"stepperSlider\");");
client.println("var stepperP = document.getElementById(\"stepperPos\"); stepperP.innerHTML = slider.value;");
client.println("slider.oninput = function() { slider.value = this.value; stepperP.innerHTML = this.value; }");
client.println("$.ajaxSetup({timeout:1000}); function stepper(pos) { ");
client.println("$.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>");

client.println("</body></html>"); 

The following part of the code retrieves the slider value from the HTTP request.

indexOf returns the position of the string to be searched for if it exists in the string object, or -1 if not found.

if(header.indexOf("GET /?value=")>=0) {
  pos1 = header.indexOf('=');
  pos2 = header.indexOf('&');
  valueString = header.substring(pos1+1, pos2);

  myStepper1.setMaxSpeed(1000);
  myStepper1.setAcceleration(50);
  myStepper1.setSpeed(200);
  myStepper1.moveTo(valueString.toInt());
  myStepper1.runToPosition();
}  

When you move the slider, you make an HTTP request on the following URL, that contains the slider position between the = and & signs.

The slider position value is saved in the valueString variable.

Set the stepper to that specific position using myStepper1.moveTo(); with the valueString variable as an argument. The valueString variable is a string, so we need to use the toInt() method to convert it into an integer number – the data type accepted by the write() method.

myStepper1.moveTo(valueString.toInt());

Clear the header when the response is finished

header = "";

Terminate the connection with the client

client.stop();
Serial.println("Client disconnected.");
Serial.println("");

As for the HTML, CSS and JavaScript code, the following code has been added to the Arduino language

HTML

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" href="data:,">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  </head>
  <body>
    <h1>Stepper Test</h1>
    <p>Position: <span id="stepperPos"></span></p>
    <input type="range" min="0" max="3000" class="slider" id="stepperSlider" onchange="stepper(this.value)"/>
  </body>
</html>

Load jQuery.

jQuery allows you to write JavaScript in a simple way.

<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>

jQuery 3.3.1

Create a slider in HTML using input tag.

To define a slider, use the “type” attribute with the “range” value. In a slider, define the minimum and the maximum range using the “min” and “max” attributes.

onchange attribute to call the stepper function to send an HTTP request to the ESP32 when the slider moves.

<input type="range" min="0" max="3000" class="slider" id="stepperSlider" onchange="stepper(this.value)"/>

CSS

body {
  text-align: center;
  font-family: fantasy;
  margin-left:auto;
  margin-right:auto;
}
.slider {
  width: 340px;
}

JavaScript

var slider = document.getElementById("stepperSlider");
    var stepperP = document.getElementById("stepperPos");
    stepperP.innerHTML = slider.value;
    slider.oninput = function() {
      slider.value = this.value;
      stepperP.innerHTML = this.value;
    }

JQuery

$.ajaxSetup({timeout:1000});
function stepper(pos) {
  $.get("/?value=" + pos + "&");
}

make an HTTP GET request on the ESP IP address in this specific URL path /?value=[0 ~ 3000]&.

$.ajaxSetup({timeout:1000});
function stepper(pos) {
  $.get("/?value=" + pos + "&");
}

For example

When the slider is at 0 deg, you make an HTTP GET request on the following URL:

http://192.168.xx.xxx/?value=0&

when the slider is at 3000 deg, you make an HTTP GET request on the following URL:

http://192.168.xx.xxx/?value=3000&

Check the IP address on the serial monitor.

Open the above IP address in the browser, can see the following interface

When opened on a smartphone, it appear as follows

What I learned

This week I created a tester for stepping motor travel distance and stepper motor position adjustment.

I would like to continue learning to make the interface more convenient considering UI and UX.

Appendix


Last update: May 8, 2022