---
title: C Library
description: A guide for installing and using the Notecard C library, known as note-c, on any microcontroller or platform with a C compiler.
source_url: https://dev.blues.io/tools-and-sdks/firmware-libraries/c-library/
canonical_url: https://dev.blues.io/tools-and-sdks/firmware-libraries/c-library/
markdown_url: https://dev.blues.io/tools-and-sdks/firmware-libraries/c-library.md
---

# C Library

[note-c](https://github.com/blues/note-c) is the official C library for communicating with the Notecard over serial or I2C. It is written in portable C, has no mandatory platform dependencies, and runs anywhere you have a C or C++ compiler—from resource-constrained microcontrollers to embedded Linux.

note-c is also the foundation for the other Blues C-based libraries. [note-arduino](https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library.md), [note-zephyr](https://dev.blues.io/tools-and-sdks/firmware-libraries/zephyr-library.md), and [note-espidf](https://dev.blues.io/tools-and-sdks/firmware-libraries/esp-idf-library.md) all wrap note-c and supply the platform-specific pieces for you. If you're on one of those platforms, start there. Use note-c directly when you're working with a vendor HAL (such as STM32Cube), a bare-metal project, an RTOS Blues doesn't have a port for, or any other environment where you need full control over how the library talks to your hardware.

## Installation

note-c isn't distributed through a package manager. Instead, you add its source to your project directly. The library is made up of `note.h`, plus every `.c` and `.h` file in the root of the repository that starts with `n_`.

**Copy the Source**

Download or clone the [note-c repository](https://github.com/blues/note-c), and copy the `note.h` file and all of the `n_*.c` and `n_*.h` files into your project alongside your other source files.

```bash
git clone https://github.com/blues/note-c.git
cp note-c/note.h note-c/n_*.c note-c/n_*.h path/to/your/project/src/
```

Make sure the directory containing the `.h` files is on your compiler's include path, and that the `.c` files are compiled and linked with the rest of your application.

**CMake**

note-c ships with a `CMakeLists.txt` that exposes a static library target named `note_c_lib`. If you've cloned the repository into your project, you can link against it like any other CMake target.

```plaintext
add_subdirectory(lib/note-c)
target_link_libraries(my_app PRIVATE note_c_lib)
```

The target carries note-c's include path and platform compile definitions for you, so there is nothing else to configure.

## Usage

To use note-c, include `note.h` at the top of any source file that talks to the Notecard. The header is C++ safe, so it works unchanged in `.c` and `.cpp` files.

```c
#include "note.h"
```

Unlike the Arduino and Python libraries, note-c doesn't know how to talk to your hardware on its own. It is deliberately platform-neutral, and expects your application to supply a handful of small callback functions, which note-c calls "hooks", for memory allocation, timing, and the serial or I2C peripheral wired to the Notecard. Once those hooks are registered, every other part of the library works the same on every platform.

Getting started therefore has two steps:

1. [Initialize the library](#library-initialization) by registering the hooks for your platform.
2. [Send Notecard requests](#sending-notecard-requests) using the [Notecard API](https://dev.blues.io/api-reference/notecard-api.md).

### Library Initialization

#### Memory and Timing Hooks

Every port must call `NoteSetFn()` to give note-c functions for allocating memory, freeing memory, delaying for a number of milliseconds, and reading a millisecond counter. On most platforms these map directly to libc and your HAL.

```c
// malloc and free come from libc. HAL_Delay and HAL_GetTick are from the
// STM32 HAL, but any functions with matching signatures will do.
NoteSetFn(malloc, free, HAL_Delay, HAL_GetTick);
```

The required signatures are:

```c
void *mallocFn(size_t size);
void freeFn(void *mem);
void delayMsFn(uint32_t ms);
uint32_t getMsFn(void);
```

#### Serial Configuration

To communicate over serial, call `NoteSetFnSerial()` with four hooks: one to reset the serial peripheral, one to transmit a buffer, one to check whether a byte is waiting to be read, and one to read a single byte.

```c
bool noteSerialReset(void);
void noteSerialTransmit(uint8_t *buf, size_t len, bool flush);
bool noteSerialAvailable(void);
char noteSerialReceive(void);

NoteSetFnSerial(noteSerialReset, noteSerialTransmit,
                noteSerialAvailable, noteSerialReceive);
```

The Notecard's serial interface runs at 9600 baud by default. Your reset hook should (re)initialize the UART at that rate; note-c calls it before the first transaction and again whenever it detects an I/O error.

#### I2C Configuration

To communicate over I2C, call `NoteSetFnI2C()` with the Notecard's I2C address, the maximum number of bytes to send in a single I2C transaction, and three hooks: one to reset the I2C peripheral, one to transmit a buffer, and one to receive a buffer.

```c
bool noteI2CReset(uint16_t addr);
const char *noteI2CTransmit(uint16_t addr, uint8_t *buf, uint16_t len);
const char *noteI2CReceive(uint16_t addr, uint8_t *buf, uint16_t len,
                           uint32_t *available);

NoteSetFnI2C(NOTE_I2C_ADDR_DEFAULT, NOTE_I2C_MTU_DEFAULT,
             noteI2CReset, noteI2CTransmit, noteI2CReceive);
```

Pass `NOTE_I2C_ADDR_DEFAULT` (`0x17`) and `NOTE_I2C_MTU_DEFAULT` unless you've [changed the Notecard's I2C address](https://dev.blues.io/notecard/notecard-walkthrough/advanced-notecard-configuration.md#change-the-notecard-i2c-address) or your platform's I2C driver has a buffer that can't handle the default transaction size.

The I2C hooks are a little more involved than the serial ones, because the Notecard uses a variable-length [serial-over-I2C protocol](https://dev.blues.io/guides-and-tutorials/notecard-guides/serial-over-i2c-protocol.md) rather than a fixed register map. In short, every write is prefixed with a length byte, and every read starts with a two-byte request followed by a two-byte header in the response. The [STM32 example](#examples) below shows a complete implementation.

> **Note:**
>
> note-c keeps one active interface at a time. Calling `NoteSetFnSerial()` makes serial the active interface, and calling `NoteSetFnI2C()` makes I2C the active interface. You can register both and switch between them with `NoteSetActiveInterface()`.

#### Mutex Hooks

If more than one task or thread in your application talks to the Notecard, register lock and unlock hooks with `NoteSetFnNoteMutex()`. note-c calls the lock hook before every transaction and the unlock hook after, so one task can't interrupt another mid-request.

```c
void lockNotecard(void);
void unlockNotecard(void);

NoteSetFnNoteMutex(lockNotecard, unlockNotecard);
```

If other devices share the Notecard's I2C bus, you can also register a pair of hooks with `NoteSetFnI2CMutex()` that note-c calls around each I2C transfer. Single-threaded applications can skip both.

#### Logging

note-c can log the raw JSON it exchanges with the Notecard, along with warnings and errors, but it needs you to tell it where that output should go. Register a debug output hook with `NoteSetFnDebugOutput()`. The hook receives a C string and returns the number of bytes it wrote.

```c
size_t noteDebugOutput(const char *text) {
    // Write to whatever you use for console output. For example, on
    // POSIX systems:
    return fputs(text, stdout) >= 0 ? strlen(text) : 0;
}

NoteSetFnDebugOutput(noteDebugOutput);
```

note-c has four log levels: `NOTE_C_LOG_LEVEL_ERROR`, `NOTE_C_LOG_LEVEL_WARN`, `NOTE_C_LOG_LEVEL_INFO` (the default, which includes every JSON request and response), and `NOTE_C_LOG_LEVEL_DEBUG`. You can change the level at runtime with `NoteSetLogLevel()`, or set the compile-time default by passing `-DNOTE_C_LOG_LEVEL=<n>` to your compiler, where `<n>` is `0` for errors only through `3` for everything.

```c
// Only log warnings and errors
NoteSetLogLevel(NOTE_C_LOG_LEVEL_WARN);
```

You can also write to the same output stream from your own code with `NoteDebug()`, `NoteDebugln()`, and `NoteDebugf()`, and poll the Notecard for its sync status with `NoteDebugSyncStatus()`.

### Sending Notecard Requests

Whether using serial or I2C, sending Notecard requests and reading responses follows the same pattern:

1. Create a JSON object that includes a [valid Notecard API Request](https://dev.blues.io/api-reference/notecard-api.md).
2. Pass the request to one of the `NoteRequest*` functions.
3. If you called a function that returns a response, check it for errors and read the data you need, then free it.

```c
#define productUID "com.your-company.your-name:your_product"

J *req = NoteNewRequest("hub.set");
if (req) {
    JAddStringToObject(req, "product", productUID);
    JAddStringToObject(req, "mode", "continuous");
    JAddBoolToObject(req, "sync", true);
    if (!NoteRequest(req)) {
        NoteDebug("FATAL: Failed to configure Notecard!\n");
        while (1);
    }
}
```

#### Choosing a Request Function

note-c provides a few functions for sending a request, depending on whether you need the response and how you'd like to handle a Notecard that is temporarily busy.

- **`NoteNewRequest()`** creates a JSON (`J`) object for the specified Notecard request, with the `req` key set. It returns `NULL` if memory can't be allocated, so always check the result.

```c
J *req = NoteNewRequest("card.status");
```

- **`NoteRequest()`** sends a request and returns `true` if it succeeded and `false` if there was an I/O error or the Notecard responded with an error. Use it when you don't need the response itself.

```c
J *req = NoteNewRequest("hub.sync");
if (req) {
    NoteRequest(req);
}
```

- **`NoteRequestResponse()`** sends a request and returns the Notecard's response as a `J` object that you can [parse](#json-handling). It returns `NULL` if the request couldn't be sent.

```c
J *req = NoteNewRequest("card.status");
if (req) {
    J *rsp = NoteRequestResponse(req);
    if (rsp != NULL && !NoteResponseError(rsp)) {
        // do something with the response
    }
    NoteDeleteResponse(rsp);
}
```

- **`NoteRequestWithRetry()`** and **`NoteRequestResponseWithRetry()`** behave like the functions above, but keep retrying for up to the number of seconds you pass if the Notecard doesn't respond or returns an I/O error. These are handy during startup, when the Notecard may still be booting.

```c
J *req = NoteNewRequest("card.version");
if (req) {
    J *rsp = NoteRequestResponseWithRetry(req, 5);
    // ...
    NoteDeleteResponse(rsp);
}
```

> **Note:**
>
> The `NoteRequest*` functions always free the request object you pass in, whether or not the request succeeds. You only need to free responses, which you do with `NoteDeleteResponse()`. (It's safe to call on `NULL`.)
>
> If you need to reuse a request object, use the lower-level `NoteTransaction()` instead, which leaves ownership of the request with you.

> **Note:**
>
> In some situations you may need to send the Notecard a request and not wait for a response. For example, when using the [`card.attn` request's `sleep` mode to power down a host MCU](https://dev.blues.io/notecard/notecard-walkthrough/low-power-firmware-design.md#host-power-management), the host is disabled and can't receive a response.
>
> For these scenarios use `NoteNewCommand()`, which creates a `J` object with the `cmd` key set instead of `req`. The Notecard processes commands without sending a response. You send them with the same `NoteRequest()` function.
>
> ```c
> J *cmd = NoteNewCommand("card.attn");
> if (cmd) {
>     JAddStringToObject(cmd, "mode", "sleep");
>     JAddNumberToObject(cmd, "seconds", 3600);
>     NoteRequest(cmd);
> }
> ```

#### Checking for Errors

When the Notecard can't process a request it returns a response with an `err` field. `NoteResponseError()` returns `true` if a response contains one, and `JGetString(rsp, "err")` gives you the message. Notecard error messages include bracketed tags such as `{io}` or `{bad-bin}` that you can test for with `NoteErrorContains()`. See [Notecard Error and Status Codes](https://dev.blues.io/support/notecard-error-and-status-codes.md) for a list.

```c
J *rsp = NoteRequestResponse(req);
if (rsp == NULL) {
    NoteDebug("No response from Notecard\n");
} else if (NoteResponseError(rsp)) {
    NoteDebugf("Notecard error: %s\n", JGetString(rsp, "err"));
}
NoteDeleteResponse(rsp);
```

#### Working with Raw JSON Strings

If you'd rather build requests as plain C strings, `NoteRequestResponseJSON()` takes a newline-terminated JSON string and returns the Notecard's response as a newly allocated, newline-terminated string. This is useful for passing requests through from another source (for example, a serial console or Notehub). Unlike the `J`-based functions, you own both strings, so free the response with `NoteFree()` when you're done.

```c
char *rsp = NoteRequestResponseJSON("{\"req\":\"card.version\"}\n");
if (rsp) {
    NoteDebug(rsp);
    NoteFree(rsp);
}
```

### JSON Handling

note-c bundles a modified version of the [cJSON library](https://github.com/DaveGamble/cJSON) and exposes it through a `J` type. Treat `J` as opaque: you'll only ever hold a `J *` and use the functions below to build and read it.

#### Creating JSON Objects

Start with `NoteNewRequest()`, which allocates a `J` object containing a `"req"` key-value pair. From there, the following functions add fields to the object.

- `JAddBoolToObject`
- `JAddNumberToObject`
- `JAddStringToObject`
- `JAddObjectToObject`
- `JAddArrayToObject`
- `JAddItemToArray`

To see how to use these functions, suppose you need to create a `J` JSON object that contains the following JSON data.

```json
{
    "req": "example.request",
    "key-1": true,
    "key-2": 2,
    "key-3": "3",
    "key-4": {
        "key-a": ["a"]
    }
}
```

The code below constructs the above JSON structure, and then passes that object to the Notecard using `NoteRequest()`.

```c
J *req = NoteNewRequest("example.request");
if (req) {
    JAddBoolToObject(req, "key-1", true);
    JAddNumberToObject(req, "key-2", 2);
    JAddStringToObject(req, "key-3", "3");
    J *key4 = JAddObjectToObject(req, "key-4");
    J *keyA = JAddArrayToObject(key4, "key-a");
    JAddItemToArray(keyA, JCreateString("a"));

    NoteRequest(req);
}
```

> **Note:**
>
> note-c includes several functions for creating standalone JSON "items", such as `JCreateObject`, `JCreateArray`, `JCreateNumber`, `JCreateBool`, and `JCreateString`. You only need these when an API takes an item as an argument, such as `JAddItemToArray`, or when building a `body` object to pass to a helper like `NoteAdd()`.

#### Parsing JSON Objects

`NoteRequestResponse()` returns a `J` object containing the Notecard's response. For example, after a `card.status` request, `rsp` points at JSON with this structure:

```json
{
  "connected": true,
  "status": "{normal}",
  "storage": 2,
  "time": 1667924973,
  "cell": true
}
```

There are a number of functions you can use to parse individual fields out of a JSON object.

- `JIsPresent`
- `JGetBool`
- `JGetInt`
- `JGetNumber`
- `JGetObject`
- `JGetArray`
- `JGetString`

The `JGet*` functions return a sensible zero value (`false`, `0`, or `""`) when a field is missing, so you can read optional fields without checking `JIsPresent` first. Strings returned by `JGetString` point into the response, so copy anything you need to keep before calling `NoteDeleteResponse()`.

```c
char status[20];
J *req = NoteNewRequest("card.status");
if (req) {
    J *rsp = NoteRequestResponse(req);

    bool connected = JGetBool(rsp, "connected");
    strlcpy(status, JGetString(rsp, "status"), sizeof(status));
    int storage = JGetInt(rsp, "storage");
    JTIME time = JGetInt(rsp, "time");
    bool cell = JGetBool(rsp, "cell");

    NoteDeleteResponse(rsp);
}
```

When the response contains nested objects you can parse them using `JGetObject`. For example, the `file.changes` request returns a JSON object with a structure that looks like the one below.

```json
{
  "info": {
    "data.qo": {
      "total": 1
    },
    "data.qi": {
      "total": 2
    },
    "total": 3
  }
}
```

Given this response, you could use the following code to parse out the `total` from within the `"data.qo"` object.

```c
J *req = NoteNewRequest("file.changes");
if (req) {
    J *rsp = NoteRequestResponse(req);

    J *info = JGetObject(rsp, "info");
    J *data = JGetObject(info, "data.qo");
    int total = JGetInt(data, "total");
    NoteDebugf("Total: %d\n", total); // 1

    NoteDeleteResponse(rsp);
}
```

If you need the response as text, for example to log it, `JPrintUnformatted()` serializes a `J` object to a newly allocated string that you free with `JFree()`.

### Using Helpers

Alongside the general-purpose request functions, note-c includes a set of helpers that wrap common Notecard requests so you don't need to build the JSON yourself: `NoteSetProductID()`, `NoteSetSyncMode()`, `NoteAdd()`, `NoteTemplate()`, `NoteGetEnv()`, `NoteGetLocation()`, `NoteIsConnected()`, and others. For example, sending a Note with `NoteAdd()` takes a Notefile name and a `J` body, which the helper frees for you.

```c
J *body = JCreateObject();
if (body) {
    JAddNumberToObject(body, "temp", 22.5);
    JAddNumberToObject(body, "humidity", 48);
    NoteAdd("sensors.qo", body, false);
}
```

The helpers make common tasks shorter, but they cover a fixed set of arguments. Whenever you need a request argument a helper doesn't expose, build the request with `NoteNewRequest()` instead. See [Helper Functions](#helper-functions) for the full list.

## Ports

Because note-c leaves the platform-specific hooks to you, Blues maintains a number of open source ports that implement those hooks for popular platforms. If one of these matches your hardware, it's the fastest way to get running: the port handles [library initialization](#library-initialization) and you can go straight to [sending requests](#sending-notecard-requests).

| Port                                                                                      | Platform                                                              |
| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| [note-arduino](https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library.md) | Any Arduino-compatible board. Wraps note-c in a `Notecard` C++ class. |
| [note-zephyr](https://dev.blues.io/tools-and-sdks/firmware-libraries/zephyr-library.md)   | Zephyr RTOS, as a West module.                                        |
| [note-espidf](https://dev.blues.io/tools-and-sdks/firmware-libraries/esp-idf-library.md)  | ESP-IDF framework for Espressif chips, as an ESP-IDF component.       |
| [note-stm32l4](https://github.com/blues/note-stm32l4)                                     | STM32L4, using the STM32Cube HAL.                                     |
| [note-stm32g0](https://github.com/blues/note-stm32g0)                                     | STM32G0, using the STM32Cube HAL.                                     |
| [note-stm32f1](https://github.com/blues/note-stm32f1)                                     | STM32F1, using the STM32Cube HAL.                                     |
| [note-stm32l0](https://github.com/blues/note-stm32l0)                                     | STM32L0, using the STM32Cube HAL.                                     |
| [note-nrf52](https://github.com/blues/note-nrf52)                                         | Nordic nRF52.                                                         |
| [note-msp430](https://github.com/blues/note-msp430)                                       | TI MSP430.                                                            |
| [note-mbed](https://github.com/blues/note-mbed)                                           | Mbed OS.                                                              |

Even if your platform isn't listed, these ports are good references. The hooks are small and the vendor HAL calls they wrap usually have a direct equivalent on other platforms.

## Examples

### Complete Example: STM32L4

The code below is adapted from the [note-stm32l4](https://github.com/blues/note-stm32l4) port, and shows everything a bare-metal application needs: hook implementations for the STM32 HAL, library initialization, and a first request. The I2C hooks are a complete implementation of the Notecard's [serial-over-I2C protocol](https://dev.blues.io/guides-and-tutorials/notecard-guides/serial-over-i2c-protocol.md), and can be adapted to any platform with blocking I2C transmit and receive functions.

```c
#include <stdlib.h>
#include <string.h>
#include "main.h"   // STM32Cube-generated HAL handles and init functions
#include "note.h"

#define PRODUCT_UID "com.your-company.your-name:your_product"

// Hooks for note-c (implemented below)
bool noteI2CReset(uint16_t addr);
const char *noteI2CTransmit(uint16_t addr, uint8_t *buf, uint16_t len);
const char *noteI2CReceive(uint16_t addr, uint8_t *buf, uint16_t len,
                           uint32_t *available);
size_t noteDebugOutput(const char *text);

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART2_UART_Init();  // debug console

    // Memory and timing hooks: libc for memory, the HAL for time.
    NoteSetFn(malloc, free, HAL_Delay, HAL_GetTick);

    // Send library logs to the debug console.
    NoteSetFnDebugOutput(noteDebugOutput);

    // Talk to the Notecard over I2C.
    NoteSetFnI2C(NOTE_I2C_ADDR_DEFAULT, NOTE_I2C_MTU_DEFAULT,
                 noteI2CReset, noteI2CTransmit, noteI2CReceive);

    // Configure the Notecard. Retry for a few seconds in case it's still
    // booting.
    J *req = NoteNewRequest("hub.set");
    if (req) {
        JAddStringToObject(req, "product", PRODUCT_UID);
        JAddStringToObject(req, "mode", "periodic");
        JAddNumberToObject(req, "outbound", 60);
        NoteRequestWithRetry(req, 5);
    }

    while (1) {
        // Read the Notecard's temperature sensor and send it as a Note.
        JNUMBER temp;
        if (NoteGetTemperature(&temp)) {
            J *body = JCreateObject();
            if (body) {
                JAddNumberToObject(body, "temp", temp);
                NoteAdd("sensors.qo", body, false);
            }
        }
        HAL_Delay(60 * 1000);
    }
}

// Reinitialize the I2C peripheral. note-c calls this before the first
// transaction and after any I/O error.
bool noteI2CReset(uint16_t addr)
{
    HAL_I2C_DeInit(&hi2c1);
    MX_I2C1_Init();
    return true;
}

// Serial-over-I2C write: a length byte followed by the payload.
const char *noteI2CTransmit(uint16_t addr, uint8_t *buf, uint16_t len)
{
    uint8_t writebuf[NOTE_I2C_MTU_MAX + 1];
    writebuf[0] = (uint8_t)len;
    memcpy(&writebuf[1], buf, len);
    if (HAL_I2C_Master_Transmit(&hi2c1, addr << 1, writebuf, len + 1, 250)
            != HAL_OK) {
        return "i2c: HAL_I2C_Master_Transmit error";
    }
    return NULL;
}

// Serial-over-I2C read: ask for up to `len` bytes, then read a two-byte
// header (bytes still available, bytes in this packet) followed by the data.
const char *noteI2CReceive(uint16_t addr, uint8_t *buf, uint16_t len,
                           uint32_t *available)
{
    uint8_t hdr[2] = { 0, (uint8_t)len };
    if (HAL_I2C_Master_Transmit(&hi2c1, addr << 1, hdr, sizeof(hdr), 250)
            != HAL_OK) {
        return "i2c: HAL_I2C_Master_Transmit error";
    }

    uint8_t readbuf[NOTE_I2C_MTU_MAX + 2];
    if (HAL_I2C_Master_Receive(&hi2c1, addr << 1, readbuf, len + 2, 250)
            != HAL_OK) {
        return "i2c: HAL_I2C_Master_Receive error";
    }
    if (readbuf[1] != len) {
        return "i2c: incorrect amount of data";
    }

    *available = readbuf[0];
    memcpy(buf, &readbuf[2], len);
    return NULL;
}

// Write note-c's log output to the debug UART.
size_t noteDebugOutput(const char *text)
{
    size_t len = strlen(text);
    HAL_UART_Transmit(&huart2, (uint8_t *)text, len, 5000);
    return len;
}
```

For a serial connection, replace the `NoteSetFnI2C()` call and the I2C hooks with `NoteSetFnSerial()` and four serial hooks. The [note-stm32l4 source](https://github.com/blues/note-stm32l4/blob/master/Src/main.c) includes an interrupt-driven implementation of both.

### More Examples

- [Writing Host Firmware with STM32Cube](https://dev.blues.io/guides-and-tutorials/writing-host-firmware/stm32-nucleo/c-cpp-stm32cube.md)
  - A step-by-step tutorial that adds note-c to an STM32CubeIDE project and sends sensor data to Notehub, with variants for a Nucleo board (serial) and a Discovery board (I2C).
- [note-arduino](https://github.com/blues/note-arduino/tree/master/src)
  - The Arduino port's source is a compact, readable example of implementing every note-c hook, including the serial, I2C, mutex, and debug output hooks, in C++.
- [note-zephyr examples](https://github.com/blues/note-zephyr/tree/main/examples)
  - Complete Zephyr applications that call the Notecard API through note-c, including a blinky that sends Notes, a multi-threaded message queue example, and sending and receiving binary data.
- [Sending and Receiving Large Binary Objects](https://dev.blues.io/guides-and-tutorials/notecard-guides/sending-and-receiving-large-binary-objects.md)
  - A guide to the Notecard's binary data store, which note-c supports with the `NoteBinaryStore*` helpers.

## API Reference

The sections below document the note-c functions you'll use in most applications. All of them are declared in [`note.h`](https://github.com/blues/note-c/blob/master/note.h), which also documents the less common functions not covered here.

### Initialization

| Function                                              | Description                                                                                              |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| [`NoteSetFn()`](#notesetfn)                           | Register the memory allocation, memory free, millisecond delay, and millisecond counter hooks. Required. |
| [`NoteSetFnSerial()`](#notesetfnserial)               | Register the serial hooks and make serial the active interface.                                          |
| [`NoteSetFnI2C()`](#notesetfni2c)                     | Register the I2C address, transfer size, and I2C hooks, and make I2C the active interface.               |
| [`NoteSetActiveInterface()`](#notesetactiveinterface) | Switch between registered serial and I2C interfaces.                                                     |
| [`NoteSetFnNoteMutex()`](#notesetfnnotemutex)         | Register lock and unlock hooks called around every Notecard transaction. Optional.                       |
| [`NoteSetFnI2CMutex()`](#notesetfni2cmutex)           | Register lock and unlock hooks called around every I2C transfer. Optional.                               |
| [`NoteSetFnDebugOutput()`](#notesetfndebugoutput)     | Register the hook that receives note-c's log output. Optional.                                           |
| [`NoteSetLogLevel()`](#notesetloglevel)               | Set the runtime log level.                                                                               |

### Requests

| Function                                                          | Description                                                                       |
| ----------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| [`NoteNewRequest()`](#notenewrequest)                             | Create a `J` object with the `req` field set to the given request name.           |
| [`NoteNewCommand()`](#notenewcommand)                             | Create a `J` object with the `cmd` field set, for requests that need no response. |
| [`NoteRequest()`](#noterequest)                                   | Send a request and return `true` on success. Frees the request.                   |
| [`NoteRequestWithRetry()`](#noterequestwithretry)                 | Like `NoteRequest()`, but retries for up to the given number of seconds.          |
| [`NoteRequestResponse()`](#noterequestresponse)                   | Send a request and return the response as a `J` object. Frees the request.        |
| [`NoteRequestResponseWithRetry()`](#noterequestresponsewithretry) | Like `NoteRequestResponse()`, but retries for up to the given number of seconds.  |
| [`NoteRequestResponseJSON()`](#noterequestresponsejson)           | Send a request given as a JSON string and return the response as a JSON string.   |
| [`NoteTransaction()`](#notetransaction)                           | Send a request and return the response without freeing the request.               |
| [`NoteResponseError()`](#noteresponseerror)                       | Return `true` if a response contains an `err` field.                              |
| [`NoteErrorContains()`](#noteerrorcontains)                       | Return `true` if an error string contains a given error tag.                      |
| [`NoteDeleteResponse()`](#notedeleteresponse)                     | Free a response object.                                                           |
| [`NoteSetRequestTimeout()`](#notesetrequesttimeout)               | Change how long note-c waits for the Notecard to respond.                         |
| [`NoteDebugSyncStatus()`](#notedebugsyncstatus)                   | Poll the Notecard for sync status and write it to the debug output.               |

### JSON

| Function                                          | Description                                                                |
| ------------------------------------------------- | -------------------------------------------------------------------------- |
| [`JCreateObject()`](#jcreateobject)               | Create an empty object, for example a Note `body`.                         |
| [`JAddBoolToObject()`](#jaddbooltoobject)         | Add a boolean field to an object.                                          |
| [`JAddNumberToObject()`](#jaddnumbertoobject)     | Add a numeric field to an object.                                          |
| [`JAddStringToObject()`](#jaddstringtoobject)     | Add a string field to an object.                                           |
| [`JAddObjectToObject()`](#jaddobjecttoobject)     | Add a nested object and return it.                                         |
| [`JAddArrayToObject()`](#jaddarraytoobject)       | Add an array and return it.                                                |
| [`JAddItemToArray()`](#jadditemtoarray)           | Append an item to an array.                                                |
| [`JAddBinaryToObject()`](#jaddbinarytoobject)     | Add binary data to an object as a base64 string.                           |
| [`JIsPresent()`](#jispresent)                     | Return `true` if an object contains the given field.                       |
| [`JGetBool()`](#jgetbool)                         | Read a boolean field.                                                      |
| [`JGetInt()`](#jgetint)                           | Read a numeric field as an integer.                                        |
| [`JGetNumber()`](#jgetnumber)                     | Read a numeric field as a floating-point number.                           |
| [`JGetString()`](#jgetstring)                     | Read a string field.                                                       |
| [`JGetObject()`](#jgetobject)                     | Read a nested object.                                                      |
| [`JGetArray()`](#jgetarray)                       | Read an array.                                                             |
| [`JGetBinaryFromObject()`](#jgetbinaryfromobject) | Read a base64 string field as decoded binary data.                         |
| [`JPrintUnformatted()`](#jprintunformatted)       | Serialize a `J` object to a string.                                        |
| [`JParse()`](#jparse)                             | Parse a JSON string into a `J` object.                                     |
| [`JDelete()`](#jdelete)                           | Free a `J` object.                                                         |
| [`JFree()`](#jfree)                               | Free memory returned by `JPrintUnformatted()` or `JGetBinaryFromObject()`. |

### Helpers

| Function                                                | Description                                                      |
| ------------------------------------------------------- | ---------------------------------------------------------------- |
| [`NoteSetProductID()`](#notesetproductid)               | Set the Notecard's ProductUID.                                   |
| [`NoteSetSyncMode()`](#notesetsyncmode)                 | Set the Notecard's sync mode and intervals.                      |
| [`NoteAdd()`](#noteadd)                                 | Add a Note to a Notefile.                                        |
| [`NoteTemplate()`](#notetemplate)                       | Set a Note template for a Notefile.                              |
| [`NoteGetEnv()`](#notegetenv)                           | Read an environment variable as a string.                        |
| [`NoteGetEnvInt()`](#notegetenvint)                     | Read an environment variable as an integer.                      |
| [`NoteGetEnvNumber()`](#notegetenvnumber)               | Read an environment variable as a floating-point number.         |
| [`NoteIsConnected()`](#noteisconnected)                 | Return `true` if the Notecard is connected to Notehub.           |
| [`NoteTime()`](#notetime)                               | Get the current time from the Notecard.                          |
| [`NoteGetLocation()`](#notegetlocation)                 | Get the Notecard's last known location.                          |
| [`NoteGetTemperature()`](#notegettemperature)           | Read the Notecard's onboard temperature sensor.                  |
| [`NoteGetVoltage()`](#notegetvoltage)                   | Read the Notecard's supply voltage.                              |
| [`NoteSleep()`](#notesleep)                             | Ask the Notecard to power down the host for a number of seconds. |
| [`NoteBinaryStoreTransmit()`](#notebinarystoretransmit) | Write binary data to the Notecard's binary store.                |
| [`NoteBinaryStoreReceive()`](#notebinarystorereceive)   | Read binary data from the Notecard's binary store.               |

## Initialization Functions

### NoteSetFn

Register the memory allocation, memory free, millisecond delay, and millisecond counter hooks. Every application must call this before any other note-c function.

```c
void NoteSetFn(mallocFn mallocHook, freeFn freeHook,
               delayMsFn delayMsHook, getMsFn getMsHook);
```

Arguments

### `mallocHook`

*`void *(*)(size_t size)`*

Allocate `size` bytes and return a pointer to them, or `NULL` on failure.

### `freeHook`

*`void (*)(void *mem)`*

Free memory previously returned by `mallocHook`.

### `delayMsHook`

*`void (*)(uint32_t ms)`*

Block for `ms` milliseconds.

### `getMsHook`

*`uint32_t (*)(void)`*

Return a free-running millisecond counter. It may wrap.

**Example**

**C/C++**

```c
// STM32 HAL
NoteSetFn(malloc, free, HAL_Delay, HAL_GetTick);
```

### NoteSetFnSerial

Register the serial hooks and make serial the active interface.

```c
void NoteSetFnSerial(serialResetFn resetFn, serialTransmitFn transmitFn,
                     serialAvailableFn availFn, serialReceiveFn receiveFn);
```

Arguments

### `resetFn`

*`bool (*)(void)`*

Reinitialize the serial peripheral connected to the Notecard at 9600 baud. Return `true` on success. note-c calls this before the first transaction and after any I/O error.

### `transmitFn`

*`void (*)(uint8_t *buf, size_t len, bool flush)`*

Write `len` bytes from `buf` to the Notecard. If `flush` is `true`, wait for the bytes to leave the transmit buffer before returning.

### `availFn`

*`bool (*)(void)`*

Return `true` if at least one byte from the Notecard is waiting to be read.

### `receiveFn`

*`char (*)(void)`*

Return the next byte from the Notecard. note-c only calls this after `availFn` returns `true`.

**Example**

**C/C++**

```c
NoteSetFnSerial(noteSerialReset, noteSerialTransmit,
                noteSerialAvailable, noteSerialReceive);
```

### NoteSetFnI2C

Register the Notecard's I2C address, the maximum transfer size, and the I2C hooks, and make I2C the active interface. The transmit and receive hooks must implement the Notecard's [serial-over-I2C protocol](https://dev.blues.io/guides-and-tutorials/notecard-guides/serial-over-i2c-protocol.md). See the [STM32 example](#complete-example-stm32l4) for a complete implementation.

```c
void NoteSetFnI2C(uint32_t notecardAddr, uint32_t maxTransmitSize,
                  i2cResetFn resetFn, i2cTransmitFn transmitFn,
                  i2cReceiveFn receiveFn);
```

Arguments

### `notecardAddr`

*`uint32_t`*

The Notecard's I2C address. Pass `NOTE_I2C_ADDR_DEFAULT` (`0x17`) or `0` for the default.

### `maxTransmitSize`

*`uint32_t`*

The maximum number of bytes to send in a single I2C transaction. Pass `NOTE_I2C_MTU_DEFAULT` or `0` for the default. The upper limit is `NOTE_I2C_MTU_MAX` (253 bytes). Lower it if your platform's I2C driver has a smaller buffer.

### `resetFn`

*`bool (*)(uint16_t addr)`*

Reinitialize the I2C peripheral connected to the Notecard. Return `true` on success.

### `transmitFn`

*`const char *(*)(uint16_t addr, uint8_t *buf, uint16_t len)`*

Write `len` bytes from `buf` to the Notecard at I2C address `addr`, prefixed with a byte containing `len`. Return `NULL` on success or an error string on failure.

### `receiveFn`

*`const char *(*)(uint16_t addr, uint8_t *buf, uint16_t len, uint32_t *available)`*

Read `len` bytes from the Notecard into `buf`, and set `*available` to the number of bytes the Notecard still has waiting. Return `NULL` on success or an error string on failure.

**Example**

**C/C++**

```c
NoteSetFnI2C(NOTE_I2C_ADDR_DEFAULT, NOTE_I2C_MTU_DEFAULT,
             noteI2CReset, noteI2CTransmit, noteI2CReceive);
```

### NoteSetActiveInterface

Select which registered interface note-c uses to talk to the Notecard. `NoteSetFnSerial()` and `NoteSetFnI2C()` each make their own interface active, so you only need this if you've registered both and want to switch between them.

```c
void NoteSetActiveInterface(int interface);
```

Arguments

### `interface`

*`int`*

One of `NOTE_C_INTERFACE_SERIAL`, `NOTE_C_INTERFACE_I2C`, or `NOTE_C_INTERFACE_NONE`.

**Example**

**C/C++**

```c
NoteSetActiveInterface(NOTE_C_INTERFACE_I2C);
```

### NoteSetFnNoteMutex

Register lock and unlock hooks that note-c calls before and after every transaction with the Notecard. Use this when more than one task or thread sends Notecard requests.

```c
void NoteSetFnNoteMutex(mutexFn lockFn, mutexFn unlockFn);
```

Arguments

### `lockFn`

*`void (*)(void)`*

Block until the Notecard is available, then acquire it.

### `unlockFn`

*`void (*)(void)`*

Release the Notecard.

**Example**

**C/C++**

```c
// FreeRTOS
static SemaphoreHandle_t notecardMutex;

void lockNotecard(void) {
    xSemaphoreTake(notecardMutex, portMAX_DELAY);
}

void unlockNotecard(void) {
    xSemaphoreGive(notecardMutex);
}

notecardMutex = xSemaphoreCreateMutex();
NoteSetFnNoteMutex(lockNotecard, unlockNotecard);
```

### NoteSetFnI2CMutex

Register lock and unlock hooks that note-c calls before and after every I2C transfer. Use this when other devices share the Notecard's I2C bus, and have the code that drives those devices call the same lock and unlock functions.

```c
void NoteSetFnI2CMutex(mutexFn lockI2Cfn, mutexFn unlockI2Cfn);
```

Arguments

### `lockI2Cfn`

*`void (*)(void)`*

Block until the I2C bus is available, then acquire it.

### `unlockI2Cfn`

*`void (*)(void)`*

Release the I2C bus.

**Example**

**C/C++**

```c
NoteSetFnI2CMutex(lockI2C, unlockI2C);
```

### NoteSetFnDebugOutput

Register the hook that receives note-c's log output. Until you call this, note-c produces no output.

```c
void NoteSetFnDebugOutput(debugOutputFn fn);
```

Arguments

### `fn`

*`size_t (*)(const char *text)`*

Write the C string `text` to your console and return the number of bytes written.

**Example**

**C/C++**

```c
size_t noteDebugOutput(const char *text) {
    size_t len = strlen(text);
    HAL_UART_Transmit(&huart2, (uint8_t *)text, len, 5000);
    return len;
}

NoteSetFnDebugOutput(noteDebugOutput);
```

### NoteSetLogLevel

Set the runtime log level. Messages above this level are discarded before they reach your debug output hook.

```c
void NoteSetLogLevel(int level);
```

Arguments

### `level`

*`int`*

One of `NOTE_C_LOG_LEVEL_ERROR`, `NOTE_C_LOG_LEVEL_WARN`, `NOTE_C_LOG_LEVEL_INFO` (the default), or `NOTE_C_LOG_LEVEL_DEBUG`. The default `INFO` level logs every JSON request and response. Lower the level to `WARN` or `ERROR` to quiet that output in production.

**Example**

**C/C++**

```c
NoteSetLogLevel(NOTE_C_LOG_LEVEL_DEBUG);
```

## Request Functions

### NoteNewRequest

Create a `J` object with the `req` field set to the given request name.

```c
J *NoteNewRequest(const char *request);
```

> **Note:**
>
> `NoteNewRequest` allocates memory, which can fail if system resources are low. It returns `NULL` in that case, so check the result before adding fields to it.
>
> ```c
> if (J *req = NoteNewRequest("hub.sync")) {
>   ...
> }
> ```

Arguments

### `request`

*`const char *`*

The request name, for example `"note.add"`.

**Example**

**C/C++**

```c
J *req = NoteNewRequest("card.status");
```

#### Returns

A pointer to a `J` object containing `{"req": "<request>"}`, or `NULL` if memory couldn't be allocated.

### NoteNewCommand

Create a `J` object with the `cmd` field set to the given request name. The Notecard processes commands without sending a response, which is useful when the host won't be around to receive one, for example when putting the host to sleep with `card.attn`.

```c
J *NoteNewCommand(const char *request);
```

Arguments

### `request`

*`const char *`*

The request name, for example `"card.attn"`.

**Example**

**C/C++**

```c
J *cmd = NoteNewCommand("card.attn");
if (cmd) {
    JAddStringToObject(cmd, "mode", "sleep");
    JAddNumberToObject(cmd, "seconds", 3600);
    NoteRequest(cmd);
}
```

#### Returns

A pointer to a `J` object containing `{"cmd": "<request>"}`, or `NULL` if memory couldn't be allocated.

### NoteRequest

Send a request to the Notecard and discard the response. Use this when you only need to know whether the request succeeded.

```c
bool NoteRequest(J *req);
```

Arguments

### `req`

*`J *`*

The request object. It is freed before this function returns, whether or not the request succeeds.

**Example**

**C/C++**

```c
J *req = NoteNewRequest("hub.sync");
if (req) {
    if (!NoteRequest(req)) {
        NoteDebug("hub.sync failed\n");
    }
}
```

#### Returns

`true` if the request was sent and the response contained no `err` field. `false` if the request couldn't be sent, memory ran out, or the Notecard returned an error. For commands created with `NoteNewCommand()`, `true` only means the command was transmitted, since the Notecard sends no response.

### NoteRequestWithRetry

Send a request to the Notecard, retrying until it succeeds or the timeout elapses, and discard the response. Retries happen when the Notecard doesn't respond or returns an I/O error, which is common while the Notecard is still booting.

```c
bool NoteRequestWithRetry(J *req, uint32_t timeoutSeconds);
```

Arguments

### `req`

*`J *`*

The request object. It is freed before this function returns.

### `timeoutSeconds`

*`uint32_t`*

How long to keep retrying, in seconds.

**Example**

**C/C++**

```c
J *req = NoteNewRequest("hub.set");
if (req) {
    JAddStringToObject(req, "product", PRODUCT_UID);
    JAddStringToObject(req, "mode", "continuous");
    NoteRequestWithRetry(req, 5);
}
```

#### Returns

`true` if the request eventually succeeded, `false` if it timed out or the Notecard returned a non-I/O error.

### NoteRequestResponse

Send a request to the Notecard and return its response.

```c
J *NoteRequestResponse(J *req);
```

Arguments

### `req`

*`J *`*

The request object. It is freed before this function returns, whether or not the request succeeds.

**Example**

**C/C++**

```c
J *req = NoteNewRequest("card.status");
if (req) {
    J *rsp = NoteRequestResponse(req);
    if (rsp != NULL && !NoteResponseError(rsp)) {
        bool connected = JGetBool(rsp, "connected");
        NoteDebugf("Connected: %d\n", connected);
    }
    NoteDeleteResponse(rsp);
}
```

#### Returns

A pointer to a `J` object containing the response, or `NULL` if the request couldn't be sent. A non-`NULL` response may still contain an `err` field, so check it with [`NoteResponseError()`](#noteresponseerror).

> **Warning:**
>
> You are responsible for freeing the response with [`NoteDeleteResponse()`](#notedeleteresponse).

### NoteRequestResponseWithRetry

Send a request to the Notecard, retrying until it succeeds or the timeout elapses, and return its response.

```c
J *NoteRequestResponseWithRetry(J *req, uint32_t timeoutSeconds);
```

Arguments

### `req`

*`J *`*

The request object. It is freed before this function returns.

### `timeoutSeconds`

*`uint32_t`*

How long to keep retrying, in seconds, when there is no response or the response contains an I/O error.

**Example**

**C/C++**

```c
J *req = NoteNewRequest("card.version");
if (req) {
    J *rsp = NoteRequestResponseWithRetry(req, 5);
    if (rsp != NULL && !NoteResponseError(rsp)) {
        NoteDebugf("Notecard firmware: %s\n", JGetString(rsp, "version"));
    }
    NoteDeleteResponse(rsp);
}
```

#### Returns

A pointer to a `J` object containing the response, or `NULL` if the request couldn't be sent before the timeout. Free it with [`NoteDeleteResponse()`](#notedeleteresponse).

### NoteRequestResponseJSON

Send a request given as a JSON string and return the response as a JSON string. This bypasses the `J` object API entirely, which is useful when forwarding requests from another source such as a serial console.

```c
char *NoteRequestResponseJSON(const char *reqJSON);
```

Arguments

### `reqJSON`

*`const char *`*

A newline-terminated JSON string containing the request. Unlike the `J`-based functions, this function does not free the request, since it may be a string literal.

**Example**

**C/C++**

```c
char *rsp = NoteRequestResponseJSON("{\"req\":\"card.version\"}\n");
if (rsp) {
    NoteDebug(rsp);
    NoteFree(rsp);
}
```

#### Returns

A newly allocated, newline-terminated JSON string containing the response, or `NULL` if there was no response or the request was a command. Free it with `NoteFree()`.

### NoteTransaction

Send a request to the Notecard and return its response, without freeing the request. This is the lower-level function the `NoteRequest*` functions are built on. Use it when you want to send the same request object more than once.

```c
J *NoteTransaction(J *req);
```

Arguments

### `req`

*`J *`*

The request object. You remain responsible for freeing it with `JDelete()`.

**Example**

**C/C++**

```c
J *req = NoteNewRequest("card.temp");
if (req) {
    for (int i = 0; i < 5; i++) {
        J *rsp = NoteTransaction(req);
        if (rsp != NULL && !NoteResponseError(rsp)) {
            NoteDebugf("Temperature: %f\n", JGetNumber(rsp, "value"));
        }
        NoteDeleteResponse(rsp);
        NoteDelayMs(1000);
    }
    JDelete(req);
}
```

#### Returns

A pointer to a `J` object containing the response, or `NULL` if the request couldn't be sent. Free it with [`NoteDeleteResponse()`](#notedeleteresponse).

### NoteResponseError

Determine whether a response contains an `err` field. This is a macro, so it is safe to pass `NULL`.

```c
bool NoteResponseError(J *rsp);
```

Arguments

### `rsp`

*`J *`*

A response object.

**Example**

**C/C++**

```c
J *rsp = NoteRequestResponse(req);
if (NoteResponseError(rsp)) {
    NoteDebugf("Notecard error: %s\n", JGetString(rsp, "err"));
}
NoteDeleteResponse(rsp);
```

#### Returns

`true` if the response contains a non-empty `err` field.

### NoteErrorContains

Check whether an error string contains a particular error tag. Notecard error messages include bracketed tags, such as `{io}` or `{file-noexist}`, that are stable across firmware releases. See [Notecard Error and Status Codes](https://dev.blues.io/support/notecard-error-and-status-codes.md) for a list.

```c
bool NoteErrorContains(const char *errstr, const char *errtype);
```

Arguments

### `errstr`

*`const char *`*

The error string, typically from `JGetString(rsp, "err")`.

### `errtype`

*`const char *`*

The tag to look for, including the braces.

**Example**

**C/C++**

```c
J *rsp = NoteRequestResponse(req);
if (NoteResponseError(rsp)) {
    if (NoteErrorContains(JGetString(rsp, "err"), "{io}")) {
        // I/O error: safe to retry
    }
}
NoteDeleteResponse(rsp);
```

#### Returns

`true` if `errstr` contains `errtype`.

### NoteDeleteResponse

Free a response object. This is a macro around `JDelete()`, and is safe to call with `NULL`.

```c
void NoteDeleteResponse(J *rsp);
```

Arguments

### `rsp`

*`J *`*

The response object to free.

**Example**

**C/C++**

```c
J *rsp = NoteRequestResponse(req);
// ... use rsp ...
NoteDeleteResponse(rsp);
```

### NoteSetRequestTimeout

Change how long note-c waits for the Notecard to respond to a request. The default is adequate for nearly all requests, but a few, such as `web.*` requests, can legitimately take longer.

```c
uint32_t NoteSetRequestTimeout(uint32_t overrideSecs);
```

Arguments

### `overrideSecs`

*`uint32_t`*

The new timeout in seconds, or `0` to restore the default.

**Example**

**C/C++**

```c
uint32_t previous = NoteSetRequestTimeout(120);
// ... send a slow request ...
NoteSetRequestTimeout(previous);
```

#### Returns

The timeout that was in effect before the call.

### NoteDebugSyncStatus

Poll the Notecard for sync status and write any new messages to the debug output. Call it from your main loop while developing to watch the Notecard connect and sync.

```c
bool NoteDebugSyncStatus(int pollFrequencyMs, int maxLevel);
```

Arguments

### `pollFrequencyMs`

*`int`*

How often to query the Notecard, in milliseconds. Calls made sooner than this return immediately.

### `maxLevel`

*`int`*

The most detailed level of messages to display: `SYNCSTATUS_LEVEL_MAJOR` (`0`), `SYNCSTATUS_LEVEL_MINOR` (`1`), `SYNCSTATUS_LEVEL_DETAILED` (`2`), `SYNCSTATUS_LEVEL_ALGORITHMIC` (`3`), or `SYNCSTATUS_LEVEL_ALL` (`-1`).

**Example**

**C/C++**

```c
while (1) {
    NoteDebugSyncStatus(2500, SYNCSTATUS_LEVEL_ALL);
    // ... rest of loop ...
}
```

#### Returns

`true` if a message was written to the debug output.

> **Warning:**
>
> Sync status messages are not part of the stable API. Don't write code that depends on their contents.

## JSON Functions

note-c's JSON API is a thin layer over a bundled, modified copy of [cJSON](https://github.com/DaveGamble/cJSON). Every function takes or returns a `J *`, which you should treat as opaque.

### Building Objects

#### JCreateObject

Create an empty JSON object. Use this to build a Note `body` or any other object you'll pass to a helper, rather than a top-level request (use [`NoteNewRequest()`](#notenewrequest) for those).

```c
J *JCreateObject(void);
```

```c
J *body = JCreateObject();
if (body) {
    JAddNumberToObject(body, "temp", 22.5);
    NoteAdd("sensors.qo", body, false);
}
```

Related functions create other kinds of standalone items: `JCreateArray()`, `JCreateString()`, `JCreateNumber()`, and `JCreateBool()`. You only need these when an API takes an item as an argument, such as `JAddItemToArray()`.

#### JAddBoolToObject

Add a boolean field to an object. `Jbool` is an `int`, so pass `true` or `false`.

```c
J *JAddBoolToObject(J *object, const char *name, Jbool boolean);
```

```c
JAddBoolToObject(req, "sync", true);
```

#### JAddNumberToObject

Add a numeric field to an object. The value is stored as a `JNUMBER` (`double` by default, or `float` when `NOTE_C_SINGLE_PRECISION` is defined), so integers and floating-point values both go through this function.

```c
J *JAddNumberToObject(J *object, const char *name, JNUMBER number);
```

```c
JAddNumberToObject(req, "outbound", 60);
JAddNumberToObject(body, "temp", 22.5);
```

#### JAddStringToObject

Add a string field to an object. The string is copied, so the source doesn't need to outlive the object.

```c
J *JAddStringToObject(J *object, const char *name, const char *string);
```

```c
JAddStringToObject(req, "file", "sensors.qo");
```

#### JAddObjectToObject

Add an empty nested object to an object and return it so you can populate it.

```c
J *JAddObjectToObject(J *object, const char *name);
```

```c
J *body = JAddObjectToObject(req, "body");
if (body) {
    JAddNumberToObject(body, "temp", 22.5);
}
```

#### JAddArrayToObject

Add an empty array to an object and return it so you can populate it with [`JAddItemToArray()`](#jadditemtoarray).

```c
J *JAddArrayToObject(J *object, const char *name);
```

```c
J *readings = JAddArrayToObject(body, "readings");
if (readings) {
    JAddItemToArray(readings, JCreateNumber(22.5));
    JAddItemToArray(readings, JCreateNumber(22.7));
}
```

#### JAddItemToArray

Append an item to an array. The array takes ownership of the item.

```c
void JAddItemToArray(J *array, J *item);
```

```c
JAddItemToArray(readings, JCreateString("ok"));
```

#### JAddBinaryToObject

Add binary data to an object as a base64-encoded string. This is useful for small payloads; for anything large, use the [binary store helpers](#notebinarystoretransmit) instead.

```c
bool JAddBinaryToObject(J *json, const char *fieldName,
                        const void *binaryData, uint32_t binaryDataLen);
```

```c
uint8_t sample[16];
JAddBinaryToObject(body, "sample", sample, sizeof(sample));
```

Returns `true` if the data was encoded and added.

### Reading Objects

The `JGet*` functions return a zero value (`false`, `0`, `""`, or `NULL`) when a field is missing or has the wrong type, so you can read optional fields without checking `JIsPresent()` first. Values returned by `JGetString()`, `JGetObject()`, and `JGetArray()` point into the parent object and become invalid when it is freed.

#### JIsPresent

Return `true` if an object contains the given field.

```c
bool JIsPresent(J *json, const char *field);
```

```c
if (JIsPresent(rsp, "lat") && JIsPresent(rsp, "lon")) {
    // location is available
}
```

#### JGetBool

Read a boolean field.

```c
bool JGetBool(J *json, const char *field);
```

```c
bool connected = JGetBool(rsp, "connected");
```

#### JGetInt

Read a numeric field as a `JINTEGER` (`int64_t`).

```c
JINTEGER JGetInt(J *json, const char *field);
```

```c
JTIME time = JGetInt(rsp, "time");
```

#### JGetNumber

Read a numeric field as a `JNUMBER` (`double`).

```c
JNUMBER JGetNumber(J *json, const char *field);
```

```c
JNUMBER voltage = JGetNumber(rsp, "value");
```

#### JGetString

Read a string field. Copy the result if you need it after the object is freed.

```c
char *JGetString(J *json, const char *field);
```

```c
char version[32];
strlcpy(version, JGetString(rsp, "version"), sizeof(version));
```

#### JGetObject

Read a nested object.

```c
J *JGetObject(J *json, const char *field);
```

```c
J *body = JGetObject(rsp, "body");
if (body) {
    JNUMBER setpoint = JGetNumber(body, "setpoint");
}
```

#### JGetArray

Read an array. Iterate it with `JGetArraySize()` and `JGetArrayItem()`.

```c
J *JGetArray(J *json, const char *field);
```

```c
J *files = JGetArray(rsp, "files");
if (files) {
    int count = JGetArraySize(files);
    for (int i = 0; i < count; i++) {
        J *item = JGetArrayItem(files, i);
        NoteDebugf("%s\n", JGetStringValue(item));
    }
}
```

#### JGetBinaryFromObject

Decode a base64 string field into a newly allocated buffer.

```c
bool JGetBinaryFromObject(J *json, const char *fieldName,
                          uint8_t **retBinaryData, uint32_t *retBinaryDataLen);
```

```c
uint8_t *data;
uint32_t len;
if (JGetBinaryFromObject(body, "payload", &data, &len)) {
    // ... use data ...
    JFree(data);
}
```

Returns `true` on success. You must free the buffer with `JFree()`.

### Serializing and Freeing

#### JPrintUnformatted

Serialize a `J` object to a compact JSON string, with no extra whitespace.

```c
char *JPrintUnformatted(const J *item);
```

```c
char *json = JPrintUnformatted(rsp);
if (json) {
    NoteDebugln(json);
    JFree(json);
}
```

Returns a newly allocated string, or `NULL` on error. Free it with `JFree()`.

#### JParse

Parse a JSON string into a `J` object.

```c
J *JParse(const char *value);
```

```c
J *obj = JParse("{\"temp\": 22.5}");
if (obj) {
    JNUMBER temp = JGetNumber(obj, "temp");
    JDelete(obj);
}
```

Returns a new `J` object, or `NULL` if the string isn't valid JSON. Free it with `JDelete()`.

#### JDelete

Free a `J` object and everything it contains. Safe to call with `NULL`. [`NoteDeleteResponse()`](#notedeleteresponse) is an alias for this function.

```c
void JDelete(J *item);
```

#### JFree

Free a block of memory allocated by note-c, such as the strings returned by `JPrintUnformatted()` or the buffers returned by `JGetBinaryFromObject()`. This is a wrapper around the free hook you registered with `NoteSetFn()`.

```c
void JFree(void *p);
```

## Helper Functions

Helpers wrap a specific Notecard request so you don't need to build the JSON yourself. Each one sends the request, checks the response for errors, and frees everything. They cover a fixed set of arguments, so when you need an argument a helper doesn't expose, build the request with [`NoteNewRequest()`](#notenewrequest) instead.

### NoteSetProductID

Set the Notecard's ProductUID, which associates it with a Notehub project. Sends a [`hub.set`](https://dev.blues.io/api-reference/notecard-api/hub-requests.md#hub-set) request.

```c
bool NoteSetProductID(const char *productID);
```

Arguments

### `productID`

*`const char *`*

The ProductUID of your Notehub project. Pass an empty string to clear it.

**Example**

**C/C++**

```c
NoteSetProductID("com.your-company.your-name:your_product");
```

#### Returns

`true` if the request succeeded.

### NoteSetSyncMode

Set the Notecard's sync mode and sync intervals. Sends a [`hub.set`](https://dev.blues.io/api-reference/notecard-api/hub-requests.md#hub-set) request.

```c
bool NoteSetSyncMode(const char *uploadMode, int uploadMinutes,
                     int downloadMinutes, bool align, bool sync);
```

Arguments

### `uploadMode`

*`const char *`*

The `hub.set` `mode`: `"periodic"`, `"continuous"`, `"minimum"`, or `"off"`.

### `uploadMinutes`

*`int`*

The `outbound` interval in minutes, or `0` to leave it unchanged.

### `downloadMinutes`

*`int`*

The `inbound` interval in minutes, or `0` to leave it unchanged.

### `align`

*`bool`*

Whether to align sync times to the interval (the `align` argument). Only sent when `uploadMinutes` is non-zero.

### `sync`

*`bool`*

Whether to sync immediately after the mode change (the `sync` argument).

**Example**

**C/C++**

```c
// Periodic mode: upload every 60 minutes, download every 120, aligned
NoteSetSyncMode("periodic", 60, 120, true, false);
```

#### Returns

`true` if the request succeeded.

### NoteAdd

Add a Note to a Notefile. Sends a [`note.add`](https://dev.blues.io/api-reference/notecard-api/note-requests.md#note-add) request.

```c
bool NoteAdd(const char *target, J *body, bool urgent);
```

Arguments

### `target`

*`const char *`*

The Notefile name, for example `"sensors.qo"`.

### `body`

*`J *`*

The Note body, typically created with `JCreateObject()`. The helper frees it whether or not the request succeeds.

### `urgent`

*`bool`*

Reserved. Pass `false`. To sync a Note immediately, build a `note.add` request with `sync: true` using `NoteNewRequest()`.

**Example**

**C/C++**

```c
J *body = JCreateObject();
if (body) {
    JAddNumberToObject(body, "temp", 22.5);
    JAddNumberToObject(body, "humidity", 48);
    NoteAdd("sensors.qo", body, false);
}
```

#### Returns

`true` if the request succeeded.

### NoteTemplate

Set a [Note template](https://dev.blues.io/notecard/notecard-walkthrough/low-bandwidth-design.md#working-with-note-templates) for a Notefile. Sends a [`note.template`](https://dev.blues.io/api-reference/notecard-api/note-requests.md#note-template) request. note-c defines constants for the template field types, such as `TBOOL`, `TINT16`, `TFLOAT32`, `TSTRING(N)`, and `TSTRINGV`.

```c
bool NoteTemplate(const char *notefileID, J *templateBody);
```

Arguments

### `notefileID`

*`const char *`*

The Notefile name.

### `templateBody`

*`J *`*

A `J` object describing the template. The helper frees it whether or not the request succeeds.

**Example**

**C/C++**

```c
J *body = JCreateObject();
if (body) {
    JAddNumberToObject(body, "temp", TFLOAT32);
    JAddNumberToObject(body, "humidity", TUINT8);
    JAddStringToObject(body, "status", TSTRING(16));
    NoteTemplate("sensors.qo", body);
}
```

#### Returns

`true` if the request succeeded.

### NoteGetEnv

Read an [environment variable](https://dev.blues.io/guides-and-tutorials/notecard-guides/understanding-environment-variables.md) as a string. Sends an [`env.get`](https://dev.blues.io/api-reference/notecard-api/env-requests.md#env-get) request.

```c
bool NoteGetEnv(const char *variable, const char *defaultVal,
                char *buf, uint32_t buflen);
```

Arguments

### `variable`

*`const char *`*

The environment variable name.

### `defaultVal`

*`const char *`*

The value to place in `buf` if the variable isn't set, or `NULL` for an empty string.

### `buf`

*`char *`*

A buffer to receive the value.

### `buflen`

*`uint32_t`*

The size of `buf`.

**Example**

**C/C++**

```c
char mode[16];
NoteGetEnv("mode", "normal", mode, sizeof(mode));
```

#### Returns

`true` if the request succeeded. `buf` holds the default value if the variable isn't set or the request failed.

### NoteGetEnvInt

Read an environment variable as an integer.

```c
JINTEGER NoteGetEnvInt(const char *variable, JINTEGER defaultVal);
```

Arguments

### `variable`

*`const char *`*

The environment variable name.

### `defaultVal`

*`JINTEGER`*

The value to return if the variable isn't set or the request fails.

**Example**

**C/C++**

```c
int intervalMins = NoteGetEnvInt("interval_mins", 60);
```

#### Returns

The variable's value as an integer, or `defaultVal`.

### NoteGetEnvNumber

Read an environment variable as a floating-point number.

```c
JNUMBER NoteGetEnvNumber(const char *variable, JNUMBER defaultVal);
```

Arguments

### `variable`

*`const char *`*

The environment variable name.

### `defaultVal`

*`JNUMBER`*

The value to return if the variable isn't set or the request fails.

**Example**

**C/C++**

```c
JNUMBER threshold = NoteGetEnvNumber("temp_threshold", 30.0);
```

#### Returns

The variable's value as a number, or `defaultVal`.

### NoteIsConnected

Check whether the Notecard is connected to Notehub. Sends a [`hub.status`](https://dev.blues.io/api-reference/notecard-api/hub-requests.md#hub-status) request.

```c
bool NoteIsConnected(void);
```

Arguments

None

**Example**

**C/C++**

```c
if (NoteIsConnected()) {
    NoteDebug("Connected to Notehub\n");
}
```

#### Returns

`true` if the Notecard reports it is connected.

### NoteTime

Get the current time from the Notecard, as seconds since the Unix epoch. Sends a [`card.time`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-time) request. The related `NoteTimeST()` returns the same value but only asks the Notecard periodically, keeping time locally with the millisecond counter hook in between, which is better suited to code that reads the time often.

```c
JTIME NoteTime(void);
```

Arguments

None

**Example**

**C/C++**

```c
if (NoteTimeValid()) {
    JTIME now = NoteTime();
    NoteDebugf("Unix time: %lu\n", (unsigned long)now);
}
```

#### Returns

The current UTC time in seconds. If the Notecard hasn't yet synchronized its clock, the value is seconds since the host booted instead, so check `NoteTimeValid()` first when it matters.

### NoteGetLocation

Get the Notecard's last known location. Sends a [`card.location`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-location) request.

```c
bool NoteGetLocation(JNUMBER *retLat, JNUMBER *retLon, JTIME *time,
                     char *statusBuf, int statusBufLen);
```

Arguments

### `retLat`

*`JNUMBER *`*

Receives the latitude, or `NULL` if you don't need it.

### `retLon`

*`JNUMBER *`*

Receives the longitude, or `NULL` if you don't need it.

### `time`

*`JTIME *`*

Receives the time the location was acquired, or `NULL` if you don't need it.

### `statusBuf`

*`char *`*

Receives the Notecard's location status string, or `NULL` if you don't need it.

### `statusBufLen`

*`int`*

The size of `statusBuf`.

**Example**

**C/C++**

```c
JNUMBER lat, lon;
JTIME when;
if (NoteGetLocation(&lat, &lon, &when, NULL, 0)) {
    NoteDebugf("Location: %f, %f\n", lat, lon);
}
```

#### Returns

`true` if the Notecard has a location. `false` if it doesn't, in which case `statusBuf` explains why.

### NoteGetTemperature

Read the Notecard's onboard temperature sensor. Sends a [`card.temp`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-temp) request.

```c
bool NoteGetTemperature(JNUMBER *temp);
```

Arguments

### `temp`

*`JNUMBER *`*

Receives the temperature in degrees Celsius.

**Example**

**C/C++**

```c
JNUMBER temp;
if (NoteGetTemperature(&temp)) {
    NoteDebugf("Temperature: %.1f C\n", temp);
}
```

#### Returns

`true` if the request succeeded.

### NoteGetVoltage

Read the Notecard's supply voltage. Sends a [`card.voltage`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-voltage) request.

```c
bool NoteGetVoltage(JNUMBER *voltage);
```

Arguments

### `voltage`

*`JNUMBER *`*

Receives the voltage in volts.

**Example**

**C/C++**

```c
JNUMBER voltage;
if (NoteGetVoltage(&voltage)) {
    NoteDebugf("Voltage: %.2f V\n", voltage);
}
```

#### Returns

`true` if the request succeeded.

### NoteSleep

Ask the Notecard to cut power to the host for a number of seconds, using the `ATTN` pin. See [Low-Power Firmware Design](https://dev.blues.io/notecard/notecard-walkthrough/low-power-firmware-design.md#host-power-management) for the required wiring. Sends a [`card.attn`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-attn) command with `mode: "sleep"`.

```c
bool NoteSleep(char *stateb64, uint32_t seconds, const char *modes);
```

Arguments

### `stateb64`

*`char *`*

An optional base64-encoded string the Notecard stores while the host sleeps and returns to `NoteWake()` afterward, or `NULL`.

### `seconds`

*`uint32_t`*

How long to sleep. The Notecard restores power after this many seconds.

### `modes`

*`const char *`*

Optional additional `card.attn` modes, comma-separated, that should wake the host early, for example `"motion"`, or `NULL`.

**Example**

**C/C++**

```c
// Sleep for an hour
NoteSleep(NULL, 3600, NULL);
```

#### Returns

`true` if the command was sent. Because this is a command, there is no response to confirm the Notecard acted on it.

### NoteBinaryStoreTransmit

Write binary data to the Notecard's binary store, handling COBS encoding and MD5 verification for you. See [Sending and Receiving Large Binary Objects](https://dev.blues.io/guides-and-tutorials/notecard-guides/sending-and-receiving-large-binary-objects.md) for the full workflow. Sends [`card.binary.put`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-binary-put).

```c
const char *NoteBinaryStoreTransmit(uint8_t *unencodedData,
                                    uint32_t unencodedLen,
                                    uint32_t bufLen,
                                    uint32_t notecardOffset);
```

Arguments

### `unencodedData`

*`uint8_t *`*

A buffer holding the data to send. The data is encoded in place, so the buffer must be larger than the data; `NoteBinaryCodecMaxEncodedLength()` returns the exact size needed.

### `unencodedLen`

*`uint32_t`*

The number of data bytes in the buffer.

### `bufLen`

*`uint32_t`*

The total size of the buffer.

### `notecardOffset`

*`uint32_t`*

The byte offset in the Notecard's binary store to write to. Pass `0` for a single write, or the running total when sending a large payload in chunks.

**Example**

**C/C++**

```c
char buf[64] = "Hello, Notecard!";
const char *err = NoteBinaryStoreTransmit((uint8_t *)buf, strlen(buf),
                                          sizeof(buf), 0);
if (err) {
    NoteDebugf("Binary transmit failed: %s\n", err);
}
```

#### Returns

`NULL` on success, or an error string.

### NoteBinaryStoreReceive

Read binary data from the Notecard's binary store, handling COBS decoding and MD5 verification for you. Use `NoteBinaryStoreDecodedLength()` to find out how much data is available. Sends [`card.binary.get`](https://dev.blues.io/api-reference/notecard-api/card-requests.md#card-binary-get).

```c
const char *NoteBinaryStoreReceive(uint8_t *buffer, uint32_t bufLen,
                                   uint32_t decodedOffset,
                                   uint32_t decodedLen);
```

Arguments

### `buffer`

*`uint8_t *`*

A buffer to receive the data. The data is decoded in place, so the buffer must be larger than `decodedLen`; `NoteBinaryCodecMaxEncodedLength()` returns the exact size needed.

### `bufLen`

*`uint32_t`*

The total size of the buffer.

### `decodedOffset`

*`uint32_t`*

The byte offset in the Notecard's binary store to read from.

### `decodedLen`

*`uint32_t`*

The number of bytes to read.

**Example**

**C/C++**

```c
uint32_t len;
if (NoteBinaryStoreDecodedLength(&len) == NULL && len > 0) {
    uint32_t bufLen = NoteBinaryCodecMaxEncodedLength(len);
    uint8_t *buf = NoteMalloc(bufLen);
    if (buf) {
        const char *err = NoteBinaryStoreReceive(buf, bufLen, 0, len);
        if (!err) {
            // ... use the first len bytes of buf ...
        }
        NoteFree(buf);
    }
}
```

#### Returns

`NULL` on success, or an error string.
