On this page

Running on Windows

Run aWOT on Windows to develop an HTTP API or browser interface without a board connected. The native example in .extras/WindowsNative ports the macOS example to Winsock and implements Arduino’s Client interface. The aWOT routes and request handlers use the same library sources.

This builds a regular Windows command-line program with MinGW GCC. You do not need Arduino IDE, a board package, or a network shield. The instructions below target 64-bit Intel/AMD Windows with the MSYS2 UCRT64 toolchain.

For a Linux computer, follow the native Linux guide.

Get the tools and source

Install MSYS2 and open its UCRT64 terminal. Install Git, the compiler, CMake, Ninja, and Python:

pacman -S --needed git mingw-w64-ucrt-x86_64-gcc \
  mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja \
  mingw-w64-ucrt-x86_64-python

Use the UCRT64 terminal for the following commands. The MinGW packages build native Windows executables; see MSYS2’s CMake instructions for this toolchain setup. The example supports MinGW GCC. The bundled Arduino API uses GNU attributes and has not been adapted for MSVC.

Clone the aWOT repository and stay in the repository root:

git clone https://github.com/lasselukkari/aWOT.git
cd aWOT

If you already have a checkout containing .extras/WindowsNative, use its root directory instead. Use a Git checkout for this example: .extras is omitted from library release archives and Arduino Library Manager packages.

Keep the example inside the checkout. CMake compiles aWOT from src/ and reuses the ArduinoCore-API sources bundled in .extras/ArduinoCore-API, shared by the native examples. No separate Arduino installation or download is required.

Hello world example

HelloWorld.cpp registers a route and handles each connection in its own thread:

/*
  HelloWorld.cpp - aWOT example using native Windows Winsock sockets

  Run HelloWorld.exe, then visit http://localhost:8080/.
*/

#include <aWOT.h>

#include <cstdlib>
#include <thread>
#include <utility>

#include "WindowsSocket.h"

App app;

void indexCmd(Request&, Response& res) {
  res.set("Content-Type", "text/html");
  res.print("<html>\n");
  res.print("<head>\n");
  res.print("<title>Hello World!</title>\n");
  res.print("</head>\n");
  res.print("<body>\n");
  res.print("<h1>Greetings middle earth!</h1>\n");
  res.print("</body>\n");
  res.print("</html>");
}

void handleClient(WindowsClient client) {
  app.process(&client);
  client.stop();
}

int main() {
  Serial.begin(115200);
  app.get("/", &indexCmd);
  WindowsServer server(8080);
  if (!server.begin()) return EXIT_FAILURE;
  Serial.println("Server listening on port 8080 (multithreaded)");
  Serial.flush();

  for (;;) {
    // Wait in select until a connection arrives, with no idle polling loop.
    WindowsClient client = server.available(-1);
    if (client.connected()) {
      std::thread(handleClient, std::move(client)).detach();
    }
  }
}

Build and run

From the repository root in the UCRT64 terminal, run:

cmake -S .extras/WindowsNative -B .extras/WindowsNative/build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build .extras/WindowsNative/build --parallel 4
./.extras/WindowsNative/build/HelloWorld.exe

CMake builds the example and its tests as C++11 programs. The server prints:

Server listening on port 8080 (multithreaded)

Leave this terminal running and open http://127.0.0.1:8080/ in your browser. The page displays Greetings middle earth!. The server listens on all IPv4 interfaces; 127.0.0.1 connects from the same Windows computer.

You can also inspect the response from a second terminal. In PowerShell, use curl.exe:

curl.exe -i http://127.0.0.1:8080/
curl.exe -i http://127.0.0.1:8080/missing

The first request returns 200 OK, a Content-Type of text/html, and the HTML page. Only GET / is registered, so /missing returns 404 Not Found. Press Control-C in the server terminal to stop it.

The executable can also run from PowerShell or Explorer after building. CMake links the GCC, C++, and thread runtimes statically, so the UCRT64 terminal is only needed for the build commands.

Use your own routes

Edit .extras/WindowsNative/HelloWorld.cpp to build your application. Register additional handlers in main(), before server.begin(), using app.get(), app.post(), or the other routing methods.

After editing, stop the running server, build again with cmake --build .extras/WindowsNative/build --parallel 4, and start the executable. Use middleware and request data with the same aWOT API as on a board. The compatibility layer supplies Arduino types, timing, formatting, and terminal output through Serial. It does not emulate GPIO or a Wi-Fi radio. Keep hardware-specific operations separate when sharing handlers with a board.

How the native server works

The Windows native source uses these pieces:

