DIYables Web Apps Custom WebApp

Overview

This example provides a complete template for creating your own custom web applications that integrate seamlessly with the DIYables WebApps library.

Arduino Custom WebApp Example - Build Your Own Web Interface Tutorial

The CustomWebApp template is perfect for beginners who want to add their own web interface to the DIYables WebApps ecosystem! This tutorial shows you how to build a simple web page with real-time two-way data exchange between web browser and Arduino via WebSocket that can:

  • Send text messages from web browser to Arduino instantly via WebSocket
  • Receive messages from Arduino and display them in real-time on web page
  • Maintain persistent WebSocket connection for continuous communication
  • Auto-reconnect when WebSocket connection is lost
  • Work on mobile devices with responsive design

Designed for Arduino Uno R4 WiFi and DIYables STEM V4 IoT - this template integrates perfectly with existing DIYables web apps and provides the foundation for building your own custom IoT interfaces!

This template provides minimal code to get you started. Users can develop their own sophisticated web applications by modifying this template. Basic web programming knowledge (HTML, CSS, JavaScript) is recommended for customizing the web interface and adding advanced features.

What You'll Learn

  • How to create a custom web app that integrates with DIYables WebApps library
  • How to add your custom page to the DIYables web apps ecosystem
  • How to send text messages from web browser to Arduino
  • How to send data from Arduino to web browser
  • How to handle WebSocket connections and auto-reconnect
  • How to make mobile-responsive web interfaces
  • How to use the DIYables WebApps template system for rapid development

Features

  • DIYables WebApps Integration: Seamlessly integrates with the DIYables WebApps library ecosystem
  • Minimal Template Code: Provides a basic foundation that you can expand and customize
  • Template-Based Development: Complete starting point you can modify to create sophisticated applications
  • Simple Text Messaging: Send messages between web browser and Arduino
  • Auto-Reconnect: Automatically reconnects when connection is lost
  • Mobile Responsive: Works perfectly on phones, tablets, and computers
  • Beginner Friendly: Clean, simple code that's easy to understand
  • Extensible Framework: Requires basic web programming knowledge (HTML/CSS/JavaScript) for advanced customization
  • Platform Extensible: Currently implemented for Arduino Uno R4 WiFi, but can be extended for other hardware platforms. See DIYables_WebApps_ESP32

Hardware Preparation

1×Arduino UNO R4 WiFi
1×Alternatively, DIYables STEM V4 IoT
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×Recommended: Screw Terminal Block Shield for Arduino UNO R4
1×Recommended: Breadboard Shield for Arduino UNO R4
1×Recommended: Enclosure for Arduino UNO R4
1×Recommended: Power Splitter for Arduino UNO R4
1×Recommended: Prototyping Base Plate & Breadboard Kit for Arduino UNO

Or you can buy the following kits:

1×DIYables STEM V4 IoT Starter Kit (Arduino included)
1×DIYables Sensor Kit (30 sensors/displays)
1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

How to Start

You can start to build your custom web app for Arduino Uno R4/DIYables STEM V4 IoT board by going through the following main steps:

  • Run the Default Custom App Template on Your Arduino Board
  • Test and Verify the Default Custom Web App Works Correctly
  • Understand Communication Protocol and How It Works in Background
  • Modify the Template to Adapt Your Application
  • Manage Multiple Custom Web Apps - Essential Conflict Prevention Guide

Let's start one by one.

Run the Default Custom App Template on Your Arduino Board

Detailed Instructions

  • If this is your first time using the Arduino Uno R4 WiFi/DIYables STEM V4 IoT, refer to the tutorial on setting up the environment for Arduino Uno R4 WiFi/DIYables STEM V4 IoT in the Arduino IDE
  • Connect the Arduino Uno R4/DIYables STEM V4 IoT board to your computer using a USB cable
  • Launch the Arduino IDE on your computer
  • Select the appropriate Arduino Uno R4 board (e.g., Arduino Uno R4 WiFi) and COM port
  • Navigate to the Libraries icon on the left bar of the Arduino IDE
  • Search "DIYables WebApps", then find the DIYables WebApps library by DIYables
  • Click Install button to install the library
Arduino UNO R4 DIYables WebApps library
  • You will be asked for installing some other library dependencies
  • Click Install All button to install all library dependencies
Arduino UNO R4 DIYables WebApps dependency
  • In Arduino IDE, go to File > Examples > DIYables WebApps > CustomWebApp
  • You'll see 4 files that make up the complete custom web app template:
    • CustomWebApp.ino - Main Arduino code (this is where you add your custom logic!)
    • CustomWebApp.h - Header file (defines the interface to DIYables WebApps library)
    • CustomWebApp.cpp - Implementation file (handles integration with library framework)
    • custom_page_html.h - Web page design (customize your web interface here!)
  • Configure WiFi Settings by changing these lines in CustomWebApp.ino:
const char WIFI_SSID[] = "YOUR_WIFI_NAME"; const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";

Step 5: Upload and Test

  • Click Upload button on Arduino IDE to upload code to Arduino UNO R4/DIYables STEM V4 IoT
  • Open the Serial Monitor to see connection status
  • Note the IP address displayed in Serial Monitor
  • Open the Serial Monitor
  • Check out the result on Serial Monitor. It looks like the below
COM6
Send
Starting Custom WebApp... INFO: Added app / INFO: Added app /custom DIYables WebApp Library Platform: Arduino Uno R4 WiFi Network connected! IP address: 192.168.0.2 HTTP server started on port 80 Configuring WebSocket server callbacks... WebSocket server started on port 81 WebSocket URL: ws://192.168.0.2:81 WebSocket server started on port 81 ========================================== DIYables WebApp Ready! ========================================== 📱 Web Interface: http://192.168.0.2 🔗 WebSocket: ws://192.168.0.2:81 📋 Available Applications: 🏠 Home Page: http://192.168.0.2/ 🔧 Custom WebApp: http://192.168.0.2/custom ==========================================
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  
  • If you do not see anything, reboot Arduino board.
  • Take note of the IP address displayed, and enter this address into the address bar of a web browser on your smartphone or PC.
  • Example: http://192.168.0.2
  • You will see the home page like below image:
Arduino UNO R4 DIYables WebApp Home page with Web Custom app
  • Click to the Web Custom link, you will see the Web Custom app's UI like the below:
Arduino UNO R4 DIYables WebApp Web Custom app
  • Or you can also access the page directly by IP address followed by /custom. For example: http://[IP_ADDRESS]/custom

Test and Verify the Default Custom Web App Works Correctly

When you run the default CustomWebApp template, here's what you should see:

On the Web Interface:

  • Connection Status: Shows "Connected" in blue when WebSocket is active
  • Message Input Box: Text field to type messages to Arduino
  • Send Button: Click to send your message (or press Enter)
  • Arduino Messages Display: Shows messages received from Arduino in blue text

Arduino Behavior:

  • Echo Response: When you send "Hello" from web, Arduino responds with "Echo: Hello"
  • Periodic Updates: Arduino sends uptime messages every 5 seconds: "Arduino uptime: X seconds"
  • Serial Monitor: All received messages are logged for debugging

Test the Communication:

  1. Type a message in the input box (e.g., "test message")
  2. Click Send or press Enter
  3. Check Serial Monitor - you should see: "Received from web: test message"
  4. Check web page - you should see: "Echo: test message"
  5. Wait a few seconds - you'll see periodic uptime messages updated every 3 seconds (e.g., "Arduino uptime: 15 seconds", "Arduino uptime: 18 seconds", etc.)

Understand Communication Protocol and How It Works in Background

Understanding the internal mechanisms helps you customize the template effectively.

App Identifier System

The CustomWebApp template uses unique message tags (called "App Identifiers") that help Arduino code and web clients filter messages that belong to them. This is essential because your application may include multiple web apps, and each app needs to process only its own messages while ignoring others:

Arduino Side (CustomWebApp.h & CustomWebApp.cpp)

// In CustomWebApp.h class CustomWebAppPage : public DIYablesWebAppPageBase { private: // WebSocket message identifier for this custom app static const String APP_IDENTIFIER; // ... }; // In CustomWebApp.cpp const String CustomWebAppPage::APP_IDENTIFIER = "CUSTOM:"; // Usage in handleWebSocketMessage: if (message.startsWith(APP_IDENTIFIER)) { String payload = message.substring(APP_IDENTIFIER.length()); // Process clean payload without identifier } // Usage in sendToWeb: broadcastToAllClients(APP_IDENTIFIER + message);

JavaScript Side (custom_page_html.h)

// WebSocket message identifier for this custom app const APP_IDENTIFIER = 'CUSTOM:'; // Usage in receiving: if (event.data.startsWith(APP_IDENTIFIER)) { let message = event.data.substring(APP_IDENTIFIER.length); // Process clean message without identifier } // Usage in sending: ws.send(APP_IDENTIFIER + userInput);

Benefits of this design:

  • Single source of truth: Change identifier in one place per language
  • No magic strings: Eliminates hardcoded "CUSTOM:" throughout code
  • Type safety: Using constants prevents typos
  • Extensible: Easy to create multiple custom apps with different identifiers
  • Avoid data conflict between multiple apps: Each app uses unique identifier to prevent message interference
  • Professional: Follows object-oriented design principles

