<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Episode 8: Motor control]]></title><description><![CDATA[<p dir="auto">I will be using a dedicated motor driver IC to control the direction and speed of a DC motor via a virtual joystick widget.</p>
<p dir="auto"><strong>I will be using GUI-O Bluetooth Low Energy (LE) connection, but the example can be easily be ported to other connection types.</strong></p>
<p dir="auto"><strong>Software prerequisites:</strong></p>
<ul>
<li>
<p dir="auto">Arduino IDE (<a href="https://www.arduino.cc/en/software" rel="nofollow ugc">https://www.arduino.cc/en/software</a>)</p>
</li>
<li>
<p dir="auto">ESP32 Arduino board support, if using ESP32 based board (see <a href="https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html" rel="nofollow ugc">https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html</a>)</p>
</li>
<li>
<p dir="auto">GUI-O design tool (<a href="https://www.gui-o.com/design-tool" rel="nofollow ugc">https://www.gui-o.com/design-tool</a>)</p>
</li>
<li>
<p dir="auto">GUI-O application (<a href="https://play.google.com/store/apps/details?id=com.guio.guioapp" rel="nofollow ugc">https://play.google.com/store/apps/details?id=com.guio.guioapp</a>)</p>
</li>
<li>
<p dir="auto">For additional information about the GUI-O application, download the developer manual from <a href="https://www.gui-o.com/" rel="nofollow ugc">https://www.gui-o.com/</a></p>
</li>
</ul>
<p dir="auto"><strong>Components needed:</strong></p>
<ul>
<li>ESP32-WROOM-32 (or any other Arduino supported Bluetooth Low Energy capable board)</li>
<li>L239D motor driver</li>
<li>Small DC motor</li>
</ul>
<p dir="auto"><strong>The entire tutorial is split into various steps. All necessary information is given in each step</strong>.</p>
<h3>0. DESIGN THE GUI (optional)</h3>
<p dir="auto">The best way to create a GUI layout is to use GUI-O live designer tool.</p>
<p dir="auto">Note that the Arduino source code already includes the necessary commands, so this step is not needed, unless you want to make some visual adjustments. If you make adjustments, please include the generated ASCII code in the Arduino source code (see section 1. UPLOAD THE SOURCE CODE).</p>
<p dir="auto">First, you need to establish a TCP/IP connection between the designer tool and GUI-O application:</p>
<ol>
<li>Determine the local IP address of your PC's network interface (WiFi or Ethernet)</li>
</ol>
<ul>
<li>Under Windows, open the command prompt, enter <code>ipconfig</code> and press Enter</li>
<li>Under Linux, open the terminal, enter <code>ifconfig</code> and press Enter</li>
</ul>
<ol start="2">
<li>
<p dir="auto">Open GUI-O application and open settings menu. Select "Connections -&gt; Ethernet" and create a new device with IP address (determined from 1.) and any port between 49152 - 65535</p>
</li>
<li>
<p dir="auto">Open GUI-O designer and select "TCP/IP connection" tab. Set the IP address and port. Both values must match the device settings created within the GUI-O application. Click "Start server" button.</p>
</li>
<li>
<p dir="auto">Within the GUI-O application, tap the created device and wait for successful connection.</p>
</li>
<li>
<p dir="auto">In the GUI-O designer, select "File -&gt; Load designer file" and load the <a href="https://drive.google.com/uc?export=download&amp;id=1DkjQxhmYRP648PWpWkkzIkkKQ9fP2m4h" rel="nofollow ugc">MotorControl.gdf</a> design file. Make the desired adjustments, if necessary. Copy / replace the GUI-O commands into the Arduino source code (see section 1. UPLOAD THE SOURCE CODE).</p>
</li>
</ol>
<h3>1. CONNECT THE COMPONENTS</h3>
<p dir="auto">Connecting the components is straightforward. "D25" pin is used to set the motor speed (via PWM output), while pins "D33" and "D32" are used to control the motor direction.</p>
<p dir="auto"><img src="https://i.imgur.com/7K6T1A3l.png" alt="schematic.png" class=" img-fluid img-markdown" /></p>
<h3>2. UPLOAD THE SOURCE CODE</h3>
<p dir="auto">The source code has inline comments, describing the important parts of the code. You can copy the source code from the snippet below, or download it <a href="https://drive.google.com/uc?export=download&amp;id=1Qpz56xL1MwUeeys3x-ClOHt7XciMWqO1" rel="nofollow ugc">here</a>.</p>
<p dir="auto">Upload the code to your board (make sure that the correct board and upload port are selected). Reset the board after upload.</p>
<pre><code>/*
 * GUI-O Motor control Bluetooth example (using ESP32-WROOM-32)
 *
 * Copyright (C) 2022, kl3m3n
 * last updated on 3.12.2022
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include &lt;BLEDevice.h&gt;
#include &lt;BLEServer.h&gt;
#include &lt;BLEUtils.h&gt;
#include &lt;BLE2902.h&gt;

namespace uuid {
  static const char *SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
  static const char *RX_CHARACTERISTIC_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
  static const char *TX_CHARACTERISTIC_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
} // namespace uuid

// forward declare parser for incoming messages
void parseGuioMsg(const String &amp;msg);

// setup done flag
bool setupDone = false;

// setup motor control pins
const int enablePin = 25;
const int inPin_1 = 32;
const int inPin_2 = 33;
// for controlling motor speed...
const double PWM_FREQ = 1000.0;
const uint8_t PWM_BITS = 8;
const uint8_t PWM_CHANNEL = 0;

// custom handling of server callbacks
class CustomBLEServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      Serial.println("Connected!");
    };
    void onDisconnect(BLEServer* pServer) {
      Serial.println("Disconnected!");
      
      // restart advertising after disconnect, otherwise GUI-O cannot re-connect
      if(setupDone) {
        // restart advertising on disconnect
        delay(500);
        pServer-&gt;startAdvertising(); 
      }
    }
};

// custom handling of characteristic callbacks
class CustomBLECharacteristicCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pCharacteristic) {
    std::string msg = pCharacteristic-&gt;getValue();
    
    // parse message string
    parseGuioMsg(String(msg.c_str()));
  }      
};

// global ptr
BLECharacteristic *pTxCharacteristic;

void setup() {
  // debug output
  Serial.begin(115200);

  // motor control setup (speed, direction)
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_BITS);
  ledcAttachPin(enablePin, PWM_CHANNEL);
  pinMode(inPin_1, OUTPUT);
  pinMode(inPin_2, OUTPUT);

  // create device
  BLEDevice::init("MotorControl");
  // create server and register callback
  BLEServer *pServer = BLEDevice::createServer();
  pServer-&gt;setCallbacks(new CustomBLEServerCallbacks());
  // create service
  BLEService *pService = pServer-&gt;createService(uuid::SERVICE_UUID);

  // crate Tx characteristic and add descriptor
  pTxCharacteristic = pService-&gt;createCharacteristic(uuid::TX_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_NOTIFY);
  pTxCharacteristic-&gt;addDescriptor(new BLE2902());
  
  // crate Rx characteristic and register callback
  BLECharacteristic *pRxCharacteristic = pService-&gt;createCharacteristic(uuid::RX_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
  pRxCharacteristic-&gt;setCallbacks(new CustomBLECharacteristicCallbacks());

  // start the service and start advertising
  pService-&gt;start();
  pServer-&gt;getAdvertising()-&gt;start();

  // setup done flag
  setupDone = true;
}

void loop() {
  
}

void sendMsg(const String &amp;msg) {
  pTxCharacteristic-&gt;setValue(std::string(msg.c_str()));
  pTxCharacteristic-&gt;notify();
  delay(50);
}

void parseGuioMsg(const String &amp;msg) {
  if(msg.startsWith("@init")) {
    Serial.println("GUI-O app is requesting INITIALIZATION!");

    // clear screen and set background
    sendMsg("@cls\r\n");
    sendMsg("@guis BGC:#FFFFFF\r\n");
    delay(100);

    // initialize GUI
    sendMsg("|LB UID:title X:50 Y:20 FSZ:4 FFA:\"font8\" TXT:\"Motor&lt;br&gt;control\"\r\n");
    sendMsg("|JOY UID:ctrl X:50 Y:50 W:60 BGC:#00FFFFFF SHE:1 SHC:#806D9DC5 BTH:0.25\r\n");
    sendMsg("|LB UID:dir X:50 Y:75 FGC:#000000 SHE:1 FSZ:5 TXT:\"n/a\"\r\n");
    sendMsg("|LB UID:details X:50 Y:90 FSZ:2 TXT:\"GUI-O motor control&lt;br&gt;demonstration by kl3m3n\"\r\n");
  }
  else if(msg.startsWith("@ctrl")) {
    const auto s_idx = msg.indexOf(' ');
    const auto e_idx = msg.lastIndexOf(' ');
    
    if(s_idx &gt; 0 &amp;&amp; e_idx &gt; 0) {
      String xDirStr = msg.substring(s_idx + 1, e_idx); xDirStr.trim();
      // "convert" values between 0.0 to 1.0 / -1.0 to percent 0 to 100 / -100 (two decimal places, round...)
      const int8_t motorSpeed = static_cast&lt;int8_t&gt;(roundf(xDirStr.toFloat() * 100.0));

      Serial.print("Motor direction: ");
      Serial.print(motorSpeed &gt; 0 ? "CW" : "CCW");
      Serial.print(", speed [%]: ");
      Serial.println(abs(motorSpeed));
      
      // determine direction
      if(motorSpeed &gt; 0) {
        digitalWrite(inPin_1, HIGH);
        digitalWrite(inPin_2, LOW);  
        // update label
        sendMsg("@dir TXT:\"CW\"\r\n");
      }
      else if(motorSpeed &lt; 0) {
        digitalWrite(inPin_1, LOW);
        digitalWrite(inPin_2, HIGH);
        // update label
        sendMsg("@dir TXT:\"CCW\"\r\n");
      }
      else
        sendMsg("@dir TXT:\"OFF\"\r\n");      

      // map motor speed to duty cycle for speed control
      ledcWrite(PWM_CHANNEL, map(abs(motorSpeed), 0, 100, 0, 255));
    }    
  }    
}
</code></pre>
<h3>3. ESTABLISH CONNECTION</h3>
<p dir="auto">Open GUI-O application and navigate to settings menu. Select "Connections -&gt; Bluetooth LE and IoT" and search for devices (enable Bluetooth and Location services, if prompted). Tap on the "MotorControl" device, select <strong>Nordic UART service</strong> and wait for successful connection (confirm device pairing if prompted).</p>
<p dir="auto">Close the settings menu and press the <strong>Initialize</strong> button (see image below) from the GUI-O application home screen.</p>
<p dir="auto"><img src="https://i.imgur.com/DDEHAR7l.jpg" alt="initialize_button.jpg" class=" img-fluid img-markdown" /></p>
<h3>4. THE RESULT</h3>
<p dir="auto">Image below shows the result (screen capture) on my Android device after pressing the "Initialize" button. Motor direction and speed is controlled by the virtual joystick position (only in horizontal direction). If the joystick is pushed all the way to the right (or left), the speed of the motor is at its maximum. When in this position, pushing the joystick up or down decreases the speed of the motor.</p>
<p dir="auto"><img src="https://i.imgur.com/FijciOSl.jpg" alt="screen.jpg" class=" img-fluid img-markdown" /></p>
<hr />
<p dir="auto">If you have any questions or run into any problems, please let me know!</p>
<p dir="auto">Best regards,<br />
kl3m3n</p>
]]></description><link>https://forum.gui-o.com/topic/93/episode-8-motor-control</link><generator>RSS for Node</generator><lastBuildDate>Sat, 14 Mar 2026 22:48:07 GMT</lastBuildDate><atom:link href="https://forum.gui-o.com/topic/93.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 04 Dec 2022 11:29:22 GMT</pubDate><ttl>60</ttl></channel></rss>