Skip to content

Devices

Sigfox

Receive Sigfox messages in Thinger.io with the Sigfox plugin or as HTTP devices, decode their payload and answer with downlinks.

Guide10 min read

Sigfox is a low-power wide-area network for sensors that send a few bytes a few times an hour. Sigfox devices send their messages to the Sigfox backend, which forwards them to Thinger.io with a callback. You can receive those callbacks with the Sigfox plugin, which creates a device and a Data Bucket for each new Sigfox device, or with one HTTP device per Sigfox device, which works on any account.

Sigfoxdeviceup to 12 bytesBase stationSigfox networkSigfox backenddevice typecustom callbackCustom payload configThinger.ioSigfox pluginuplink and downlink codecreates device and bucketHTTP devicecallback actionsWrite BucketCall EndpointSet Device PropertyTimeoutResponse DataData Bucketdashboards, endpointsradioInternetwithout the plugin:callback/dataresponse to a BIDIR callback:8 bytes of downlink data,encoded by the plugin if you use itUplink: the callback posts each message to Thinger.ioDownlink: the answer to the callback, when the device asks for it
Two ways to receive Sigfox messages

Message limits ​

Sigfox limits the size and the number of messages, so design the payload in bytes before you write the firmware:

DirectionPayloadMessages per day
Uplink, device to networkUp to 12 bytesUp to 140
Downlink, network to deviceExactly 8 bytesUp to 4

The device uses the 868 MHz band in Europe and the 902 MHz band in the United States. A downlink is only sent when the device asks for one with an uplink, through a BIDIR callback.

Choose how to integrate ​

Sigfox pluginHTTP device
AccountA private instance: plugins are not available on the free Cloud account.Any account, including the free Cloud account.
DevicesCreates an HTTP device and a Data Bucket for each new Sigfox device of a device type.You create one HTTP device for each Sigfox device.
PayloadDecoded by the Sigfox backend with Custom payload config, or by your uplink code in the plugin.Decoded by the Sigfox backend with Custom payload config. Payload code runs only in the plugin.
DownlinksThe downlink property of each device, encoded by your downlink code.The Response Data property, already in the format Sigfox expects.

Before you start ​

  • Access to the Sigfox backend, with a device type that groups your devices.
  • Your devices registered in the Sigfox backend with their ID and PAC. Program a Sigfox board shows how to read them from an Arduino MKR FOX 1200.
  • Your server is YOUR_SERVER: iot.thinger.io on the free Cloud account, or your own domain on a private instance.

