This software example demonstrates the downlink capabilities of the SIGFOX network.

Dependencies:   QW_Sensors mbed

Fork of QW-Downlink by Quicksand

Information

This example demonstrates how you can use SIGFOX downlinks on your QW development kit.

When the device boots, all leds are turned on. After successful initialization the LEDs are turned off again. A bidirectional message is sent to the SIGFOX cloud when the user pushes either SW1 or SW2. All the leds light up during SIGFOX transmission.

The Downstream messages are, in fact, frame acknowledgment. It means that to receive a message on the device, you have to send one indicating the SIGFOX network you are expecting an acknowledgement.

The device is sending a message with ack flag activated and then turns into receive mode during 25 seconds, looking for the network acknowledgment. Between the end of the send and the begin of the receive you have about 15 seconds of sleep.

A moving LED animation is displayed while the modem waits to begin listening for its downlink packet. The first LED and the last LED start burning continuously when the modem is listening for a downlink packet.

When reception is complete, the signal strength (RSSI) of your SIGFOX device is displayed on the led's.

  • 1 LEDs = maximum -130dBm or lower
  • 2 LEDs = maximum -110dBm or lower
  • 3 LEDs = maximum -90dBm or lower
  • 4 LEDs = higher than -90dBm

This is the RSSI at which the original uplink transmission was received on the basestation. You can also read what basestation received the uplink message if you connect to the virtual com-port of your QW development kit (9600 baud, 8-N-1)

/media/uploads/quicksand/downlink_0.jpg

If something goes wrong, all leds will blink together for 5 seconds. If no downlink message was received you will get the following notification in your terminal window:

/media/uploads/quicksand/downlink_1.jpg

SIGFOX Backend settings

This example makes use of the "default downlink settings" when you create a device type. This is a static "direct" message, no calls to an external server are used when using this type of downlink callback. The first 4 bytes of the downlink message contains the tap id that received your message, the next 2 bytes are user-payload, in this case 0x0000, the last 2 bytes contain the RSSI.

SIGFOX downlink messages are always limited to 8 bytes total!

/media/uploads/quicksand/downlink_2.jpg

You can alter this example to send your own type of payload back to the device. The sent back data can also be the result of a callback to your own server.

Your own server as downlink data provider

You can find more information about setting callbacks here: https://lpwan.be/wp/faq/#toggle-id-6 Basically you will need to create a BIDIR callback if you want to send data to your server and receive a response to send back to the device, from the same server.

The backend server will receive a new parameter : ack. The value will be “false” or “true”. When true is set, an acknowledgment is expected.

Then you have two choices :

  • You can refuse to respond to the ack, for this you have two ways:

The first one is to respond with a HTTP 204 (no Content) ; this can be done in php simply executing

<?php
header("HTTP/1.0 204 No Content");
?>

The second way is to use the standard json way indicating you do not want to ack. For this you can use something like this :

<?php
   $_id = $_GET["id"];
   $_time = $_GET["time"];
   $_ack = $_GET["ack"];
   $_data = $_GET["data"];

   if ( $_ack == "true" ) {
     echo "{";
     echo "\"". $_id ."\" : { \"noData\" : true }";
     echo "}";
   }
?>
  • You can answer to the acknowledgment by a 8 byte message like this

<?php
   $_id = $_GET["id"];
   $_time = $_GET["time"];
   $_ack = $_GET["ack"];
   $_data = $_GET["data"];

   if ( $_ack == "true" ) {
     echo "{";
     echo "\"". $_id ."\" : { \"downlinkData\" : \"0102030405060708\" }";
     echo "}";
   } 
   header("HTTP/1.0 200 OK");
   header("Content-Type : application/json");
?>

Battery consumption

During the RX period, the device is consuming 15mA until the message is received. This has to be added to the device consumption and it reduce the battery life if you are expecting potential acknowledgment on every message.

If no ack is sent, the system will listen for 25 seconds. If an ack is sent, the device will transmit again and consume an extra 45mA during less than a second.

The number of downlink messages is limited

The number of message in downlink depends on your contract, it can be up to 4 on platinum contract. You can send more than that but it will be transferred on best effort – meaning you have no insurance it will be transmitted or not. In the future there might be penalties involved.

