Docs
How Plynx works
Plynx is three ordinary pieces: a C++ library on your microcontroller, a Java server you can run yourself, and a native iOS app. Nothing about it is exotic, and that is the point. This page explains what each piece does, what actually travels over the socket, and why the design looks the way it does.
Architecture
Firmware library. The Plynx C++ library (it speaks the classic Blynk Legacy protocol). Small enough for an ATmega328; the flash and RAM stay yours. It opens one TCP connection out to the server on port 8080 and keeps it for the life of the device. Everything the board sends or receives travels over that socket.
Server. The open source Blynk legacy server: a single Java jar built on Netty. No database; state and history are plain files under a data folder, which is why a Raspberry Pi Zero is enough. It authenticates sessions, routes frames between boards and apps that share a project, stores the last value of every pin, and keeps time series for charts. The free public instance at plynx.cc and a self-hosted one are interchangeable; the app treats both identically.
iOS app. A native Swift client. It connects to the server with a persistent TCP connection over TLS on port 9443 and keeps it open while a project is on screen, so updates are pushed to the phone, not polled. Every widget is bound to a pin. The protocol layer is a standalone Swift package, linked below.
The protocol
Plynx does not invent a protocol. It speaks the Blynk legacy protocol, a wire format that has been in production use for years, with open implementations on all three sides (linked below). The code is the specification, and here is the short version.
Framing
Every message is a binary frame with a fixed header, all integers big endian:
- command: 1 byte (
LOGIN= 2,PING= 6,HARDWARE= 20, …) - message id: 2 bytes, echoed by replies, wraps at 65535
- body length: 2 bytes on the hardware port, 4 bytes on the app port
- body: UTF-8 fields separated by null bytes
So a hardware frame has a 5 byte header, an app frame a 7 byte one. Replies
are RESPONSE frames (command 0x00): same message id, and a status code in
place of the length. 200 is OK.
Here is Plynx.virtualWrite(V1, 23.5) on the wire, 14 bytes total:
14 command: 0x14 HARDWARE
00 07 message id: 7
00 09 body length: 9
76 77 00 "vw" virtual write
31 00 "1" pin V1
32 33 2E 35 "23.5" the value, ASCII Connection and login
- The board opens a plain TCP connection to the server, port 8080, outbound.
- First frame:
LOGINwith the project’s auth token as the body. - Server replies
RESPONSE 200and marks the device online. The socket then stays open until power-off. - The app does the same on port 9443 over TLS, logging in with account credentials instead of a token.
Heartbeat
- Idle sides send
PING(empty body) roughly every 10 seconds. - The server drops a hardware connection after 10 seconds of silence
(
hard.socket.idle.timeout), so a dead link is detected in seconds, and the app flips the device dot to offline. - The library reconnects and re-authenticates on its own; your sketch keeps
calling
Plynx.run()and never sees any of this.
Virtual pins
The key abstraction. Alongside physical pins, the protocol defines up to 128 virtual pins (V0 to V127) that carry values instead of voltages. Your sketch writes a parsed sensor reading to V2; the app renders whatever widget is bound to V2. Virtual pins decouple the dashboard from the wiring, which is what lets one generic app drive any project without custom firmware per widget.
A write, end to end
Board to phone:
- Your sketch calls
Plynx.virtualWrite(V2, t): oneHARDWAREframe to the server. - The server stores the value (last state plus history for charts).
- It forwards the frame to every app session logged into that project; each open app redraws the widget on V2.
Phone to board is the mirror image: a button press sends a HARDWARE write
from the app, the server relays it down the device’s socket, and
PLYNX_WRITE(V1) runs in your sketch.
Two more mechanisms round it out. A board that reboots can call
Blynk.syncVirtual: a HARDWARE_SYNC frame asks the server to replay the
stored pin values, so state survives power cycles without the board
persisting anything. And events flow up as well: the board can raise a
notification, and if no app session is connected the server delivers it to
the phone through APNs as a real push notification.
Why not just an HTTP server on the ESP
A fair question, and the most common objection. An ESP32 can serve a web page over WiFi, so why add a server and an app in between? Because the direct approach stops being simple the moment you leave your desk:
- Reaching it from outside your LAN. A web server on the ESP is only reachable inside your network. Using it from anywhere means port forwarding, a static IP or dynamic DNS, and TLS certificates, on a microcontroller. The Plynx board makes an outbound connection, which works through NAT, CGNAT and hotel WiFi with zero router configuration, and nothing in your home accepts inbound traffic.
- Push notifications. An ESP cannot wake your phone. Notifications on iOS have to come through APNs, which requires a server holding an Apple certificate. “Freezer above -15 C” on your lock screen is not something a self-served web page can do.
- Flash and RAM. A useful embedded web UI (HTML, CSS, JS, TLS) costs real flash and RAM on the board. The Blynk client library is a thin protocol encoder; the UI lives on the phone where resources are free.
- More than one device. Each ESP web server is its own island with its own address. Plynx shows every board in one place, with state and history kept even while a board sleeps or reboots.
- Sharing access. Giving a family member access to a device is an account on the server, not a hole in your firewall.
The honest counterpoint: if you have exactly one device, only use it on your home LAN, and never need a notification, an HTTP server on the ESP is a perfectly good solution. Plynx exists for every case past that.
Open source
The protocol has three independent implementations you can read:
- PlynxConnector, the Swift protocol library the iOS app is built on
- plynx-server, the open source Java server (the self-hostable jar), a maintained fork of the discontinued legacy server
- plynx-library, the C++ firmware library for your board
The app itself sends no telemetry. The only connections it opens are to the servers you configure, and if that server is your own, your data never leaves your network.
Where to go next
Read getting started to go from a bare board to live data in about 15 minutes, or self-hosting on a Raspberry Pi to run the whole stack yourself.