Arduino Uno R4 WiFi REST API Server with JSON

WebServerJson Example - REST API Server

Overview

This example demonstrates how to create a REST API server on Arduino Uno R4 WiFi that handles JSON requests and responses, perfect for modern web applications and mobile app backends.

Features

  • REST API endpoints with JSON request/response handling
  • POST request processing with JSON data parsing
  • GET endpoints for data retrieval
  • Professional JSON responses with proper HTTP status codes
  • Error handling with appropriate JSON error messages
  • CORS support for cross-origin requests

Hardware Required

1×Arduino UNO R4 WiFi
1×Alternatively, DIYables STEM V4 IoT
1×USB Cable Type-C
Disclosure: Some links in this section are Amazon affiliate links. If you make a purchase through these links, we may earn a commission at no extra cost to you.
Additionally, some links direct to products from our own brand, DIYables .

Library Installation

Follow these instructions step by step:

  • If this is your first time using the Arduino Uno R4 WiFi/Minima, refer to the tutorial on setting up the environment for Arduino Uno R4 WiFi/Minima in the Arduino IDE.
  • Connect the Arduino Uno R4 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.
  • Open the Library Manager by clicking on the Library Manager icon on the left side of the Arduino IDE.
  • Search for Web Server for Arduino Uno R4 WiFi and locate the mWebSockets by DIYables.
  • Click on the Install button to add the mWebSockets library.

https://arduinogetstarted.com/images/tutorial/arduino-uno-r4-web-server-library.jpg

Web Server Json Example

  • On Arduino IDE, Go to File Examples Web Server for Arduino Uno R4 WiFi WebServerJson example to open the example code

API Endpoints

GET /api/data

Returns a success message with timestamp in JSON format.

Response:

{ "status": "success", "message": "GET request received", "timestamp": 12345 }

The timestamp value represents milliseconds since the Arduino started (from millis() function).

POST /api/data

Accepts JSON data and returns the received key value.

Request:

{ "key": "your_value" }

Response:

{ "status": "success", "message": "Data received", "received_key": "your_value" }

Setup Instructions

1. Network Configuration

Edit the WiFi credentials in the WebServerJson.ino file:

const char WIFI_SSID[] = "YOUR_WIFI_SSID"; const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";

2. Upload Code and Monitor Output

  1. Connect your Arduino Uno R4 WiFi to your computer
  2. Select the correct board and port in Arduino IDE
  3. Upload the WebServerJson.ino sketch
  4. Open Serial Monitor (9600 baud)
  5. Wait for WiFi connection
  6. Note the IP address displayed
  7. If you do not see IP address in Serial monitor, press the reset button on the Arduino Uno R4 WiFi or DIYables STEM V4 IoT board

Usage

Testing with cURL

Replace your-arduino-ip with the actual IP address shown in your Serial Monitor.

Test GET Request
curl -X GET http://your-arduino-ip/api/data

Expected Output:

Command Prompt
C:\Users\youruser>curl -X GET http://your-arduino-ip/api/data { "status": "success", "message": "GET request received", "timestamp": 12345 }

The timestamp shows milliseconds since Arduino startup.

Test POST Request with JSON Data
curl -X POST http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test_value\"}"

Expected Output:

Command Prompt
C:\Users\youruser>curl -X POST http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test_value\"}" { "status": "success", "message": "Data received", "received_key": "test_value" }
Test POST Request with Different Data
curl -X POST http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"hello_world\"}"

Expected Output:

Command Prompt
C:\Users\youruser>curl -X POST http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"hello_world\"}" { "status": "success", "message": "Data received", "received_key": "hello_world" }
Test Invalid JSON (Error Response)
curl -X POST http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{invalid json}"

Expected Output:

Command Prompt
C:\Users\youruser>curl -X POST http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{invalid json}" { "status": "error", "message": "Invalid JSON" }
Test Missing JSON Body (Error Response)
curl -X POST http://your-arduino-ip/api/data

Expected Output:

Command Prompt
C:\Users\youruser>curl -X POST http://your-arduino-ip/api/data { "status": "error", "message": "No JSON data received" }
Test Unsupported Method (Error Response)
curl -X PUT http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test\"}"

