# Datalink library

Datalink (uplink and downlink) lib using PPRZ protocol
======================================================

HOWTO export
------------

- Export library: in this folder (`sw/airborne/subsystems/datalink`), type
  ~~~
  make export_lib
  ~~~
  be sure that you have correctly build the paparazzi project (`make` in the main directory)

- The default export directory is `${PAPARAZZI_HOME}/var/export`, to change it:
  ~~~
  EXPORT_ROOT=<your_export_root_dir> make export_lib
  ~~~

- `make clean` will only remove the export directory

HOWTO use the datalink library
------------------------------

1. Copy the folder `datalink` in the export directory to your project directory (or untar the file `pprz_datalink.tar`)

2. In your project makefile:
  * define the variable `PPRZ_DATALINK_PORT` to your device (ex: `PPRZ_DATALINK_PORT=XXX`)
  * define the variable `PPRZ_DATALINK_DIR` to your extract folder
  * include the library makefile `include ${PPRZ_DATALINK_DIR}/datalink/pprz_datalink.mk`
  * add `PPRZ_DATALINK_CFLAGS` to your cflags
  * add `PPRZ_DATALINK_SRCS` to your sources (if your project requires a list of object files, you can use `PPRZ_DATALINK_OBJS` instead)

3. Device interface
  You need to provide an interface to send the data. If your device is named `XXX`, you need to provide the following functions or macros
  * `XXXCheckFreeSpace(len)` that return true if your device has enough space for `len` bytes
  * `XXXTransmit(c)` that send a single byte `c` using the device `XXX` (or store them in a buffer)
  * `XXXSendMessage()` that flushs the bytes to send (if needed)
  * `XXXChAvailable()` that returns true new characters are available for reading
  * `XXXGetch()` taht returns the next character to parse

  Usually, an UART interface is used for datalink.

4. Message parsing and includes:
  in a C file of you project (for example a file named `datalink.c`), add the following lines:
  ~~~{.c}
  #define DATALINK_C 1 // this should be placed before the includes

  #inlcude "uart.h" // or change this include to the header of your actual device interface
  #include "downlink.h"

  void dl_parse_msg(void) {
    <add your code to parse messages here>
  }
  ~~~
  You also need to poll for new characters, which means that you have to call the `DatalinkEvent()` function in your main loop.
  This function is non-blocking and calls the decoder on each received byte. Finally the `dl_parse_msg` function is called when a complete message is received (checksum is valid).

Example project for Mbeb dev board
==================================

Test file `test_dl.cpp` (assuming you already have the Mbed library)
~~~{.c}
#include "mbed.h"

Ticker daq;
Serial uart1(p9, p10);
DigitalOut led1(LED1);
DigitalOut led2(LED2);

/**** PPRZ interface ****/
#define DATALINK_C 1
#define UartCheckFreeSpace(_len) uart1.writeable()
#define UartTransmit(_c) uart1.putc(_c)
#define UartSendMessage() {}
#define UartChAvailable() uart1.readable()
#define UartGetch() uart1.getc()

#include "downlink.h"

void dl_parse_msg(void) {
  int msg_id = dl_buffer[1];

  switch (msg_id) {
    case DL_PONG:
      led2 =! led2; // toggle led on new PONG message
      break;
    default:
      break;
  }

}
/************************/

void cb()
{
  // Toggle led 1 when sending PING
  led1 =! led1;
  // Send PPRZ message
  DOWNLINK_SEND_PING(DefaultChannel, DefaultDevice);
}

int main()
{
  uart1.baud(57600); // set serial baud rate for PPRZ
  led1 = led2 = 0; // turn off all LEDs

  daq.attach(&cb, 1); // callback

  while(1) {

    // Your (non-blocking) code

    // Poll new bytes on uart
    DatalinkEvent();

  } // ~while
} // ~main
~~~

With the makefile:
~~~
# This file was automagically generated by mbed.org with PPRZ datalink library addition

GCC_BIN = /usr/bin/
PROJECT = Test_PPRZ_Datalink
OBJECTS = ./test_dl.o
SYS_OBJECTS = ./mbed/LPC1768/GCC_CS/sys.o ./mbed/LPC1768/GCC_CS/cmsis_nvic.o ./mbed/LPC1768/GCC_CS/system_LPC17xx.o ./mbed/LPC1768/GCC_CS/core_cm3.o ./mbed/LPC1768/GCC_CS/startup_LPC17xx.o
INCLUDE_PATHS = -I. -I./mbed -I./mbed/LPC1768 -I./mbed/LPC1768/GCC_CS
LIBRARY_PATHS = -L./mbed/LPC1768/GCC_CS
LIBRARIES = -lmbed -lcapi
LINKER_SCRIPT = ./mbed/LPC1768/GCC_CS/LPC1768.ld

CC = $(GCC_BIN)arm-none-eabi-gcc
CPP = $(GCC_BIN)arm-none-eabi-g++
CC_FLAGS = -c -Os -fno-common -fmessage-length=0 -Wall -fno-exceptions -mcpu=cortex-m3 -mthumb -ffunction-sections -fdata-sections
ONLY_C_FLAGS = -std=gnu99
ONLY_CPP_FLAGS = -std=gnu++98
CC_SYMBOLS = -DTARGET_LPC1768 -DTOOLCHAIN_GCC_CS -DNDEBUG


### PPRZ Datalink ###

PPRZ_DATALINK_PORT = Uart
PPRZ_DATALINK_DIR = .
include $(PPRZ_DATALINK_DIR)/datalink/pprz_datalink.mk
OBJECTS += $(PPRZ_DATALINK_OBJS)
CC_SYMBOLS += $(PPRZ_DATALINK_CFLAGS)

#####################


AS = $(GCC_BIN)arm-none-eabi-as

LD = $(GCC_BIN)arm-none-eabi-gcc
LD_FLAGS = -mcpu=cortex-m3 -mthumb -Wl,--gc-sections
LD_SYS_LIBS = -lstdc++ -lsupc++ -lm -lc -lgcc

OBJCOPY = $(GCC_BIN)arm-none-eabi-objcopy

all: $(PROJECT).bin

clean:
	rm -f $(PROJECT).bin $(PROJECT).elf $(OBJECTS)

.s.o:
	$(AS)  $(CC_FLAGS) $(CC_SYMBOLS) -o $@ $<

.c.o:
	$(CC)  $(CC_FLAGS) $(CC_SYMBOLS) $(ONLY_C_FLAGS)   $(INCLUDE_PATHS) -o $@ $<

.cpp.o:
	$(CPP) $(CC_FLAGS) $(CC_SYMBOLS) $(ONLY_CPP_FLAGS) $(INCLUDE_PATHS) -o $@ $<


$(PROJECT).elf: $(OBJECTS) $(SYS_OBJECTS)
	$(LD) $(LD_FLAGS) -T$(LINKER_SCRIPT) $(LIBRARY_PATHS) -o $@ $^ $(LIBRARIES) $(LD_SYS_LIBS) $(LIBRARIES) $(LD_SYS_LIBS)

$(PROJECT).bin: $(PROJECT).elf
	$(OBJCOPY) -O binary $< $@
~~~