Important Notes:

  • You can keep the current identifier "CUSTOM:" when modifying this custom web app template to adapt to your project. However, when you create more than one custom app, make sure to change it to avoid conflicts.
  • If changing the identifier, make sure that the value in JavaScript (.h file) and Arduino code (.cpp file) are the same (e.g., both use "TEMP:" or both use "SENSOR:").
  • Pre-reserved identifiers by built-in apps: The following identifiers are already used by DIYables WebApps built-in applications and should be avoided:
    • Main app identifiers: "CHAT:", "MONITOR:", "PLOTTER:", "DIGITAL_PINS:", "JOYSTICK:", "SLIDER:", "TABLE:", "RTC:", "ROTATOR:", "GAUGE:"
    • Sub-protocol identifiers: "TIME:", "DATETIME:", "JOYSTICK_CONFIG:", "PLOTTER_DATA:", "PLOTTER_CONFIG:", "SLIDER_VALUES:", "TABLE_CONFIG:", "TABLE_DATA:", "VALUE_UPDATE:", "PIN_CONFIG:", "PIN_STATES:", "PIN_UPDATE:"

    Communication Flow

    From Web Page to Arduino:

    When you type a message on web interface and click send button, for example: Hello, the below flow happens:

    1. JavaScript adds identifier: JavaScript automatically appends the app identifier (which is "CUSTOM:" in this template) using APP_IDENTIFIER constant, then sends message to Arduino via WebSocket. The actual message sent is: CUSTOM:Hello
    2. DIYables WebApps library receives: The library receives the message CUSTOM:Hello and passes it to your CustomWebAppPage::handleWebSocketMessage method
    3. CustomWebAppPage class removes identifier: In handleWebSocketMessage, the CustomWebAppPage class checks if the message starts with its APP_IDENTIFIER, removes the identifier using .substring(APP_IDENTIFIER.length()), then passes the remaining message Hello by calling the callback function implemented in your .ino file
    4. Your application handles: Your application in the .ino file receives just Hello and can handle the message depending on your custom logic. The current template just prints it out and sends back a response

    From Arduino to Web Page:

    When your Arduino wants to send data to the web interface, for example: Temperature: 25°C, the below flow happens:

    1. Your application calls sendToWeb(): In your .ino file, you call customPage.sendToWeb("Temperature: 25°C") to send data to the web browser
    2. CustomWebAppPage class adds identifier and broadcasts: The CustomWebAppPage class automatically adds the app identifier using its APP_IDENTIFIER constant to your message and broadcasts CUSTOM:Temperature: 25°C to all connected web clients via WebSocket
    3. JavaScript receives and filters message: The web browser receives CUSTOM:Temperature: 25°C through the ws.onmessage event handler, but the JavaScript only processes messages starting with APP_IDENTIFIER and strips the identifier using .substring(APP_IDENTIFIER.length())
    4. Web page displays clean message: The current template displays the clean message Temperature: 25°C (without identifier) in the "Message from Arduino" section. You can customize the JavaScript to parse and display the data in different ways depending on your application needs

    Architecture Overview

    The CustomWebApp example consists of four main files:

    1. CustomWebApp.ino - Main Arduino sketch with your application logic
    2. CustomWebApp.h - Header file defining the custom page class (library interface)
    3. CustomWebApp.cpp - Implementation with communication logic (library code)
    4. custom_page_html.h - HTML interface separated for easy customization

Modify the Template to Adapt Your Application

The template is designed to be easily customizable for your specific needs. Here's how to adapt it:

1. Hardware Integration

Add Hardware Initialization

In CustomWebApp.ino setup() function:

void setup() { Serial.begin(9600); // Add your hardware initialization here pinMode(LED_BUILTIN, OUTPUT); // Built-in LED pinMode(3, OUTPUT); // PWM output pin pinMode(4, INPUT_PULLUP); // Button input with pullup pinMode(A0, INPUT); // Analog sensor input // Initialize sensors, displays, motors, etc. // servo.attach(9); // lcd.begin(16, 2); // Rest of setup... webAppsServer.addApp(&homePage); webAppsServer.addApp(&customPage); webAppsServer.begin(WIFI_SSID, WIFI_PASSWORD);

Handle Custom Commands

Extend the callback function to handle your custom commands:

customPage.onCustomMessageReceived([](const String& message) { Serial.println("Received: " + message); // LED Control if (message == "led_on") { digitalWrite(LED_BUILTIN, HIGH); customPage.sendToWeb("LED turned ON"); } else if (message == "led_off") { digitalWrite(LED_BUILTIN, LOW); customPage.sendToWeb("LED turned OFF"); } // Servo Control else if (message.startsWith("servo:")) { int angle = message.substring(6).toInt(); // Get number after "servo:" // servo.write(angle); customPage.sendToWeb("Servo moved to " + String(angle) + " degrees"); } // Sensor Reading Request else if (message == "get_temperature") { float temp = readTemperatureSensor(); // Your sensor function customPage.sendToWeb("Temperature: " + String(temp) + "°C"); } // Add more custom commands here });

Send Real-time Sensor Data

void loop() { webAppsServer.loop(); // Send sensor data every 3 seconds static unsigned long lastSend = 0; if (millis() - lastSend > 3000) { // Read your sensors int lightLevel = analogRead(A0); bool buttonPressed = !digitalRead(4); // Inverted due to pullup float temperature = readTemperatureSensor(); // Send to web interface customPage.sendToWeb("Light: " + String(lightLevel)); customPage.sendToWeb("Button: " + String(buttonPressed ? "Pressed" : "Released")); customPage.sendToWeb("Temp: " + String(temperature) + "°C"); lastSend = millis(); } }

2. Web Interface Customization

Modify HTML Layout

Edit the HTML in custom_page_html.h to change the interface:

<!-- Add new controls --> <div> <h3>🔌 Device Control</h3> <button onclick="send('led_on')">LED ON</button> <button onclick="send('led_off')">LED OFF</button> <br><br> <label>Servo Angle:</label> <input type="range" id="servoSlider" min="0" max="180" value="90" onchange="send('servo:' + this.value)"> <span id="servoValue">90°</span> </div> <div> <h3>📊 Sensor Data</h3> <div>Temperature: <span id="tempValue">--°C</span></div> <div>Light Level: <span id="lightValue">--</span></div> <div>Button Status: <span id="buttonValue">--</span></div> </div>

Customize JavaScript Processing

Update the ws.onmessage function to handle specific data types:

ws.onmessage = function(event) { if (event.data.startsWith(APP_IDENTIFIER)) { let message = event.data.substring(APP_IDENTIFIER.length); // Display all messages in general area document.getElementById('rawMessage').textContent = message; // Handle specific message types if (message.startsWith('Temperature:')) { let temp = message.split(':')[1].trim(); document.getElementById('tempValue').textContent = temp; } else if (message.startsWith('Light:')) { let light = message.split(':')[1].trim(); document.getElementById('lightValue').textContent = light; } else if (message.startsWith('Button:')) { let button = message.split(':')[1].trim(); document.getElementById('buttonValue').textContent = button; } } };

Add Styling

Customize the CSS for your application:

<style> .control-panel { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; padding: 20px; margin: 10px 0; color: white; } .sensor-display { background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 10px; padding: 15px; margin: 10px 0; } button { background: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 5px; margin: 5px; cursor: pointer; } button:hover { background: #0056b3; } </style>

Other Customization

Beyond modifying the web interface and hardware integration, you can also customize how your app appears in the DIYables WebApps ecosystem:

1. Customize App Path

You can change the URL path where your custom web app is accessible by modifying the constructor in your header file:

Default Path:

// In CustomWebApp.cpp - Default path is "/custom" CustomWebAppPage::CustomWebAppPage() : DIYablesWebAppPageBase("/custom") { }

Custom Path Examples:

// Temperature monitoring app CustomWebAppPage::CustomWebAppPage() : DIYablesWebAppPageBase("/new-path") { } // Accessible at: http://[IP_ADDRESS]/new-path

Important Notes:

  • Path must start with "/": Always begin your path with a forward slash
  • Use descriptive names: Choose paths that clearly describe your app's function
  • Avoid conflicts: Don't use paths already taken by built-in apps like /chat, /monitor, /plotter, etc.
  • Use lowercase and hyphens: Follow web URL conventions for better compatibility

2. Customize App Card on Home Page

You can customize how your app appears on the DIYables WebApps home page by modifying the getNavigationInfo() method in your implementation file:

Basic App Card:

// In CustomWebApp.cpp String CustomWebAppPage::getNavigationInfo() const { return "<a href=\"" + getPagePath() + "\" class=\"app-card\">" "<h3>🔧 Custom App</h3>" "<p>My custom web application</p>" "</a>"; }

Advanced App Card with Inline CSS:

// In CustomWebApp.cpp String CustomWebAppPage::getNavigationInfo() const { return "<a href=\"" + getPagePath() + "\" class=\"app-card\" " "style=\"background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);\">" "<h3>🌡️ Temperature Monitor</h3>" "<p>Real-time temperature monitoring</p>" "</a>"; }

Manage Multiple Custom Web Apps - Essential Conflict Prevention Guide

When developing multiple custom web applications, it's crucial to avoid conflicts between different apps. Let's suppose that we want to add three custom apps named "Temperature Monitor", "Motor Controller", and "Sensor Dashboard" to our Arduino project. Here's how to ensure they work together harmoniously:

1. Use Unique App Identifiers

Each custom web app must have a unique identifier to prevent message conflicts:

Example: Temperature Monitor App

// In TemperatureApp.cpp const String TemperatureAppPage::APP_IDENTIFIER = "TEMP:"; // JavaScript in temperature_page_html.h const APP_IDENTIFIER = 'TEMP:';

Example: Motor Controller App

// In MotorApp.cpp const String MotorAppPage::APP_IDENTIFIER = "MOTOR:"; // JavaScript in motor_page_html.h const APP_IDENTIFIER = 'MOTOR:';

Example: Sensor Dashboard App

// In SensorApp.cpp const String SensorAppPage::APP_IDENTIFIER = "SENSOR:"; // JavaScript in sensor_page_html.h const APP_IDENTIFIER = 'SENSOR:';

2. Use Unique Page Paths

Each web app needs a unique URL path:

// Temperature App TemperatureAppPage::TemperatureAppPage() : DIYablesWebAppPageBase("/temperature") { } // Motor Controller App MotorAppPage::MotorAppPage() : DIYablesWebAppPageBase("/motor") { } // Sensor Dashboard App SensorAppPage::SensorAppPage() : DIYablesWebAppPageBase("/sensors") { }

3. Use Unique Class Names

Avoid naming conflicts by using descriptive class names:

// Instead of multiple "CustomWebAppPage" classes class TemperatureMonitorPage : public DIYablesWebAppPageBase { }; class MotorControllerPage : public DIYablesWebAppPageBase { }; class SensorDashboardPage : public DIYablesWebAppPageBase { };

4. Organize Multiple Apps in One Project

Here's how to structure a project with multiple custom apps:

// In main .ino file #include "TemperatureApp.h" #include "MotorApp.h" #include "SensorApp.h" // Create instances DIYablesHomePage homePage; TemperatureMonitorPage tempPage; MotorControllerPage motorPage; SensorDashboardPage sensorPage; void setup() { // Add all pages to server webAppsServer.addApp(&homePage); // pre-built app webAppsServer.addApp(&tempPage); webAppsServer.addApp(&motorPage); webAppsServer.addApp(&sensorPage); webAppsServer.begin(WIFI_SSID, WIFI_PASSWORD); // Set up callbacks for each app tempPage.onTemperatureMessageReceived([](const String& message) { // Handle temperature app messages }); motorPage.onMotorMessageReceived([](const String& message) { // Handle motor app messages }); sensorPage.onSensorMessageReceived([](const String& message) { // Handle sensor app messages }); }

5. Best Practices for Multiple Apps

File Organization
MyProject/ ├── MyProject.ino // Main sketch ├── TemperatureApp.h // Temperature app header ├── TemperatureApp.cpp // Temperature app implementation ├── temperature_page_html.h // Temperature app web page ├── MotorApp.h // Motor app header ├── MotorApp.cpp // Motor app implementation ├── motor_page_html.h // Motor app web page ├── SensorApp.h // Sensor app header ├── SensorApp.cpp // Sensor app implementation └── sensor_page_html.h // Sensor app web page

Navigation Between Apps

Update the getNavigationInfo() method in each app to provide easy navigation:

String TemperatureMonitorPage::getNavigationInfo() const { return "<a href=\"" + getPagePath() + "\" class=\"app-card temperature\">" "<h3>🌡️ Temperature Monitor</h3>" "<p>View real-time temperature data</p>" "</a>"; } String MotorControllerPage::getNavigationInfo() const { return "<a href=\"" + getPagePath() + "\" class=\"app-card motor\">" "<h3>⚙️ Motor Controller</h3>" "<p>Control servo and stepper motors</p>" "</a>"; }

6. Testing Multiple Apps

When testing multiple apps:

  1. Test each app individually first
  2. Check Serial Monitor for message conflicts
  3. Verify unique identifiers are working correctly
  4. Test navigation between different apps
  5. Monitor memory usage with multiple apps loaded

By following these guidelines, you can create multiple custom web applications that work together seamlessly without interfering with each other or with other DIYables WebApps.

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!