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:
Mon May 02 12:04:13 2016 +0000
Revision:
3:4da15d6e1429
Parent:
2:a4a68a858624
Child:
4:1d707586e24d
Added header byte (0x01) to the sigfox packets that indicates this example.

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 1:897a1b3f0955 3 #include "LinearTempSensor.h"
quicksand 0:49858c4c3500 4
quicksand 1:897a1b3f0955 5 /* The 4 onboard LEDs */
quicksand 0:49858c4c3500 6 DigitalOut LED_0 (PB_6);
quicksand 0:49858c4c3500 7 DigitalOut LED_1 (PA_7);
quicksand 0:49858c4c3500 8 DigitalOut LED_2 (PA_6);
quicksand 0:49858c4c3500 9 DigitalOut LED_3 (PA_5);
quicksand 0:49858c4c3500 10
quicksand 1:897a1b3f0955 11 /* The 2 user buttons */
quicksand 1:897a1b3f0955 12 InterruptIn SW1(PA_8);
quicksand 1:897a1b3f0955 13 InterruptIn SW2(PB_10);
quicksand 1:897a1b3f0955 14
quicksand 1:897a1b3f0955 15 /*Temperature sensor */
quicksand 2:a4a68a858624 16 LinearTempSensor sensor(PA_0, 5);
quicksand 0:49858c4c3500 17
quicksand 1:897a1b3f0955 18 /* Function prototypes */
quicksand 1:897a1b3f0955 19 void sw1interrupt();
quicksand 1:897a1b3f0955 20 void sw2interrupt();
quicksand 1:897a1b3f0955 21 void beat();
quicksand 1:897a1b3f0955 22 void sertmout();
quicksand 1:897a1b3f0955 23 bool modem_command_check_ok(char * command);
quicksand 1:897a1b3f0955 24 void modem_setup();
quicksand 1:897a1b3f0955 25 void txData(uint8_t btn);
quicksand 0:49858c4c3500 26
quicksand 1:897a1b3f0955 27 bool ser_timeout = false;
quicksand 1:897a1b3f0955 28
quicksand 1:897a1b3f0955 29 Ticker heartbeat;
quicksand 1:897a1b3f0955 30
quicksand 1:897a1b3f0955 31 /* Serial port over USB */
quicksand 0:49858c4c3500 32 Serial pc(USBTX, USBRX);
quicksand 1:897a1b3f0955 33
quicksand 1:897a1b3f0955 34 /* Serial connection to sigfox modem */
quicksand 0:49858c4c3500 35 Serial modem(PA_9, PA_10);
quicksand 0:49858c4c3500 36
quicksand 1:897a1b3f0955 37
quicksand 1:897a1b3f0955 38 int main()
quicksand 1:897a1b3f0955 39 {
quicksand 0:49858c4c3500 40
quicksand 1:897a1b3f0955 41 /* Setup TD120x */
quicksand 1:897a1b3f0955 42 wait(3);
quicksand 1:897a1b3f0955 43 modem_setup();
quicksand 1:897a1b3f0955 44 /* Test temperature sensor */
quicksand 1:897a1b3f0955 45 float vOut = sensor.Sense();
quicksand 1:897a1b3f0955 46 pc.printf("\n\rMCP9700 reading: Vout: %.2f mV", vOut);
quicksand 0:49858c4c3500 47
quicksand 1:897a1b3f0955 48 /* Turn off all LED */
quicksand 0:49858c4c3500 49 LED_0 = 1;
quicksand 0:49858c4c3500 50 LED_1 = 1;
quicksand 0:49858c4c3500 51 LED_2 = 1;
quicksand 0:49858c4c3500 52 LED_3 = 1;
quicksand 1:897a1b3f0955 53
quicksand 1:897a1b3f0955 54 /* Blinking LED */
quicksand 1:897a1b3f0955 55 heartbeat.attach(&beat, 0.5);
quicksand 1:897a1b3f0955 56
quicksand 1:897a1b3f0955 57 /* Setup button interrupts */
quicksand 1:897a1b3f0955 58 SW2.fall(&sw2interrupt);
quicksand 1:897a1b3f0955 59 SW1.fall(&sw1interrupt);
quicksand 1:897a1b3f0955 60
quicksand 1:897a1b3f0955 61 while(1) {
quicksand 0:49858c4c3500 62 if(pc.readable()) {
quicksand 0:49858c4c3500 63 modem.putc(pc.getc());
quicksand 0:49858c4c3500 64 }
quicksand 0:49858c4c3500 65 if(modem.readable()) {
quicksand 1:897a1b3f0955 66 pc.putc(modem.getc());
quicksand 0:49858c4c3500 67 }
quicksand 0:49858c4c3500 68 }
quicksand 0:49858c4c3500 69 }
quicksand 1:897a1b3f0955 70
quicksand 1:897a1b3f0955 71 void beat()
quicksand 1:897a1b3f0955 72 {
quicksand 1:897a1b3f0955 73 LED_0 = !LED_0;
quicksand 1:897a1b3f0955 74 }
quicksand 1:897a1b3f0955 75
quicksand 1:897a1b3f0955 76 /* Button 1 ISR */
quicksand 1:897a1b3f0955 77 void sw1interrupt()
quicksand 1:897a1b3f0955 78 {
quicksand 1:897a1b3f0955 79 pc.printf("\n\rButton 1 pressed\n\r");
quicksand 1:897a1b3f0955 80 LED_1 = 0;
quicksand 1:897a1b3f0955 81 txData(1);
quicksand 1:897a1b3f0955 82 }
quicksand 1:897a1b3f0955 83
quicksand 1:897a1b3f0955 84 /* Button 2 ISR */
quicksand 1:897a1b3f0955 85 void sw2interrupt()
quicksand 1:897a1b3f0955 86 {
quicksand 1:897a1b3f0955 87 pc.printf("\n\rButton 2 pressed\n\r");
quicksand 1:897a1b3f0955 88 LED_2 = 0;
quicksand 1:897a1b3f0955 89 txData(2);
quicksand 1:897a1b3f0955 90 }
quicksand 1:897a1b3f0955 91
quicksand 1:897a1b3f0955 92
quicksand 1:897a1b3f0955 93 /* TX data over Sigfox */
quicksand 1:897a1b3f0955 94 void txData (uint8_t btn)
quicksand 1:897a1b3f0955 95 {
quicksand 1:897a1b3f0955 96
quicksand 1:897a1b3f0955 97 float tAvg;
quicksand 1:897a1b3f0955 98 char command[32];
quicksand 2:a4a68a858624 99 sensor.Sense();
quicksand 1:897a1b3f0955 100 tAvg = sensor.GetAverageTemp();
quicksand 1:897a1b3f0955 101 char temperature[6] ="";
quicksand 1:897a1b3f0955 102 sprintf(temperature, "%3.1f", tAvg);
quicksand 1:897a1b3f0955 103 for(int i = 0; i < 5; i++)
quicksand 1:897a1b3f0955 104 if(temperature[i]==0) temperature[i] = ' ';
quicksand 3:4da15d6e1429 105 sprintf(command, "AT$SF=0142544e%x20%x%x%x%x%x43,2,0\n", btn+48, temperature[0],temperature[1],temperature[2],temperature[3],temperature[4]);
quicksand 1:897a1b3f0955 106 pc.printf("Sending pressed button %d and temperature %s C over Sigfox.\n", btn, temperature);
quicksand 1:897a1b3f0955 107 pc.printf("using modem command: %s", command);
quicksand 1:897a1b3f0955 108 modem_command_check_ok(command);
quicksand 1:897a1b3f0955 109 LED_1 = 1;
quicksand 1:897a1b3f0955 110 LED_2 = 1;
quicksand 1:897a1b3f0955 111 }
quicksand 1:897a1b3f0955 112
quicksand 1:897a1b3f0955 113 void modem_setup()
quicksand 1:897a1b3f0955 114 {
quicksand 1:897a1b3f0955 115 /* Reset to factory defaults */
quicksand 1:897a1b3f0955 116 if(modem_command_check_ok("AT&F")) {
quicksand 1:897a1b3f0955 117 pc.printf("Factory reset succesfull\r\n");
quicksand 1:897a1b3f0955 118 } else {
quicksand 1:897a1b3f0955 119 pc.printf("Factory reset TD120x failed\r\n");
quicksand 1:897a1b3f0955 120 }
quicksand 1:897a1b3f0955 121 /* Disable local echo */
quicksand 1:897a1b3f0955 122 modem.printf("ATE0\n");
quicksand 1:897a1b3f0955 123 if(modem_command_check_ok("ATE0")) {
quicksand 1:897a1b3f0955 124 pc.printf("Local echo disabled\r\n");
quicksand 1:897a1b3f0955 125 }
quicksand 1:897a1b3f0955 126 /* Write to mem */
quicksand 1:897a1b3f0955 127 if(modem_command_check_ok("AT&W")) {
quicksand 1:897a1b3f0955 128 pc.printf("Settings saved!\r\n");
quicksand 1:897a1b3f0955 129 }
quicksand 1:897a1b3f0955 130 }
quicksand 1:897a1b3f0955 131
quicksand 1:897a1b3f0955 132 /* ISR for serial timeout */
quicksand 1:897a1b3f0955 133 void sertmout()
quicksand 1:897a1b3f0955 134 {
quicksand 1:897a1b3f0955 135 ser_timeout = true;
quicksand 1:897a1b3f0955 136 }
quicksand 1:897a1b3f0955 137
quicksand 1:897a1b3f0955 138 bool modem_command_check_ok(char * command)
quicksand 1:897a1b3f0955 139 {
quicksand 1:897a1b3f0955 140 /* first clear serial data buffers */
quicksand 1:897a1b3f0955 141 while(modem.readable()) modem.getc();
quicksand 1:897a1b3f0955 142 /* Timeout for response of the modem */
quicksand 1:897a1b3f0955 143 Timeout tmout;
quicksand 1:897a1b3f0955 144 ser_timeout = false;
quicksand 1:897a1b3f0955 145 /* Buffer for incoming data */
quicksand 1:897a1b3f0955 146 char responsebuffer[6];
quicksand 1:897a1b3f0955 147 /* Flag to set when we get 'OK' response */
quicksand 1:897a1b3f0955 148 bool ok = false;
quicksand 1:897a1b3f0955 149 bool error = false;
quicksand 1:897a1b3f0955 150 /* Print command to TD120x */
quicksand 1:897a1b3f0955 151 modem.printf(command);
quicksand 1:897a1b3f0955 152 /* Newline to activate command */
quicksand 1:897a1b3f0955 153 modem.printf("\n");
quicksand 1:897a1b3f0955 154 /* Wait untill serial feedback, max 3 seconds before timeout */
quicksand 1:897a1b3f0955 155 tmout.attach(&sertmout, 3.0);
quicksand 1:897a1b3f0955 156 while(!modem.readable()&& ser_timeout == false);
quicksand 1:897a1b3f0955 157 while(!ok && !ser_timeout && !error) {
quicksand 1:897a1b3f0955 158 if(modem.readable()) {
quicksand 1:897a1b3f0955 159 for(int i = 0; i < 5; i++) {
quicksand 1:897a1b3f0955 160 responsebuffer[i] = responsebuffer[i+1];
quicksand 1:897a1b3f0955 161 }
quicksand 1:897a1b3f0955 162 responsebuffer[5] = modem.getc();
quicksand 1:897a1b3f0955 163 if(responsebuffer[0] == '\r' && responsebuffer[1] == '\n' && responsebuffer[2] == 'O' && responsebuffer[3] == 'K' && responsebuffer[4] == '\r' && responsebuffer[5] == '\n' ) {
quicksand 1:897a1b3f0955 164 ok = true;
quicksand 1:897a1b3f0955 165 } else if(responsebuffer[0] == '\r' && responsebuffer[1] == '\n' && responsebuffer[2] == 'E' && responsebuffer[3] == 'R' && responsebuffer[4] == 'R' && responsebuffer[5] == 'O' ) {
quicksand 1:897a1b3f0955 166 error = true;
quicksand 1:897a1b3f0955 167 }
quicksand 1:897a1b3f0955 168 }
quicksand 1:897a1b3f0955 169 }
quicksand 1:897a1b3f0955 170 tmout.detach();
quicksand 1:897a1b3f0955 171 return ok;
quicksand 1:897a1b3f0955 172 }