File or directoryPurpose
HelloWorld.cppRegisters routes, starts the listener on port 8080, and moves each connection into a worker thread.
WindowsSocket.h and WindowsSocket.cppImplement WindowsServer and WindowsClient with Winsock.
Arduino.h and Arduino.cppSupply the compatibility layer for timing, formatting, and terminal output.
../ArduinoCore-API/Supplies Arduino types such as Client, Stream, Print, and String, shared by the native examples.
CMakeLists.txtBuilds the adapter, example, aWOT sources, and tests, and links Winsock.
tests/Tests the socket adapter and the running HTTP server.

server.available(-1) waits for connections without an idle polling loop. Each accepted WindowsClient is moved into a worker thread, which calls app.process(&client) and then client.stop(). The example handles one request per connection and closes it afterward. See Connections before changing that lifecycle.

Route handlers can run concurrently. Register routes before accepting clients, and synchronize shared mutable state with a mutex or atomics. Give each concurrent request its own header-capture buffers and custom request and response storage, and synchronize access to any shared context.

WindowsClient copies share the SOCKET, connection status, and unread receive buffer. Destroying or reassigning a handle releases its reference; the last reference closes the socket with closesocket. Calling stop() on any copy closes the connection and clears buffered input for every copy. Reconnecting a handle closes its old shared connection and attaches that handle to a new one. Synchronize concurrent access to copies of the same connection.

The adapter manages WSAStartup and WSACleanup; each live connection retains Winsock initialization, including copies that outlive their listener. Reads use nonblocking sockets and a 1 KiB shared receive buffer, including for raw request.client() reads, so Arduino and aWOT can apply their read timeouts. Writes wait for backpressure to clear within the client’s timeout.

Outgoing WindowsClient::connect() calls accept numeric IPv4 addresses. DNS names and IPv6 connections are not implemented.

Use WsChannel<WindowsClient, N> or SseChannel<WindowsClient, N> to retain native connections. Adapt the Hello World worker to check ProcessResult.responseOpen: after a successful WebSocket upgrade, let the local client leave scope without calling stop(). For SSE, call events.add(client) after process() returns an open response, then let the local client leave scope. The channel’s copy keeps the connection alive. Close connections that were not handed to a channel.

Run the Windows checks

From the repository root in the UCRT64 terminal, run:

ctest --test-dir .extras/WindowsNative/build --output-on-failure

The suite runs three tests:

  • windows-shims: checks timers, concurrent timing calls, integer conversion, and floating-point formatting using the same assertions as the macOS example.
  • windows-sockets: uses real loopback TCP connections to check ownership, Winsock lifetime, buffered and nonblocking reads, disconnects, reconnection, timeouts, write backpressure, listener conflicts, and aWOT request processing.
  • windows-http: starts the built executable on port 8080 and checks status codes, content type, the Hello World body, missing routes, and concurrent requests. It also checks that startup fails on an occupied port, then terminates the test server.

Stop the example server before running the tests; port 8080 must be free. Each test has a bounded runtime. A successful run reports 100% tests passed, 0 tests failed out of 3. Python is only needed for the HTTP test; to build the application without tests, add -DBUILD_TESTING=OFF to the CMake configuration command.

The native Unit Tests workflow includes a Windows native example job on windows-latest. It builds and runs these checks on Windows for relevant pushes and pull requests. A Windows run exercises Microsoft’s Winsock implementation; cross-compiling only establishes that the code builds. Wine can provide an additional local check from macOS or Linux using the commands in the example README.

Library revision 26cc31a4 passed all three checks on the Windows runner on 25 October 2026, including the shared client ownership and channel regressions, in the hosted native CI run. The same checks also passed under Wine on macOS.

Troubleshooting

  • The example directory is missing: use a Git checkout containing .extras/WindowsNative; release archives omit .extras.
  • CMake rejects the toolchain: open the MSYS2 UCRT64 terminal and install the mingw-w64-ucrt-x86_64-* packages above. This project requires Windows MinGW. If you configured with another compiler, configure again using a new build directory so the previous CMake compiler selection is not reused.
  • Python is missing during configuration: install mingw-w64-ucrt-x86_64-python, or configure with -DBUILD_TESTING=OFF for an application-only build.
  • The server exits at startup: read the reported Winsock error and check whether another process uses port 8080. Stop that process before starting the example. Windows exclusive binding can keep a recently used port reserved while closed TCP connections finish; wait before restarting.
  • You still see the old response: stop the old process before rebuilding and restarting. Recompiling does not replace an already running server.

Continue with routing to organize a larger application, or Hello world on a board to use Wi-Fi or Ethernet.