Connect with the plugin ​

  1. Install the plugin

    Go to Plugins > Marketplace and install Sigfox. Open it from the Plugins menu.

  2. Add the device type

    In Device Types, add a device type with the same name as the device type in the Sigfox backend, or use Default for all your devices.

  3. Configure the callback behavior

    In Callback Config, enable Auto provision resources and set:

    FieldWhat it does
    Device Identifier PrefixPrefix of the device ID; the Sigfox device ID is added after it, such as sigfox_1A2B3C.
    Bucket Identifier PrefixPrefix of the Data Bucket created for each device.
    Device Connection TimeoutMinutes without messages before the device shows as disconnected.
    Assign Asset Type, Assign Asset Group, Assign Endpoint, Assign ProjectOptional. Classify the new devices and call an endpoint on each message.
    Initialize Downlink DataJSON that starts the downlink property of each device. Set it if you use downlinks.

    To decode the raw Sigfox payload into fields, enable Callback Processing and write the uplink function (and downlink, for downlinks) in Callback Code. Test it with Test Callback Code, then click Save.

  4. Create the callback in the Sigfox backend

    Open Sigfox Integration in the plugin and copy the URL Pattern and the Authorization Header. In the Sigfox backend, create a custom callback for the device type with:

    • Type DATA and UPLINK, or BIDIR to answer with downlinks.
    • Channel URL, the copied URL pattern and method POST.
    • Send duplicate off, so that a message received by several base stations is stored once.
    • A Custom payload config if the backend decodes the payload (see Decode the payload).
    • Header Authorization with the copied value, and content type application/json.
    • A JSON body with the fields you need, based on the Sample Body of the plugin, such as {"temperature": {customData#temp}, "humidity": {customData#hum}, "data": "{data}"}.

On the next message of each Sigfox device, the plugin creates an HTTP device and its Data Bucket, and stores the message.

Connect without the plugin ​

Each Sigfox device is an HTTP device, and the Sigfox callback posts to its callback URL. The HTTP device stores the message in a Data Bucket, calls endpoints or sets properties like any other HTTP device, so you can build dashboards and alerts on it.

Deprecated

These steps use the HTTP callback of the device, which is deprecated. It keeps working, but for a new integration point the Sigfox callback to an API resource of a product instead: the same body works, the product can create each device on its first message with Auto Provision, and its response carries the downlink. Send the time as a ts field of the body ("ts": {time}000) instead of the ts query parameter.

  1. Create the Data Bucket

    Go to Data Buckets and create a Data Bucket, such as sigfox_data, with From Device Write Call as Data Source. The callback only writes to Data Buckets that already exist.

  2. Create the HTTP device

    Go to Devices, click Add Device and choose HTTP Device (Sigfox, Lora, cURL) as Device Type, with a Device Id that includes the Sigfox ID, such as sigfox_1A2B3C. Here YOUR_DEVICE is that ID.

  3. Configure the device callback

    Open the device, go to Callback and set Write Bucket to sigfox_data, a Timeout longer than the time between messages, such as 30 minutes for a message every 10 minutes, and Response Data to downlink if you use downlinks. Enable Authorization and copy the token from Overview: it is YOUR_TOKEN below.

    From the API, set the same with PUT /v3/users/{user}/devices/{device}/callback:

    json
    {
      "actions": {
        "write_bucket": "sigfox_data",
        "send_property": "downlink"
      },
      "properties": { "timeout": 30 }
    }
  4. Create the custom callback in the Sigfox backend

    In the Sigfox backend, go to Device Type, open your device type, click Callbacks and create a new Custom callback.

    Sigfox backend, Device Type section, Callbacks page of a device type with the Custom callback option
    Create a custom callback for the device type

    Fill in the form:

    FieldValue
    TypeDATA and UPLINK. Use BIDIR to answer with downlinks.
    ChannelURL
    Send duplicateOff. Otherwise a message received by several base stations is stored several times.
    Custom payload configHow to split the payload into fields, such as temp::float:32:little-endian hum::float:32:little-endian. See Decode the payload.
    Url patternhttps://YOUR_SERVER/v3/users/YOUR_USER/devices/YOUR_DEVICE/callback/data?ts={time}000
    Use HTTP MethodPOST
    HeadersAuthorization with the value Bearer YOUR_TOKEN, with a space after Bearer.
    Content typeapplication/json
    BodyThe JSON to store, with Sigfox variables. See the example below.
    Custom callback form of the Sigfox backend with type DATA UPLINK, channel URL, Send duplicate unchecked and the custom payload config temp::float:32:little-endian hum::float:32:little-endian, followed by the list of available and custom variables
    Type, channel, Send duplicate and Custom payload config

    {time} is the time of the message in seconds; ts={time}000 turns it into the milliseconds the callback expects, so each record keeps the time the device sent it, not the time it arrived.

    A body that stores the decoded fields with the signal quality and the coarse location of the message:

    json
    {
      "device": "{device}",
      "temperature": {customData#temp},
      "humidity": {customData#hum},
      "data": "{data}",
      "seqNumber": {seqNumber},
      "snr": {snr},
      "rssi": {rssi},
      "station": "{station}",
      "latitude": {lat},
      "longitude": {lng}
    }

    The Sigfox backend replaces each variable before it posts the body: {device} is the Sigfox ID, {data} the raw payload in hexadecimal, {seqNumber} the message counter, {snr}, {rssi} and {station} the signal and the base station, and {lat} and {lng} a location with an accuracy of kilometers. {customData#temp} and {customData#hum} exist only if the Custom payload config defines temp and hum. As the body has latitude and longitude, each message also updates the device location.

A device type usually has several devices. To serve them all with one callback, create an HTTP device for each one, named after its Sigfox ID, and use {device} in the URL: https://YOUR_SERVER/v3/users/YOUR_USER/devices/sigfox_{device}/callback/data?ts={time}000. The token must then be an access token with the CallDeviceCallback action on those devices, because the token from Overview only works for its own device.

Write straight to a Data Bucket

If you only need the data, the callback can post to the Data Bucket instead of a device: https://YOUR_SERVER/v1/users/YOUR_USER/buckets/sigfox_data/data?ts={time}000, with an access token that only has the WriteBucket action on that Data Bucket (see POST /v1/users/{user}/buckets/{bucket}/data). Use {device} in the bucket ID for one Data Bucket per device. There is no device then: no connection state, no endpoints and no downlinks.

Decode the payload ​

The Custom payload config of the Sigfox callback splits the bytes of each message into named fields, which the body uses as {customData#name}. Fields are read in order from the first byte, separated by spaces, with the syntax name::type:bits:endianness:

TypeExampleReads
uinthum::uint:8An unsigned integer of the given number of bits.
inttemp::int:16:little-endianA signed integer of the given number of bits.
floattemp::float:32:little-endianA 32-bit or 64-bit floating-point number.

The default byte order is big-endian. Microcontrollers such as the SAMD21 of the Arduino MKR boards and the ESP32 store numbers in little-endian, so add little-endian to every field of more than one byte that the firmware copies from memory.

The payload config must match the bytes the firmware sends, field by field. Send a C struct and mark it packed, so that the compiler adds no padding bytes between fields:

Custom payload configStruct in the firmwareBytes
temp::float:32:little-endian hum::float:32:little-endianstruct __attribute__((packed)) { float temp; float hum; }8
temp::int:16:little-endian hum::uint:8struct __attribute__((packed)) { int16_t temp; uint8_t hum; }3

Integers leave room for more fields within the 12 bytes. To keep decimals, send scaled values, such as the temperature in tenths of a degree (215 for 21.5 °C), and convert them in the plugin uplink code or in a product.

Program a Sigfox board ​

Any Sigfox module works with Thinger.io, as long as its payload matches the callback. This example uses the Arduino MKR FOX 1200, a SAMD21 board with an ATA8520 Sigfox module that runs on two AA or AAA batteries.

Arduino MKR FOX 1200 board, with its headers, the antenna connector and the battery terminal
Arduino MKR FOX 1200

Prepare the Arduino IDE:

  1. In Boards Manager, install Arduino SAMD Boards.
  2. In Library Manager, install Arduino SigFox for MKRFox1200, Arduino Low Power and RTCZero, and SimpleDHT for the sensor of the second sketch.
  3. Select the board Arduino MKR FOX 1200 and its port.

Read the ID and PAC ​

Upload this sketch and open the serial monitor at 9600 baud. Register the board in the Sigfox backend at backend.sigfox.com/activate with the ID and the PAC it prints, or follow Arduino's first configuration tutorial.

sigfox_id.ino
cpp
#include <SigFox.h>

void setup() {
  Serial.begin(9600);
  while (!Serial) {}

  if (!SigFox.begin()) {
    Serial.println("Sigfox module not found");
    return;
  }

  Serial.println("Firmware version: " + SigFox.SigVersion());
  Serial.println("ID:  " + SigFox.ID());
  Serial.println("PAC: " + SigFox.PAC());
  Serial.print("Module temperature: ");
  Serial.println(SigFox.internalTemperature());

  SigFox.end();  // put the module to sleep
}

void loop() {
}

Send temperature and humidity ​

This sketch reads a DHT11 sensor on pin 0 and sends the temperature and the humidity every 11 minutes, within the 140 messages a day, as two floats that match temp::float:32:little-endian hum::float:32:little-endian. The built-in LED blinks once when the message is sent and three times when it fails. Without a DHT11, send SigFox.internalTemperature() as the temperature.

sigfox_dht11.ino
cpp
#include <SigFox.h>
#include <ArduinoLowPower.h>
#include <SimpleDHT.h>

#define DHT11_PIN 0
#define SEND_INTERVAL (11UL * 60UL * 1000UL)  // 11 minutes: 131 messages a day

SimpleDHT11 dht11(DHT11_PIN);

// must match the Custom payload config of the callback
struct __attribute__((packed)) Reading {
  float temp;
  float hum;
};

void blink(int times, unsigned long ms) {
  for (int i = 0; i < times; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(ms);
    digitalWrite(LED_BUILTIN, LOW);
    delay(ms);
  }
}

void sendReading() {
  float temp = 0, hum = 0;
  if (dht11.read2(&temp, &hum, NULL) != SimpleDHTErrSuccess) {
    Serial.println("DHT11 read failed");
    return;
  }
  Reading reading = {temp, hum};

  SigFox.begin();
  delay(100);
  SigFox.status();  // clear pending interrupts
  delay(1);

  SigFox.beginPacket();
  SigFox.write((const uint8_t*)&reading, sizeof(reading));  // 8 bytes
  int error = SigFox.endPacket();
  SigFox.end();

  if (error) {
    Serial.println("Transmission failed");
    blink(3, 500);
  } else {
    Serial.println("Transmission ok");
    blink(1, 1000);
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  sendReading();
  delay(SEND_INTERVAL);
  // on batteries, sleep instead of waiting:
  // LowPower.sleep(SEND_INTERVAL);
}

Sleep mode and uploads

On batteries, replace delay() with LowPower.sleep() and remove the serial output. While the board sleeps, the IDE cannot upload a new sketch: press the reset button twice to enter the bootloader, then upload.

Result ​

In the Sigfox backend, open the device and go to Messages. Each message shows its payload in hexadecimal, the link quality and an icon with the result of the callback.

Messages of a device in the Sigfox backend, with the time, the payload in hexadecimal, the location, the link quality and a callback icon for each message
Messages of a device in the Sigfox backend

Click the callback icon to see the answer of Thinger.io: 200 - OK when the message was accepted.

Callback result dialog of the Sigfox backend showing OK from a base station and the answer 200 - OK
A successful callback

In Thinger.io, the device shows as connected in Devices, and its Data Bucket stores one record per message with the fields of the callback body. Plot them in a dashboard with the Data Bucket as the source.

Data table of the sigfox_data bucket with one record every 10 minutes: device, humidity, rssi, seqNumber, snr, station and temperature
Records of a Sigfox device

With a BIDIR callback, Thinger.io answers each message with downlink data, and the Sigfox network delivers its 8 bytes to the device.

  • With the plugin: the plugin answers with the downlink property of the device, converted by your downlink function.
  • Without the plugin: the HTTP device answers with its Response Data property, so the property must hold the answer that Sigfox expects: the Sigfox ID of the device as key, and downlinkData with 16 hexadecimal characters.

Without the plugin, set the property like this, where YOUR_TOKEN is an access token of your account and 1A2B3C the Sigfox ID:

PUT/v3/users/{user}/devices/{device}/properties/{property}API reference
bash
curl -X PUT "https://YOUR_SERVER/v3/users/YOUR_USER/devices/YOUR_DEVICE/properties/downlink" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": {"1A2B3C": {"downlinkData": "0102030405060708"}}}'

Change the property from the console, a dashboard or the API, and the device receives the new value the next time it asks for a downlink. On the MKR FOX 1200, ask for it with SigFox.endPacket(true) and read the 8 bytes with SigFox.parsePacket() and SigFox.read(). The device can only receive 4 downlinks a day.

Next steps ​