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 15:40:25 2017 +0000
Revision:
5:225de2b86ed5
Parent:
4:1d707586e24d
Code cleanup and shortening timeouts.

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