Nothing however limits the number of downlink requests. This can be every SIGFOX message, but you would only be allowed to reply 4 times per day.

Committer:
quicksand
Date:
Tue Mar 14 12:45:21 2017 +0000
Revision:
4:1d707586e24d
Parent:
3:4da15d6e1429
Child:
5:225de2b86ed5
This first version of the QW-Downlink program demonstrates the usage of SIGFOX downlinks to receive data from the cloud on the device.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
quicksand 0:49858c4c3500 1 #include "mbed.h"
quicksand 1:897a1b3f0955 2 #include "math.h"
quicksand 0:49858c4c3500 3
quicksand 1:897a1b3f0955 4 /* The 4 onboard LEDs */
quicksand 4:1d707586e24d 5 BusOut leds(PB_6, PA_7, PA_6, PA_5);
quicksand 4:1d707586e24d 6
quicksand 4:1d707586e24d 7 /*
quicksand 0:49858c4c3500 8 DigitalOut LED_0 (PB_6);
quicksand 0:49858c4c3500 9 DigitalOut LED_1 (PA_7);
quicksand 0:49858c4c3500 10 DigitalOut LED_2 (PA_6);
quicksand 0:49858c4c3500 11 DigitalOut LED_3 (PA_5);
quicksand 4:1d707586e24d 12 */
quicksand 0:49858c4c3500 13
quicksand 1:897a1b3f0955 14 /* The 2 user buttons */
quicksand 1:897a1b3f0955 15 InterruptIn SW1(PA_8);
quicksand 1:897a1b3f0955 16 InterruptIn SW2(PB_10);
quicksand 1:897a1b3f0955 17
quicksand 1:897a1b3f0955 18 /* Function prototypes */
quicksand 4:1d707586e24d 19 void buttoninterrupt();
quicksand 1:897a1b3f0955 20 void beat();
quicksand 1:897a1b3f0955 21 void sertmout();
quicksand 1:897a1b3f0955 22 bool modem_command_check_ok(char * command);
quicksand 1:897a1b3f0955 23 void modem_setup();
quicksand 4:1d707586e24d 24 void downlink();
quicksand 4:1d707586e24d 25 void error();
quicksand 0:49858c4c3500 26
quicksand 1:897a1b3f0955 27 bool ser_timeout = false;
quicksand 4:1d707586e24d 28 bool go_downlink = false;
quicksand 1:897a1b3f0955 29
quicksand 1:897a1b3f0955 30 /* Serial port over USB */
quicksand 0:49858c4c3500 31 Serial pc(USBTX, USBRX);
quicksand 1:897a1b3f0955 32
quicksand 1:897a1b3f0955 33 /* Serial connection to sigfox modem */
quicksand 0:49858c4c3500 34 Serial modem(PA_9, PA_10);
quicksand 0:49858c4c3500 35
quicksand 4:1d707586e24d 36 void binToString(char *inputData, int dataLength, char *outputData) // Note outputdata must be 2 x inputdata + 1 in size!
quicksand 4:1d707586e24d 37 {
quicksand 4:1d707586e24d 38
quicksand 4:1d707586e24d 39 for (int i = 0; i<dataLength; i++) {
quicksand 4:1d707586e24d 40 sprintf(outputData+2*i,"%02X",*(inputData+i));
quicksand 4:1d707586e24d 41 }
quicksand 4:1d707586e24d 42 outputData[dataLength*2] = 0; // in theory redundant, the last sprintf should have done this but just to be sure...
quicksand 4:1d707586e24d 43 }
quicksand 4:1d707586e24d 44
quicksand 4:1d707586e24d 45 void error()
quicksand 4:1d707586e24d 46 {
quicksand 4:1d707586e24d 47 pc.printf("Something went wrong, please try again\n\r");
quicksand 4:1d707586e24d 48 int i = 0;
quicksand 4:1d707586e24d 49 // Failure, blink rapidly:
quicksand 4:1d707586e24d 50 while(i < 25) {
quicksand 4:1d707586e24d 51 leds = 0b0000;
quicksand 4:1d707586e24d 52 wait_ms(100);
quicksand 4:1d707586e24d 53 leds = 0b1111;
quicksand 4:1d707586e24d 54 wait_ms(100);
quicksand 4:1d707586e24d 55 i++;
quicksand 4:1d707586e24d 56 }
quicksand 4:1d707586e24d 57 }
quicksand 4:1d707586e24d 58
quicksand 4:1d707586e24d 59 /* Button 1 ISR */
quicksand 4:1d707586e24d 60 void buttoninterrupt()
quicksand 4:1d707586e24d 61 {
quicksand 4:1d707586e24d 62 go_downlink = true;
quicksand 4:1d707586e24d 63 }
quicksand 1:897a1b3f0955 64
quicksand 1:897a1b3f0955 65 int main()
quicksand 1:897a1b3f0955 66 {
quicksand 0:49858c4c3500 67
quicksand 1:897a1b3f0955 68 /* Setup TD120x */
quicksand 1:897a1b3f0955 69 wait(3);
quicksand 1:897a1b3f0955 70 modem_setup();
quicksand 4:1d707586e24d 71 leds = 0b1111;
quicksand 1:897a1b3f0955 72 /* Setup button interrupts */
quicksand 4:1d707586e24d 73 SW2.fall(&buttoninterrupt);
quicksand 4:1d707586e24d 74 SW1.fall(&buttoninterrupt);
quicksand 1:897a1b3f0955 75
quicksand 1:897a1b3f0955 76 while(1) {
quicksand 4:1d707586e24d 77 if(go_downlink) {
quicksand 4:1d707586e24d 78 downlink();
quicksand 4:1d707586e24d 79 go_downlink = false;
quicksand 4:1d707586e24d 80 }
quicksand 0:49858c4c3500 81 if(pc.readable()) {
quicksand 0:49858c4c3500 82 modem.putc(pc.getc());
quicksand 0:49858c4c3500 83 }
quicksand 0:49858c4c3500 84 if(modem.readable()) {
quicksand 1:897a1b3f0955 85 pc.putc(modem.getc());
quicksand 0:49858c4c3500 86 }
quicksand 0:49858c4c3500 87 }
quicksand 0:49858c4c3500 88 }
quicksand 1:897a1b3f0955 89
quicksand 4:1d707586e24d 90 void downlink()
quicksand 1:897a1b3f0955 91 {
quicksand 4:1d707586e24d 92 leds = 0b0000;
quicksand 4:1d707586e24d 93 pc.printf("Requesting bidirectional data\n\r");
quicksand 4:1d707586e24d 94 if(modem_command_check_ok("AT$SF=0102030405060708090A0B0C,2,1")) {
quicksand 4:1d707586e24d 95 pc.printf("Done sending request, waiting to start reception...\n\r");
quicksand 4:1d707586e24d 96 char * beginString = "+RX BEGIN";
quicksand 4:1d707586e24d 97 char * beginStringPtr;
quicksand 4:1d707586e24d 98 beginStringPtr = beginString;
quicksand 4:1d707586e24d 99 bool begin_rx = false;
quicksand 4:1d707586e24d 100
quicksand 4:1d707586e24d 101 /* Turn off all LED*/
quicksand 4:1d707586e24d 102 leds = 0b1111;
quicksand 4:1d707586e24d 103 /* Timeout */
quicksand 4:1d707586e24d 104 Timeout tmout;
quicksand 4:1d707586e24d 105 ser_timeout = false;
quicksand 4:1d707586e24d 106 tmout.attach(&sertmout, 35.0);
quicksand 4:1d707586e24d 107 /* Animation while waiting for downlink */
quicksand 4:1d707586e24d 108 int c;
quicksand 4:1d707586e24d 109 int animation = 0;
quicksand 4:1d707586e24d 110 while(!ser_timeout && !begin_rx) {
quicksand 1:897a1b3f0955 111
quicksand 4:1d707586e24d 112 while (modem.readable() && !ser_timeout && !begin_rx) {
quicksand 4:1d707586e24d 113 c = modem.getc();
quicksand 4:1d707586e24d 114 if ( (char)c == *beginStringPtr ) beginStringPtr++;
quicksand 4:1d707586e24d 115 else beginStringPtr = beginString;
quicksand 4:1d707586e24d 116 if ( *beginStringPtr == 0 ) {
quicksand 4:1d707586e24d 117 begin_rx = true;
quicksand 4:1d707586e24d 118 pc.printf("Entering reception window\n\r");
quicksand 4:1d707586e24d 119 }
quicksand 4:1d707586e24d 120 }
quicksand 4:1d707586e24d 121 animation = (animation + 1)%150000;
quicksand 4:1d707586e24d 122 if(animation == 0) leds = 0b1110;
quicksand 4:1d707586e24d 123 if(animation == 25000) leds = 0b1101;
quicksand 4:1d707586e24d 124 if(animation == 50000) leds = 0b1011;
quicksand 4:1d707586e24d 125 if(animation == 75000) leds = 0b0111;
quicksand 4:1d707586e24d 126 if(animation == 100000) leds = 0b1011;
quicksand 4:1d707586e24d 127 if(animation == 125000) leds = 0b1101;
quicksand 4:1d707586e24d 128 }
quicksand 4:1d707586e24d 129 /* turn off timeout timer */
quicksand 4:1d707586e24d 130 tmout.detach();
quicksand 4:1d707586e24d 131 if(ser_timeout) error();
quicksand 4:1d707586e24d 132 else {
quicksand 4:1d707586e24d 133 /* Turn on outer LEDs*/
quicksand 4:1d707586e24d 134 leds = 0b0110;
quicksand 4:1d707586e24d 135 /* Read the data */
quicksand 4:1d707586e24d 136 // check if reception window ended
quicksand 4:1d707586e24d 137 char * readyString = "+RX END";
quicksand 4:1d707586e24d 138 char * readyStringPtr;
quicksand 4:1d707586e24d 139 readyStringPtr = readyString;
quicksand 4:1d707586e24d 140 bool rx_ready = false;
quicksand 4:1d707586e24d 141 int c;
quicksand 4:1d707586e24d 142 char rx_buffer[128];
quicksand 4:1d707586e24d 143 int i = 0;
quicksand 4:1d707586e24d 144 int j = 0;
quicksand 4:1d707586e24d 145 tmout.attach(&sertmout, 30.0);
quicksand 4:1d707586e24d 146 ser_timeout = false;
quicksand 4:1d707586e24d 147 while(modem.readable() && !ser_timeout && !rx_ready)
quicksand 4:1d707586e24d 148 while (((c = modem.getc()) > 0) && !ser_timeout && !rx_ready) {
quicksand 4:1d707586e24d 149 rx_buffer[i++] = (char)c;
quicksand 4:1d707586e24d 150 pc.putc((char)c);
quicksand 4:1d707586e24d 151 if ( (char)c == *readyStringPtr ) readyStringPtr++;
quicksand 4:1d707586e24d 152 else readyStringPtr = readyString;
quicksand 4:1d707586e24d 153 if ( *readyStringPtr == 0 ) {
quicksand 4:1d707586e24d 154 rx_ready = true;
quicksand 4:1d707586e24d 155 }
quicksand 4:1d707586e24d 156 }
quicksand 4:1d707586e24d 157 rx_buffer[i++] = 0; // EOS
quicksand 4:1d707586e24d 158 tmout.detach();
quicksand 4:1d707586e24d 159 if(ser_timeout) error();
quicksand 4:1d707586e24d 160 else {
quicksand 4:1d707586e24d 161 /* Turn off all LED*/
quicksand 4:1d707586e24d 162 leds = 0b1111;
quicksand 4:1d707586e24d 163 pc.printf("\n\rReception done, received data:\n\r");
quicksand 4:1d707586e24d 164 /* removing spaces, new lines, start and end markings */
quicksand 4:1d707586e24d 165 for(i = 0, j = 0; i < strlen(rx_buffer); i++) {
quicksand 4:1d707586e24d 166 rx_buffer[i-j] = rx_buffer[i];
quicksand 4:1d707586e24d 167 if(rx_buffer[i] == ' ' || rx_buffer[i] == '\n' || rx_buffer[i] == '\r' || rx_buffer[i] == '+' || rx_buffer[i] == '=' || (rx_buffer[i] > 'A' && rx_buffer[i] < 'Z'))
quicksand 4:1d707586e24d 168 j++;
quicksand 4:1d707586e24d 169 }
quicksand 4:1d707586e24d 170 /* do not forget EOS again! */
quicksand 4:1d707586e24d 171 rx_buffer[i-j] = 0;
quicksand 4:1d707586e24d 172
quicksand 4:1d707586e24d 173 if(strlen(rx_buffer) != 0) {
quicksand 4:1d707586e24d 174 /* Print received data without spaces and markers */
quicksand 4:1d707586e24d 175 for(j = 0; j < strlen(rx_buffer); j++) pc.putc(rx_buffer[j]);
quicksand 4:1d707586e24d 176
quicksand 4:1d707586e24d 177 char buff[32];
quicksand 4:1d707586e24d 178
quicksand 4:1d707586e24d 179 /* Extract tap and store to int (in this case you would have been able to just print the received data)*/
quicksand 4:1d707586e24d 180 memcpy( buff, &rx_buffer[0], 8 );
quicksand 4:1d707586e24d 181 buff[8] = '\0';
quicksand 4:1d707586e24d 182 int tap = (int)strtol(buff, NULL, 16);
quicksand 4:1d707586e24d 183
quicksand 4:1d707586e24d 184 /* Extract Client data*/
quicksand 4:1d707586e24d 185 memcpy( buff, &rx_buffer[8], 4 );
quicksand 4:1d707586e24d 186 buff[4] = '\0';
quicksand 4:1d707586e24d 187 int client = (int)strtol(buff, NULL, 16);
quicksand 4:1d707586e24d 188
quicksand 4:1d707586e24d 189 /* Exctract the RSSI */
quicksand 4:1d707586e24d 190 memcpy( buff, &rx_buffer[12], 4 );
quicksand 4:1d707586e24d 191 buff[4] = '\0';
quicksand 4:1d707586e24d 192 int16_t rssi = (int16_t)strtol(buff, NULL, 16);
quicksand 4:1d707586e24d 193
quicksand 4:1d707586e24d 194 pc.printf("\n\rMessage received on tap: %X\n\r", tap);
quicksand 4:1d707586e24d 195 pc.printf("Client data: %X\n\r", client);
quicksand 4:1d707586e24d 196 pc.printf("RSSI level: %d\n\r", rssi);
quicksand 4:1d707586e24d 197
quicksand 4:1d707586e24d 198 if(rssi > -90) leds = 0b0000;
quicksand 4:1d707586e24d 199 else if (rssi > -110) leds = 0b0001;
quicksand 4:1d707586e24d 200 else if (rssi > -130) leds = 0b0011;
quicksand 4:1d707586e24d 201 else leds = 0b0111;
quicksand 4:1d707586e24d 202 } else {
quicksand 4:1d707586e24d 203 pc.printf("\n\rNo valid downlink data received!\n\r");
quicksand 4:1d707586e24d 204 error();
quicksand 4:1d707586e24d 205 }
quicksand 4:1d707586e24d 206 }
quicksand 4:1d707586e24d 207 }
quicksand 1:897a1b3f0955 208
quicksand 1:897a1b3f0955 209
quicksand 4:1d707586e24d 210 } else {
quicksand 4:1d707586e24d 211 // Failure, blink rapidly indefinetly:
quicksand 4:1d707586e24d 212 error();
quicksand 4:1d707586e24d 213 }
quicksand 1:897a1b3f0955 214 }
quicksand 1:897a1b3f0955 215
quicksand 1:897a1b3f0955 216 void modem_setup()
quicksand 1:897a1b3f0955 217 {
quicksand 1:897a1b3f0955 218 /* Reset to factory defaults */
quicksand 1:897a1b3f0955 219 if(modem_command_check_ok("AT&F")) {
quicksand 1:897a1b3f0955 220 pc.printf("Factory reset succesfull\r\n");
quicksand 1:897a1b3f0955 221 } else {
quicksand 1:897a1b3f0955 222 pc.printf("Factory reset TD120x failed\r\n");
quicksand 1:897a1b3f0955 223 }
quicksand 1:897a1b3f0955 224 /* Disable local echo */
quicksand 1:897a1b3f0955 225 modem.printf("ATE0\n");
quicksand 1:897a1b3f0955 226 if(modem_command_check_ok("ATE0")) {
quicksand 1:897a1b3f0955 227 pc.printf("Local echo disabled\r\n");
quicksand 1:897a1b3f0955 228 }
quicksand 1:897a1b3f0955 229 /* Write to mem */
quicksand 1:897a1b3f0955 230 if(modem_command_check_ok("AT&W")) {
quicksand 1:897a1b3f0955 231 pc.printf("Settings saved!\r\n");
quicksand 1:897a1b3f0955 232 }
quicksand 1:897a1b3f0955 233 }
quicksand 1:897a1b3f0955 234
quicksand 1:897a1b3f0955 235 /* ISR for serial timeout */
quicksand 1:897a1b3f0955 236 void sertmout()
quicksand 1:897a1b3f0955 237 {
quicksand 1:897a1b3f0955 238 ser_timeout = true;
quicksand 1:897a1b3f0955 239 }
quicksand 1:897a1b3f0955 240
quicksand 1:897a1b3f0955 241 bool modem_command_check_ok(char * command)
quicksand 1:897a1b3f0955 242 {
quicksand 1:897a1b3f0955 243 /* first clear serial data buffers */
quicksand 1:897a1b3f0955 244 while(modem.readable()) modem.getc();
quicksand 1:897a1b3f0955 245 /* Timeout for response of the modem */
quicksand 1:897a1b3f0955 246 Timeout tmout;
quicksand 1:897a1b3f0955 247 ser_timeout = false;
quicksand 4:1d707586e24d 248 // check if ok or error is received
quicksand 4:1d707586e24d 249 char * readyString = "OK";
quicksand 4:1d707586e24d 250 char * readyStringPtr;
quicksand 4:1d707586e24d 251 readyStringPtr = readyString;
quicksand 4:1d707586e24d 252 char * errorString = "ERROR";
quicksand 4:1d707586e24d 253 char * errorStringPtr;
quicksand 4:1d707586e24d 254 errorStringPtr = errorString;
quicksand 1:897a1b3f0955 255 /* Flag to set when we get 'OK' response */
quicksand 1:897a1b3f0955 256 bool ok = false;
quicksand 1:897a1b3f0955 257 bool error = false;
quicksand 1:897a1b3f0955 258 /* Print command to TD120x */
quicksand 1:897a1b3f0955 259 modem.printf(command);
quicksand 1:897a1b3f0955 260 /* Newline to activate command */
quicksand 1:897a1b3f0955 261 modem.printf("\n");
quicksand 4:1d707586e24d 262 /* Wait untill serial feedback, max 10 seconds before timeout */
quicksand 4:1d707586e24d 263 tmout.attach(&sertmout, 10.0);
quicksand 4:1d707586e24d 264 int c;
quicksand 4:1d707586e24d 265 while(!ser_timeout && !ok && !error)
quicksand 4:1d707586e24d 266 if(modem.readable())
quicksand 4:1d707586e24d 267 while (((c = modem.getc()) > 0) && !ser_timeout && !ok && !error) {
quicksand 4:1d707586e24d 268 if ( (char)c == *readyStringPtr ) readyStringPtr++;
quicksand 4:1d707586e24d 269 else readyStringPtr = readyString;
quicksand 4:1d707586e24d 270 if ( *readyStringPtr == 0 ) {
quicksand 4:1d707586e24d 271 ok = true;
quicksand 4:1d707586e24d 272 }
quicksand 4:1d707586e24d 273 if ( (char)c == *errorStringPtr ) errorStringPtr++;
quicksand 4:1d707586e24d 274 else errorStringPtr = errorString;
quicksand 4:1d707586e24d 275 if ( *errorStringPtr == 0 ) {
quicksand 4:1d707586e24d 276 error = true;
quicksand 4:1d707586e24d 277 }
quicksand 1:897a1b3f0955 278 }
quicksand 1:897a1b3f0955 279 tmout.detach();
quicksand 4:1d707586e24d 280 if(ser_timeout) return false;
quicksand 4:1d707586e24d 281 else return ok;
quicksand 1:897a1b3f0955 282 }