Blog

What is the Room Server in MeshCore?

9/1/2025 3 min read

The Room Server in MeshCore functions like a group chat server with history. Imagine joining a chat room: your device (client) connects to the Room Server, sends messages to this "room," and receives the chat history upon connecting or after a break—if it's still available.

Here's how it works:

  • A client tells the server: "I know all messages up to time T" (this is the sync_since timestamp).
  • The server then automatically sends the client all newer messages posted since T.
  • Once a client is active, it receives new messages pushed directly to it without needing to ask.
  • Every message is acknowledged (ACK). Only after a successful ACK does the server update the client's timestamp. This keeps everything nicely synchronized, similar to a traditional chat – with one limitation: the server stores only a limited number of messages.

Where and how are messages stored?

Messages reside only in the RAM of the Room Server, specifically in a ring buffer. In the example implementation (simple_room_server), the buffer is limited to 32 messages (defined by MAX_UNSYNCED_POSTS 32). This means:

  • The server holds a maximum of the last 32 messages in memory.

  • There is no persistent storage (e.g., on flash or SD card). After a restart, all messages are lost. Why like this?

  • Microcontrollers often have limited RAM (sometimes only a few dozen kilobytes).

  • Each message requires space for elements like the public key, timestamp, text, and buffer.

  • A large buffer would overload RAM and slow down over-the-air synchronization because more data would need to be sent and confirmed.

Can more messages be stored?

Yes, technically it's possible by increasing the buffer size in the code (e.g., MAX_UNSYNCED_POSTS). But beware:

  • Larger buffer = more RAM usage and longer synchronization times since clients must catch up on more data when connecting.
  • On devices with limited RAM (e.g., nRF52 chips), this is risky – 32 messages is often the limit.
  • On more powerful devices like the ESP32 (with e.g., 512 kB RAM), you could increase the buffer to 64 or 128 messages. But this strains the radio network because more data must be transmitted. A good balance between buffer size, RAM, and network load is necessary.

How does synchronization work in detail?

Typical process:

  1. Client logs in: upon connection or via a "keep-alive" signal, the client provides its last known timestamp (sync_since).
  2. Server selects messages: it looks in the ring buffer for messages newer than sync_since and not sent by the client itself.
  3. Push with acknowledgment: the server sends each message individually to all active clients (in a round-robin fashion) and waits for an ACK. If no ACK is received, it retries a few times.
  4. Update timestamp: after a successful ACK, the server updates the client's sync_since timestamp so that next time the client only receives newer messages.
  5. Continuous sync: new messages from other clients are pushed immediately to all active clients so no one needs to keep asking. Problem with offline periods: If a client has been offline for a long time and its sync_since is far back, it receives only the last 32 messages (or the current buffer size). Older messages are lost.

Tips and pitfalls

  • Choose buffer size wisely: more messages mean more RAM and longer sync times. For low-end chips like nRF52: maximum 32 messages. For ESP32: larger buffers (e.g., 64 or 128) are possible but test impact on radio and latency.
  • No persistent storage: want permanent history? You'll need to implement your own solution, e.g., with flash memory.
  • Watch radio load: many unsynced messages = many push/ACK cycles = high radio network load. This can degrade performance. If you want to dive deeper into any detail, just let me know!