Expected Output:

Command Prompt
C:\Users\youruser>curl -X PUT http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test\"}" { "status": "error", "message": "Method not allowed" }
Test Non-existent Endpoint (404 Error)
curl -X GET http://your-arduino-ip/api/nonexistent

Expected Output:

Command Prompt
C:\Users\youruser>curl -X GET http://your-arduino-ip/api/nonexistent <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>404 Not Found</title> </head> <body> <h1>404 - Page Not Found</h1> <p>Method: GET</p> <p>Sorry, we couldn't find that page!</p> <a href="/">Return to Home</a> </body> </html>

Testing with Postman

Test GET Request
  1. Create a new GET request
  2. Set URL to http://your-arduino-ip/api/data
  3. Send request
  4. Verify response contains status, message, and timestamp
Test POST Request
  1. Create a new POST request
  2. Set URL to http://your-arduino-ip/api/data
  3. Add header: Content-Type: application/json
  4. Add JSON body: {"key": "test_value"}
  5. Send request
  6. Verify response shows the received key value

Testing Error Responses

Test the error handling by sending invalid requests as shown in the example code:

Missing JSON Data
curl -X POST http://your-arduino-ip/api/data

Expected: {"status": "error","message": "No JSON data received"}

Invalid JSON Format
curl -X POST http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{invalid json}"

Expected: {"status": "error","message": "Invalid JSON"}

Missing Key Field
curl -X POST http://your-arduino-ip/api/data -H "Content-Type: application/json" -d "{\"other_field\": \"value\"}"

Expected: The key will default to "none" as per the example code: doc["key"] | "none"

Unsupported HTTP Method
curl -X DELETE http://your-arduino-ip/api/data

Expected: {"status": "error","message": "Method not allowed"}

Code Explanation

Route Configuration

// Configure API routes server.addRoute("/api/data", handleApiData);

Handler Function Signature

All handler functions must follow this signature:

