---
title: Sending and Receiving Large Binary Objects
description: A guide to both sending and receiving large binary objects using the card.binary APIs.
source_url: https://dev.blues.io/guides-and-tutorials/notecard-guides/sending-and-receiving-large-binary-objects/
canonical_url: https://dev.blues.io/guides-and-tutorials/notecard-guides/sending-and-receiving-large-binary-objects/
markdown_url: https://dev.blues.io/guides-and-tutorials/notecard-guides/sending-and-receiving-large-binary-objects.md
---

# Sending and Receiving Large Binary Objects

While Notecard is designed to be a low-bandwidth wireless device, it is also possible to sync large binary payloads with the cloud.

This is accomplished by storing raw binary data in a reserved area on the Notecard, and then having Notecard send that large block directly to Notehub. Likewise, Notecard and Notehub can work together to get a binary payload from a remote endpoint and save it to the reserved area on the Notecard.

> **Note:**
>
> **Important Considerations When Syncing Large Binary Objects**
>
> 1. In your app design, it's safe to assume the maximum space available for data in the binary storage area on the Notecard is 100KB. The exact available space (in bytes) is returned in the `max` field in response to a [card.binary](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-binary) request.
>
>    If the total size of the binary data you are sending is > than `max`, you will need to "flush" the storage with the appropriate `web.post` request each time that limit is reached (see examples below), and then reassemble the binary data after it has been routed to your cloud.
>
> 2. Notehub charges one [event credit](https://dev.blues.io/api-reference/glossary.md#event-credit) for each megabyte of data uploaded via web transactions.
>
> 3. The `card.binary` and `card.binary.put` APIs are **not** supported on Notecard for LoRa, which does not include a binary storage area.

> **Tip:**
>
> **Let AI write your firmware.** Blues Expert MCP connects your AI coding assistant (Claude Code, GitHub Copilot, Cursor) directly to our API docs, providing live request validation and firmware best practices for Arduino, C, Zephyr, and Python. [Install the Blues Expert MCP →](https://dev.blues.io/tools-and-sdks/generative-ai-tools/blues-expert-mcp.md)

## Sending Large Binary Objects

Sending a large binary object from the Notecard involves two steps:

1. [Storing the binary payload](#storing-binary-data-on-notecard) in Notecard's reserved binary buffer.
2. [Syncing that buffer with Notehub](#syncing-binary-data-to-notehub) so it can be routed to your cloud endpoint.

These two steps are independent, so you can mix and match any storing method with any syncing method. Most applications pair the SDK helpers with a `web.post` request.

### Storing Binary Data on Notecard

You have two paths to choose from when populating Notecard's binary buffer. They both build on the [`card.binary`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-binary) and [`card.binary.put`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-binary-put) APIs, but the `note-arduino` SDK provides helper methods to ease the process.

1. [Storing Binary Data with the note-arduino SDK](#storing-binary-data-with-the-note-arduino-sdk) (Recommended)
2. [Storing Binary Data with the card.binary APIs](#storing-binary-data-with-the-card-binary-apis)

#### Storing Binary Data with the note-arduino SDK

Due to the complexities of using the `card.binary` APIs directly, the recommended path is to use the helper methods provided in the [note-arduino SDK](https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library.md).

> **Note:**
>
> The following Arduino examples demonstrate storing binary data with `note-arduino`: [basic binary data example](https://github.com/blues/note-arduino/blob/master/examples/Example8_BinarySendReceive/Example8_BinarySendReceive.ino) and [sending a large binary payload in chunks](https://github.com/blues/note-arduino/blob/master/examples/Example9_BinarySendReceiveChunked/Example9_BinarySendReceiveChunked.ino).

##### Storing a Single Binary Fragment

For small binary payloads (e.g. `<= 8 KB`), you can store the entire payload in a single fragment without the need to split and reassemble it on your cloud endpoint.

1. Define the binary data and use the `NoteBinaryStoreTransmit()` function to store the data in the reserved binary space on the Notecard. The fourth argument is the offset into the Notecard's binary area where the data should be written (0 for a single-fragment upload).

   ```c
   char buff[25] = "Hello World";
   NoteBinaryStoreTransmit((uint8_t *) buff, strlen(buff), sizeof(buff), 0);
   ```

2. Once the buffer is populated, continue on to [Syncing Binary Data to Notehub](#syncing-binary-data-to-notehub).

##### Storing Multiple Binary Fragments

For larger binary payloads, you may need to split the payload into multiple smaller fragments and reassemble them on your cloud endpoint.

1. Define the size of the binary payload fragments to send to the Notecard.

   ```c
   #define CHUNK_SIZE 4096
   uint8_t temp_buffer[CHUNK_SIZE + 128];
   ```

2. Specify the binary array and length of the binary array from your binary object and send the binary payload to the Notecard in `CHUNK_SIZE` fragments.

   > **Note:**
   >
   > The binary buffer requires additional overhead, so the buffer can be encoded in place. If you wish to know the exact requirements of your binary payload, you may use `NoteBinaryCodecMaxEncodedLength()`. In this example, an arbitrary overhead was specified.

   ```c
   const uint8_t * img_map = big_img_map;
   const size_t img_len = big_img_len;

   int i = 0;
   size_t bytes_left = img_len;
   while (bytes_left) {
     notecard.logDebugf("\nSending chunk %d, offset: %d...\n", i, i * CHUNK_SIZE);
     size_t bytes_to_send = bytes_left >= CHUNK_SIZE ? CHUNK_SIZE : bytes_left;
     memcpy(temp_buffer, img_map + i * CHUNK_SIZE, bytes_to_send);

     const char *err = NoteBinaryStoreTransmit((uint8_t *)temp_buffer, bytes_to_send, sizeof(temp_buffer), i * CHUNK_SIZE);
        
     if (!err) {
       bytes_left -= bytes_to_send;
       i++;
     }
   }
   ```

3. Once the buffer is populated, continue on to [Syncing Binary Data to Notehub](#syncing-binary-data-to-notehub).

#### Storing Binary Data with the card.binary APIs

As an alternative to using the SDK helpers, you can populate the binary buffer directly with the `card.binary` and `card.binary.put` APIs. This path requires you to handle COBS encoding and MD5 verification yourself.

1. Issue a `card.binary` request to the Notecard to verify the available space (`max`) is larger than the size of the binary payload you want to store.

   ```unknown
   {"req":"card.binary"}
   ```

   ```unknown
   {"max":130554}
   ```

2. Calculate the MD5 checksum of the binary payload.

3. COBS-encode the binary payload. The [note-c](https://blues.github.io/note-c/) library (the core C library that also powers `note-arduino`) includes a `NoteBinaryCodecEncode` function to simplify this process.

4. Calculate the length of the new COBS-encoded payload.

5. Append a newline character to the COBS-encoded payload (`\n`).

6. Send a `card.binary.put` request to the Notecard with the MD5 checksum in the `status` argument and the length of the payload in the `cobs` argument.

   Use the `offset` argument if you are supplying multiple payloads in succession, where the current `offset` is the index location of where the previous ended.

   ```unknown
   {
     "req": "card.binary.put",
     "cobs": 5,
     "status": "ce6fdef565eeecf14ab38d83643b922d"
   }
   ```

7. At this point, the Notecard is in a state where it expects the next input to be binary data, not a JSON-formatted API request. Send it the COBS-encoded payload.

   ```unknown
   000011110110100101100101011010000111100100001010
   ```

8. Next, you can optionally send a `card.binary` request to check for errors and verify the binary data was properly saved to the Notecard by checking the MD5 checksum:

   ```unknown
   {"req":"card.binary"}
   ```

   ```unknown
   {
    "connected": true,
    "max": 130554,
    "status": "ce6fdef565eeecf14ab38d83643b922d",
    "length": 4,
    "cobs": 5
   }
   ```

   If an error occurs on the transfer it will appear in the `err` field:

   ```unknown
   {"err":"md5 mismatch","max":130554}
   ```

9. Once the buffer is populated, continue on to [Syncing Binary Data to Notehub](#syncing-binary-data-to-notehub).

### Syncing Binary Data to Notehub

After storing binary data on the Notecard, you have two options for transmitting the buffer to Notehub:

- [web.post](#syncing-binary-data-with-web-post), which sends the buffer to a [Proxy for Notecard Web Requests](https://dev.blues.io/notecard/notecard-walkthrough/web-transactions.md) route.
- [note.add](#syncing-binary-data-with-note-add), which attaches the buffer to a Note and delivers it through a standard Notehub route.

#### Syncing Binary Data with web.post

1. Issue a `web.post` request with the `"binary":true` and `content` (the appropriate MIME type) arguments supplied. This tells Notecard to send all the data in the binary buffer to the specified proxy route in Notehub.

   > **Note:**
   >
   > Consult the [Web Transactions docs](https://dev.blues.io/notecard/notecard-walkthrough/web-transactions.md) for detailed information on using the `web.post` API and proxy routes (noting the Notecard must be connected and in `continuous` mode).

   ```unknown
   {
     "req": "web.post",
     "route": "PostBinaryDataRoute",
     "binary": true,
     "verify": true,
     "content": "application/octet-stream"
   }
   ```

   ```unknown
   {"result":200}
   ```

   Here is the equivalent request in C using the `note-arduino` SDK:

   ```c
   if (J *req = NoteNewRequest("web.post")) {
     JAddStringToObject(req, "route", "PostImageRoute");
     JAddStringToObject(req, "content", "images/jpeg");
     JAddBoolToObject(req, "binary", true);
     JAddBoolToObject(req, "verify", true);

     if (!NoteRequest(req)) {
       NoteDebug("Error sending image\n");
       delay(15000);
     }
   }
   ```

2. After the `web.post` is complete, reset the binary buffer on Notecard by sending a `card.binary` request with the `"delete":true` argument:

   ```unknown
   {
     "req": "card.binary",
     "delete": true
   }
   ```

   ```unknown
   {"max":130554}
   ```

   Or, with the `note-arduino` SDK, call `NoteBinaryStoreReset()`:

   ```c
   NoteBinaryStoreReset();
   ```

#### Syncing Binary Data with note.add

As an alternative to `web.post`, you can transmit the contents of the binary buffer by attaching it to a Note using a [note.add](https://dev.blues.io/api-reference/notecard-api/note-requests.md#note-add) request with the `"binary":true` and `"live":true` arguments. This allows the binary payload to flow through a standard Notefile sync and Notehub route.

> **Note:**
>
> When using `"binary":true` with `note.add`, the `"live":true` argument is **required**. The `live` argument tells Notecard to bypass saving the Note to flash, since the binary buffer itself is not stored in the Notefile on the Notecard.

1. Issue a `note.add` request with `"binary":true` and `"live":true`, specifying the Notefile (for example, `binary.qo`) that your Notehub route is configured to filter on.

   ```unknown
   {
     "req": "note.add",
     "file": "binary.qo",
     "binary": true,
     "live": true
   }
   ```

   ```unknown
   {"total":1}
   ```

   Here is the equivalent request in C using the `note-arduino` SDK:

   ```c
   if (J *req = NoteNewRequest("note.add")) {
     JAddStringToObject(req, "file", "binary.qo");
     JAddBoolToObject(req, "binary", true);
     JAddBoolToObject(req, "live", true);
     NoteRequest(req);
   }
   ```

2. When Notecard next syncs with Notehub, the contents of the binary buffer will be delivered as the payload of the resulting event on the specified Notefile. After the sync has completed, reset the binary buffer on the Notecard before storing the next payload by sending a `card.binary` request with the `"delete":true` argument:

   ```unknown
   {
     "req": "card.binary",
     "delete": true
   }
   ```

   ```unknown
   {"max":130554}
   ```

   Or, with the `note-arduino` SDK, call `NoteBinaryStoreReset()`:

   ```c
   NoteBinaryStoreReset();
   ```

> **Warning:**
>
> If you plan to route binary payloads to external services, be aware that Notehub removes any payload larger than 256 bytes from the stored event after the event has been successfully routed.
>
> After that point, the stored event in Notehub no longer includes the original payload. If you need to access these payloads later, make sure your route persists them when it first receives them.

## Receiving Large Binary Objects

Receiving a large binary object on Notecard involves two steps:

1. [Syncing the binary payload](#syncing-binary-data-from-notehub) from Notehub into Notecard's reserved binary buffer.
2. [Reading that buffer](#reading-binary-data-from-notecard) from your host microcontroller.

These two steps are independent, so you can choose how to read the buffer regardless of how it was populated. Most applications pair a `web.get` request with the SDK helpers.

### Syncing Binary Data from Notehub

Before reading, you first need to get the binary payload into the Notecard's binary buffer by issuing a `web.get` request with the `"binary":true` argument.

1. Issue a `card.binary` request to the Notecard to verify the available space (`max`) is larger than the size of the binary payload you expect to download.

   ```unknown
   {"req":"card.binary"}
   ```

   ```unknown
   {"max":130554}
   ```

2. Send a `web.get` request to the specified [Notehub proxy route](https://dev.blues.io/notecard/notecard-walkthrough/web-transactions.md) with the `"binary":true` and `content` (the appropriate MIME type) arguments supplied, which requests that the response be placed in the Notecard's binary buffer.

   > **Note:**
   >
   > Consult the [Web Transactions docs](https://dev.blues.io/notecard/notecard-walkthrough/web-transactions.md) for detailed information on using the `web.get` API and proxy routes (noting the Notecard must be connected and in `continuous` mode).

   ```unknown
   {
     "req": "web.get",
     "route": "GetBinaryDataRoute",
     "binary": true,
     "content": "application/octet-stream"
   }
   ```

   ```unknown
   {
    "result": 200,
    "length": 78179,
    "cobs": 78194,
    "body": {}
   }
   ```

   Here is the equivalent request in C using the `note-arduino` SDK:

   ```c
   if (J *req = NoteNewRequest("web.get")) {
     JAddStringToObject(req, "route", "GetImageRoute");
     JAddStringToObject(req, "content", "images/jpeg");
     JAddBoolToObject(req, "binary", true);

     if (!NoteRequest(req)) {
       NoteDebug("Error receiving image\n");
     }
   }
   ```

3. Next, you can send a `card.binary` request to verify the binary data was properly saved to the Notecard, noting the MD5 checksum returned in the `status` field is computed *before* COBS-encoding, and therefore does not include the `\n`.

   ```unknown
   {"req":"card.binary"}
   ```

   ```unknown
   {
    "connected": true,
    "max": 130554,
    "status": "c381abe19c96870db6d73fb4d670ef25",
    "length": 78179,
    "cobs": 78194
   }
   ```

### Reading Binary Data from Notecard

You have two paths to choose from when reading the binary buffer on your host. They both use the [`card.binary`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-binary) and [`card.binary.get`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-binary-get) APIs, but the `note-arduino` SDK provides helper methods to ease the process.

1. [Reading Binary Data with the note-arduino SDK](#reading-binary-data-with-the-note-arduino-sdk) (Recommended)
2. [Reading Binary Data with the card.binary APIs](#reading-binary-data-with-the-card-binary-apis)

#### Reading Binary Data with the note-arduino SDK

Due to the complexities of using the `card.binary` APIs directly, the recommended path is to use the helper methods provided in the `note-arduino` SDK.

> **Note:**
>
> The following Arduino examples demonstrate receiving binary data with `note-arduino`: [basic binary data example](https://github.com/blues/note-arduino/blob/master/examples/Example8_BinarySendReceive/Example8_BinarySendReceive.ino) and [receiving a large binary payload in chunks](https://github.com/blues/note-arduino/blob/master/examples/Example9_BinarySendReceiveChunked/Example9_BinarySendReceiveChunked.ino).

1. Get the decoded length of the downloaded binary data via a call to `NoteBinaryStoreDecodedLength()`:

   ```c
   uint32_t buffer_len = 0;
   NoteBinaryStoreDecodedLength(&buffer_len);
   ```

2. Call `NoteBinaryStoreReceive()` to verify and decode the binary data. The third and fourth arguments are the decoded-byte offset and decoded length to retrieve — pass `0` and the full `buffer_len` to fetch the entire payload.

   ```c
   uint8_t * my_binary_data = (uint8_t *)malloc(buffer_len);
   NoteBinaryStoreReceive(my_binary_data, buffer_len, 0, buffer_len);
   ```

3. Clear the binary buffer on the Notecard after the host has handled the binary data.

   ```c
   NoteBinaryStoreReset();
   ```

#### Reading Binary Data with the card.binary APIs

As an alternative to using the SDK helpers, you can read from the binary buffer directly with the `card.binary.get` API. This path requires you to handle COBS decoding yourself.

1. Send a `card.binary.get` request to Notecard to fetch the binary data:

   ```unknown
   {"req":"card.binary.get"}
   ```

   ```unknown
   {"status":"39f66921b9fb84a0400a1579e3dd3210"}
   ```

   Binary data will immediately follow this response. It can be fetched by reading until the `\n` character is encountered.

2. COBS-decode the binary data. If you're working in C or C++, the [note-c](https://blues.github.io/note-c/) library (the core C library that also powers `note-arduino`) includes a `NoteBinaryCodecDecode` function to simplify this process.

3. After successfully retrieving the binary data, clear the binary buffer on the Notecard.

   ```unknown
   {"req":"card.binary", "delete":true}
   ```

   ```unknown
   {"max":130554}
   ```

   Or, with the `note-arduino` SDK, call `NoteBinaryStoreReset()`:

   ```c
   NoteBinaryStoreReset();
   ```

## Binary Uploads with Web APIs

Using Notecard's [binary storage area](#sending-large-binary-objects) is the recommended path for most binary uploads. As an alternative, the `web.post` API accepts base64-encoded `payload` fragments that Notehub reassembles before invoking your route, delivering a single payload to your cloud endpoint.

You may opt to utilize this alternative binary data upload path when your payload exceeds the binary buffer on the Notecard. The `card.binary` path is capped at the Notecard's reserved binary area (i.e. `max` in the `card.binary` response, typically \~100KB). Larger payloads require multiple buffer flushes and reassembly on your cloud endpoint, while fragment uploads let Notehub handle reassembly before routing.

> **Note:**
>
> There are some tradeoffs to be aware of when using this method:
>
> - Base64 encoding adds \~33% bandwidth overhead per fragment compared to the raw binary sent by the `card.binary` path.
> - Your host must manage fragment sizing, offsets, and per-fragment MD5s manually.
> - This is a *synchronous* path as the Notecard must be connected and in `continuous` mode, the same as the other `web.*` approaches.
> - The maximum recommended size of each fragment depends on the type and quality of your network connection. A safe range for most scenarios is 4–8 KB.

### Sending Binary Fragments

Your host will split the binary payload into fragments and send them in successive `web.post` requests. Each request must set the `"content": "application/octet-stream"` argument and include the following additional arguments so Notehub can verify each fragment and place it correctly in the reassembled payload:

- `total` - The total size of the reassembled payload, in raw (pre-base64) bytes.
- `offset` - The byte offset of this fragment within the reassembled payload, in raw (pre-base64) bytes.
- `status` - A 32-character hex-encoded MD5 sum of the fragment's bytes, used by Notehub to verify each fragment on receipt.
- `verify` - Set to `true` to request verification from Notehub once the fragment is received. Automatically set to `true` when `status` is supplied.

1. Send the first fragment of your payload with `offset: 0`. The example below shows the first fragment of an 8191-byte payload:

   **JSON**

   ```json
   {
     "req": "web.post",
     "route": "SensorService",
     "content": "application/octet-stream",
     "payload": "<base64-encoded first 600 raw bytes>",
     "status": "<hex-encoded md5 of those 600 bytes>",
     "offset": 0,
     "total": 8191
   }
   ```

   **C/C++**

   ```cpp
   J *req = NoteNewRequest("web.post");
   JAddStringToObject(req, "route", "SensorService");
   JAddStringToObject(req, "content", "application/octet-stream");
   JAddStringToObject(req, "payload", "<base64-encoded first 600 raw bytes>");
   JAddStringToObject(req, "status", "<hex-encoded md5 of those 600 bytes>");
   JAddNumberToObject(req, "offset", 0);
   JAddNumberToObject(req, "total", 8191);

   NoteRequest(req);
   ```

   **Python**

   ```python
   req = {"req": "web.post"}
   req["route"] = "SensorService"
   req["content"] = "application/octet-stream"
   req["payload"] = "<base64-encoded first 600 raw bytes>"
   req["status"] = "<hex-encoded md5 of those 600 bytes>"
   req["offset"] = 0
   req["total"] = 8191

   rsp = card.Transaction(req)
   ```

2. Send each subsequent fragment, advancing `offset` by the raw byte count of the prior fragment. For example, after sending 600 bytes, the next fragment uses `offset: 600`:

   **JSON**

   ```json
   {
     "req": "web.post",
     "route": "SensorService",
     "content": "application/octet-stream",
     "payload": "<base64-encoded next 600 raw bytes>",
     "status": "<hex-encoded md5 of those 600 bytes>",
     "offset": 600,
     "total": 8191
   }
   ```

   **C/C++**

   ```cpp
   J *req = NoteNewRequest("web.post");
   JAddStringToObject(req, "route", "SensorService");
   JAddStringToObject(req, "content", "application/octet-stream");
   JAddStringToObject(req, "payload", "<base64-encoded next 600 raw bytes>");
   JAddStringToObject(req, "status", "<hex-encoded md5 of those 600 bytes>");
   JAddNumberToObject(req, "offset", 600);
   JAddNumberToObject(req, "total", 8191);

   NoteRequest(req);
   ```

   **Python**

   ```python
   req = {"req": "web.post"}
   req["route"] = "SensorService"
   req["content"] = "application/octet-stream"
   req["payload"] = "<base64-encoded next 600 raw bytes>"
   req["status"] = "<hex-encoded md5 of those 600 bytes>"
   req["offset"] = 600
   req["total"] = 8191

   rsp = card.Transaction(req)
   ```

3. Continue sending fragments until the sum of fragment sizes reaches `total`. When the final fragment arrives, Notehub reassembles the complete payload, invokes the proxy route, and returns the route's HTTP response to the Notecard:

   ```unknown
   {
     "req": "web.post",
     "route": "SensorService",
     "content": "application/octet-stream",
     "payload": "<base64-encoded final fragment>",
     "status": "<hex-encoded md5 of final fragment>",
     "offset": 7800,
     "total": 8191
   }
   ```

   ```unknown
   {"result":200}
   ```

   If a fragment fails MD5 verification, Notehub returns an `err` field in the response so the host can retransmit that fragment.

## Additional Resources

- [card.binary APIs](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-binary)
- [Notecard Web Transactions](https://dev.blues.io/notecard/notecard-walkthrough/web-transactions.md)