void handleApiData(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { // Handler implementation }

Method Detection and JSON Processing

void handleApiData(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { Serial.print("[API] "); Serial.print(method); Serial.print(" request received"); if (method == "POST") { if (jsonData.length() == 0) { Serial.println("Error: No JSON data received"); client.println("HTTP/1.1 400 Bad Request"); client.println("Content-Type: application/json"); client.println("Connection: close"); client.println(); client.print("{\"status\": \"error\",\"message\": \"No JSON data received\"}"); return; } StaticJsonDocument<200> doc; DeserializationError error = deserializeJson(doc, jsonData); if (!error) { const char* key = doc["key"] | "none"; String response = JSON_RESPONSE; response.replace("%KEY%", key); server.sendResponse(client, response.c_str(), "application/json"); } else { client.println("HTTP/1.1 400 Bad Request"); client.println("Content-Type: application/json"); client.println("Connection: close"); client.println(); client.print("{\"status\": \"error\",\"message\": \"Invalid JSON\"}"); } } else if (method == "GET") { String response = JSON_GET_RESPONSE; response.replace("%TIMESTAMP%", String(millis())); server.sendResponse(client, response.c_str(), "application/json"); } else { client.println("HTTP/1.1 405 Method Not Allowed"); client.println("Content-Type: application/json"); client.println("Connection: close"); client.println(); client.print("{\"status\":\"error\",\"message\":\"Method not allowed\"}"); } }

Integration Examples

JavaScript Frontend

// Control LED async function controlLED(action) { try { const response = await fetch('http://your-arduino-ip/api/led', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ action: action }) }); const result = await response.json(); console.log('LED control result:', result); } catch (error) { console.error('Error:', error); } } // Get sensor data async function getSensorData() { try { const response = await fetch('http://your-arduino-ip/api/sensor'); const data = await response.json(); console.log('Sensor data:', data); } catch (error) { console.error('Error:', error); } }

Python Client

import requests import json

Control LED

def control_led(action): url = "http://your-arduino-ip/api/led" data = {"action": action} response = requests.post(url, json=data) return response.json()

Get status

def get_status(): url = "http://your-arduino-ip/api/status" response = requests.get(url) return response.json()

Usage

result = control_led("on") print(result) status = get_status() print(status)

Error Handling

HTTP Status Codes

  • 200: Success
  • 400: Bad Request (invalid JSON, missing parameters)
  • 404: Endpoint not found
  • 405: Method not allowed
  • 500: Internal server error

Error Response Format

Based on the actual example code, different errors return specific messages:

Missing JSON Data Error
{ "status": "error", "message": "No JSON data received" }

Returned when: POST request is sent without JSON body

Invalid JSON Format Error
{ "status": "error", "message": "Invalid JSON" }

Returned when: JSON data cannot be parsed (syntax errors)

Method Not Allowed Error
{ "status": "error", "message": "Method not allowed" }

Returned when: Using unsupported HTTP methods (PUT, DELETE, PATCH, etc.)

404 Not Found Error
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>404 Not Found</title> </head> <body> <h1>404 - Page Not Found</h1> <p>Method: [HTTP_METHOD]</p> <p>Sorry, we couldn't find that page!</p> <a href="/">Return to Home</a> </body> </html>

Returned when: Accessing non-existent endpoints

Customization

Adding New Handler Functions

// Create additional handler for a new endpoint void handleApiStatus(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { if (method == "GET") { String json = "{\"status\":\"online\",\"uptime\":" + String(millis() / 1000) + "}"; server.sendResponse(client, json.c_str(), "application/json"); } else { client.println("HTTP/1.1 405 Method Not Allowed"); client.println("Content-Type: application/json"); client.println("Connection: close"); client.println(); client.print("{\"status\":\"error\",\"message\":\"Method not allowed\"}"); } } // Register the new route in setup() server.addRoute("/api/status", handleApiStatus);

Template-Based JSON Responses

The example uses template strings for consistent JSON formatting:

const char JSON_RESPONSE[] PROGMEM = R"rawliteral( { "status": "success", "message": "Data received", "received_key": "%KEY%" } )rawliteral"; // Usage in handler String response = JSON_RESPONSE; response.replace("%KEY%", extractedValue); server.sendResponse(client, response.c_str(), "application/json");

Troubleshooting

Common Issues

Connection Issues

If you can't connect to the API endpoints:

  1. Verify your Arduino is connected to WiFi (check Serial Monitor)
  2. Ensure your client and Arduino are on the same network
  3. Check that the IP address is correct
  4. Verify the Arduino hasn't reset (which would change the IP)

JSON Parsing Errors

If you get "Invalid JSON" responses:

  1. Ensure Content-Type header is set to application/json
  2. Verify your JSON syntax is valid
  3. Check that special characters are properly escaped
  4. Make sure the JSON payload isn't too large (current limit: 200 bytes)

POST Request Issues

If POST requests return "No JSON data received":

  1. Verify you're sending a JSON body with the request
  2. Check that Content-Length header is set correctly
  3. Ensure the HTTP method is actually POST
  4. Test with a simple JSON like {"key": "test"}

Memory Issues

If the Arduino becomes unresponsive:

  1. Monitor memory usage - the StaticJsonDocument uses 200 bytes
  2. Reduce JSON_RESPONSE template size if needed
  3. Consider using DynamicJsonDocument for variable-sized data
  4. Check for memory leaks in custom handler functions

Performance Tips

Optimize JSON Processing

// Use appropriate document size for your data StaticJsonDocument<200> doc; // For small JSON objects StaticJsonDocument<1024> doc; // For larger JSON objects // Reuse string objects to reduce memory allocation String response; response.reserve(256); // Pre-allocate space response = JSON_RESPONSE; response.replace("%KEY%", value);

Efficient Response Handling

// Send responses directly for simple cases client.print(F("{\"status\":\"ok\",\"value\":")); client.print(sensorValue); client.print(F("}")); // Use PROGMEM for large static responses const char LARGE_RESPONSE[] PROGMEM = R"({ "status": "success", "data": { ... } })";

Next Steps

  • Explore WebServerQueryStrings.ino for URL parameter handling
  • Try WebServerWithWebSocket.ino for real-time communication
  • Consider integrating with databases or cloud services

Learning Resources

ARDUINO BUY RECOMMENDATION

Arduino UNO R3
Arduino Starter Kit

※ OUR MESSAGES