Commit 73408d1129e80905e55b23318563dec0f4adc872

Authored by Thomas Telkamp
0 parents

Initial commit

LICENSE 0 โ†’ 100644
  1 +++ a/LICENSE
... ... @@ -0,0 +1,26 @@
  1 +Applicable to base64.h and base64.c:
  2 +
  3 +Copyright (C) 2013, SEMTECH S.A.
  4 +All rights reserved.
  5 +
  6 +Redistribution and use in source and binary forms, with or without
  7 +modification, are permitted provided that the following conditions are met:
  8 + * Redistributions of source code must retain the above copyright
  9 + notice, this list of conditions and the following disclaimer.
  10 + * Redistributions in binary form must reproduce the above copyright
  11 + notice, this list of conditions and the following disclaimer in the
  12 + documentation and/or other materials provided with the distribution.
  13 + * Neither the name of the Semtech corporation nor the
  14 + names of its contributors may be used to endorse or promote products
  15 + derived from this software without specific prior written permission.
  16 +
  17 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  18 +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19 +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20 +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
  21 +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22 +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23 +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24 +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  26 +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
... ...
Makefile 0 โ†’ 100644
  1 +++ a/Makefile
... ... @@ -0,0 +1,20 @@
  1 +# single_chan_pkt_fwd
  2 +# Single Channel LoRaWAN Gateway
  3 +
  4 +CC=g++
  5 +CFLAGS=-c -Wall
  6 +LIBS=-lwiringPi
  7 +
  8 +all: single_chan_pkt_fwd
  9 +
  10 +single_chan_pkt_fwd: base64.o main.o
  11 + $(CC) main.o base64.o $(LIBS) -o single_chan_pkt_fwd
  12 +
  13 +main.o: main.cpp
  14 + $(CC) $(CFLAGS) main.cpp
  15 +
  16 +base64.o: base64.c
  17 + $(CC) $(CFLAGS) base64.c
  18 +
  19 +clean:
  20 + rm *.o single_chan_pkt_fwd
0 21 \ No newline at end of file
... ...
README.md 0 โ†’ 100644
  1 +++ a/README.md
... ... @@ -0,0 +1,69 @@
  1 +Single Channel LoRaWAN Gateway
  2 +==============================
  3 +This repository contains a proof-of-concept implementation of a single
  4 +channel LoRaWAN gateway.
  5 +
  6 +It has been tested on the Raspberry Pi platform, using a Semtech SX1272
  7 +transceiver (HopeRF RFM92W).
  8 +
  9 +The code is for testing and development purposes only, and is not meant
  10 +for production usage.
  11 +
  12 +Part of the source has been copied from the Semtech Packet Forwarder
  13 +(with permission).
  14 +
  15 +Maintainer: Thomas Telkamp <thomas@telkamp.eu>
  16 +
  17 +Features
  18 +--------
  19 +- listen on configurable frequency and spreading factor
  20 +- SF7 to SF12
  21 +- status updates
  22 +- can forward to two servers
  23 +
  24 +Not (yet) supported:
  25 +- PACKET_PUSH_ACK processing
  26 +- SX1276
  27 +- SF7BW250 modulation
  28 +- FSK modulation
  29 +- downstream messages (tx)
  30 +
  31 +Dependencies
  32 +------------
  33 +- SPI needs to be enabled on the Raspberry Pi (use raspi-config)
  34 +- WiringPi: a GPIO access library written in C for the BCM2835
  35 + used in the Raspberry Pi.
  36 + sudo apt-get install wiringpi
  37 + see http://wiringpi.com
  38 +- Run packet forwarder as root
  39 +
  40 +Connections
  41 +-----------
  42 +SX1272 - Raspberry
  43 +
  44 +3.3V - 3.3V (header pin #1)
  45 +GND - GND (pin #6)
  46 +MISO - MISO (pin #21)
  47 +MOSI - MOSI (pin #19)
  48 +SCK - CLK (pin #23)
  49 +NSS - GPIO6 (pin #22)
  50 +DIO0 - GPIO7 (pin #7)
  51 +RST - GPIO0 (in #11)
  52 +
  53 +Configuration
  54 +-------------
  55 +
  56 +Defaults:
  57 +
  58 +- LoRa: SF7 at 868.1 Mhz
  59 +- Server: 54.229.214.112, port 1700 (The Things Network: croft.thethings.girovito.nl)
  60 +
  61 +Edit source node (main.cpp) to change configuration (look for: "Configure these values!").
  62 +
  63 +Please set location, email and description.
  64 +
  65 +License
  66 +-------
  67 +The source files in this repository are made available under the Eclipse
  68 +Public License v1.0, except for the base64 implementation, that has been
  69 +copied from the Semtech Packet Forwader.
... ...
base64.c 0 โ†’ 100644
  1 +++ a/base64.c
... ... @@ -0,0 +1,308 @@
  1 +/*
  2 + / _____) _ | |
  3 +( (____ _____ ____ _| |_ _____ ____| |__
  4 + \____ \| ___ | (_ _) ___ |/ ___) _ \
  5 + _____) ) ____| | | || |_| ____( (___| | | |
  6 +(______/|_____)_|_|_| \__)_____)\____)_| |_|
  7 + (C)2013 Semtech-Cycleo
  8 +
  9 +Description:
  10 + Base64 encoding & decoding library
  11 +
  12 +License: Revised BSD License, see LICENSE.TXT file include in the project
  13 +Maintainer: Sylvain Miermont
  14 +*/
  15 +
  16 +
  17 +/* -------------------------------------------------------------------------- */
  18 +/* --- DEPENDANCIES --------------------------------------------------------- */
  19 +
  20 +#include <stdio.h>
  21 +#include <stdlib.h>
  22 +#include <stdint.h>
  23 +
  24 +#include "base64.h"
  25 +
  26 +/* -------------------------------------------------------------------------- */
  27 +/* --- PRIVATE MACROS ------------------------------------------------------- */
  28 +
  29 +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
  30 +#define CRIT(a) fprintf(stderr, "\nCRITICAL file:%s line:%u msg:%s\n", __FILE__, __LINE__,a);exit(EXIT_FAILURE)
  31 +
  32 +//#define DEBUG(args...) fprintf(stderr,"debug: " args) /* diagnostic message that is destined to the user */
  33 +#define DEBUG(args...)
  34 +
  35 +/* -------------------------------------------------------------------------- */
  36 +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
  37 +
  38 +/* -------------------------------------------------------------------------- */
  39 +/* --- PRIVATE MODULE-WIDE VARIABLES ---------------------------------------- */
  40 +
  41 +static char code_62 = '+'; /* RFC 1421 standard character for code 62 */
  42 +static char code_63 = '/'; /* RFC 1421 standard character for code 63 */
  43 +static char code_pad = '='; /* RFC 1421 padding character if padding */
  44 +
  45 +/* -------------------------------------------------------------------------- */
  46 +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
  47 +
  48 +/**
  49 +@brief Convert a code in the range 0-63 to an ASCII character
  50 +*/
  51 +char code_to_char(uint8_t x);
  52 +
  53 +/**
  54 +@brief Convert an ASCII character to a code in the range 0-63
  55 +*/
  56 +uint8_t char_to_code(char x);
  57 +
  58 +/* -------------------------------------------------------------------------- */
  59 +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
  60 +
  61 +char code_to_char(uint8_t x) {
  62 + if (x <= 25) {
  63 + return 'A' + x;
  64 + } else if ((x >= 26) && (x <= 51)) {
  65 + return 'a' + (x-26);
  66 + } else if ((x >= 52) && (x <= 61)) {
  67 + return '0' + (x-52);
  68 + } else if (x == 62) {
  69 + return code_62;
  70 + } else if (x == 63) {
  71 + return code_63;
  72 + } else {
  73 + DEBUG("ERROR: %i IS OUT OF RANGE 0-63 FOR BASE64 ENCODING\n", x);
  74 + exit(EXIT_FAILURE);
  75 + } //TODO: improve error management
  76 +}
  77 +
  78 +uint8_t char_to_code(char x) {
  79 + if ((x >= 'A') && (x <= 'Z')) {
  80 + return (uint8_t)x - (uint8_t)'A';
  81 + } else if ((x >= 'a') && (x <= 'z')) {
  82 + return (uint8_t)x - (uint8_t)'a' + 26;
  83 + } else if ((x >= '0') && (x <= '9')) {
  84 + return (uint8_t)x - (uint8_t)'0' + 52;
  85 + } else if (x == code_62) {
  86 + return 62;
  87 + } else if (x == code_63) {
  88 + return 63;
  89 + } else {
  90 + DEBUG("ERROR: %c (0x%x) IS INVALID CHARACTER FOR BASE64 DECODING\n", x, x);
  91 + exit(EXIT_FAILURE);
  92 + } //TODO: improve error management
  93 +}
  94 +
  95 +/* -------------------------------------------------------------------------- */
  96 +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
  97 +
  98 +int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len) {
  99 + int i;
  100 + int result_len; /* size of the result */
  101 + int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
  102 + int last_bytes; /* number of unsigned chars <3 in the last block */
  103 + int last_chars; /* number of characters <4 in the last block */
  104 + uint32_t b;
  105 +
  106 + /* check input values */
  107 + if ((out == NULL) || (in == NULL)) {
  108 + DEBUG("ERROR: NULL POINTER AS OUTPUT IN BIN_TO_B64\n");
  109 + return -1;
  110 + }
  111 + if (size == 0) {
  112 + *out = 0; /* null string */
  113 + return 0;
  114 + }
  115 +
  116 + /* calculate the number of base64 'blocks' */
  117 + full_blocks = size / 3;
  118 + last_bytes = size % 3;
  119 + switch (last_bytes) {
  120 + case 0: /* no byte left to encode */
  121 + last_chars = 0;
  122 + break;
  123 + case 1: /* 1 byte left to encode -> +2 chars */
  124 + last_chars = 2;
  125 + break;
  126 + case 2: /* 2 bytes left to encode -> +3 chars */
  127 + last_chars = 3;
  128 + break;
  129 + default:
  130 + CRIT("switch default that should not be possible");
  131 + }
  132 +
  133 + /* check if output buffer is big enough */
  134 + result_len = (4*full_blocks) + last_chars;
  135 + if (max_len < (result_len + 1)) { /* 1 char added for string terminator */
  136 + DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN BIN_TO_B64\n");
  137 + return -1;
  138 + }
  139 +
  140 + /* process all the full blocks */
  141 + for (i=0; i < full_blocks; ++i) {
  142 + b = (0xFF & in[3*i] ) << 16;
  143 + b |= (0xFF & in[3*i + 1]) << 8;
  144 + b |= 0xFF & in[3*i + 2];
  145 + out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
  146 + out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
  147 + out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
  148 + out[4*i + 3] = code_to_char( b & 0x3F);
  149 + }
  150 +
  151 + /* process the last 'partial' block and terminate string */
  152 + i = full_blocks;
  153 + if (last_chars == 0) {
  154 + out[4*i] = 0; /* null character to terminate string */
  155 + } else if (last_chars == 2) {
  156 + b = (0xFF & in[3*i] ) << 16;
  157 + out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
  158 + out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
  159 + out[4*i + 2] = 0; /* null character to terminate string */
  160 + } else if (last_chars == 3) {
  161 + b = (0xFF & in[3*i] ) << 16;
  162 + b |= (0xFF & in[3*i + 1]) << 8;
  163 + out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
  164 + out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
  165 + out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
  166 + out[4*i + 3] = 0; /* null character to terminate string */
  167 + }
  168 +
  169 + return result_len;
  170 +}
  171 +
  172 +int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len) {
  173 + int i;
  174 + int result_len; /* size of the result */
  175 + int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
  176 + int last_chars; /* number of characters <4 in the last block */
  177 + int last_bytes; /* number of unsigned chars <3 in the last block */
  178 + uint32_t b;
  179 + ;
  180 +
  181 + /* check input values */
  182 + if ((out == NULL) || (in == NULL)) {
  183 + DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
  184 + return -1;
  185 + }
  186 + if (size == 0) {
  187 + return 0;
  188 + }
  189 +
  190 + /* calculate the number of base64 'blocks' */
  191 + full_blocks = size / 4;
  192 + last_chars = size % 4;
  193 + switch (last_chars) {
  194 + case 0: /* no char left to decode */
  195 + last_bytes = 0;
  196 + break;
  197 + case 1: /* only 1 char left is an error */
  198 + DEBUG("ERROR: ONLY ONE CHAR LEFT IN B64_TO_BIN\n");
  199 + return -1;
  200 + case 2: /* 2 chars left to decode -> +1 byte */
  201 + last_bytes = 1;
  202 + break;
  203 + case 3: /* 3 chars left to decode -> +2 bytes */
  204 + last_bytes = 2;
  205 + break;
  206 + default:
  207 + CRIT("switch default that should not be possible");
  208 + }
  209 +
  210 + /* check if output buffer is big enough */
  211 + result_len = (3*full_blocks) + last_bytes;
  212 + if (max_len < result_len) {
  213 + DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN B64_TO_BIN\n");
  214 + return -1;
  215 + }
  216 +
  217 + /* process all the full blocks */
  218 + for (i=0; i < full_blocks; ++i) {
  219 + b = (0x3F & char_to_code(in[4*i] )) << 18;
  220 + b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
  221 + b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
  222 + b |= 0x3F & char_to_code(in[4*i + 3]);
  223 + out[3*i + 0] = (b >> 16) & 0xFF;
  224 + out[3*i + 1] = (b >> 8 ) & 0xFF;
  225 + out[3*i + 2] = b & 0xFF;
  226 + }
  227 +
  228 + /* process the last 'partial' block */
  229 + i = full_blocks;
  230 + if (last_bytes == 1) {
  231 + b = (0x3F & char_to_code(in[4*i] )) << 18;
  232 + b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
  233 + out[3*i + 0] = (b >> 16) & 0xFF;
  234 + if (((b >> 12) & 0x0F) != 0) {
  235 + DEBUG("WARNING: last character contains unusable bits\n");
  236 + }
  237 + } else if (last_bytes == 2) {
  238 + b = (0x3F & char_to_code(in[4*i] )) << 18;
  239 + b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
  240 + b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
  241 + out[3*i + 0] = (b >> 16) & 0xFF;
  242 + out[3*i + 1] = (b >> 8 ) & 0xFF;
  243 + if (((b >> 6) & 0x03) != 0) {
  244 + DEBUG("WARNING: last character contains unusable bits\n");
  245 + }
  246 + }
  247 +
  248 + return result_len;
  249 +}
  250 +
  251 +int bin_to_b64(const uint8_t * in, int size, char * out, int max_len) {
  252 + int ret;
  253 +
  254 + ret = bin_to_b64_nopad(in, size, out, max_len);
  255 +
  256 + if (ret == -1) {
  257 + return -1;
  258 + }
  259 + switch (ret%4) {
  260 + case 0: /* nothing to do */
  261 + return ret;
  262 + case 1:
  263 + DEBUG("ERROR: INVALID UNPADDED BASE64 STRING\n");
  264 + return -1;
  265 + case 2: /* 2 chars in last block, must add 2 padding char */
  266 + if (max_len > (ret + 2 + 1)) {
  267 + out[ret] = code_pad;
  268 + out[ret+1] = code_pad;
  269 + out[ret+2] = 0;
  270 + return ret+2;
  271 + } else {
  272 + DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
  273 + return -1;
  274 + }
  275 + case 3: /* 3 chars in last block, must add 1 padding char */
  276 + if (max_len > (ret + 1 + 1)) {
  277 + out[ret] = code_pad;
  278 + out[ret+1] = 0;
  279 + return ret+1;
  280 + } else {
  281 + DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
  282 + return -1;
  283 + }
  284 + default:
  285 + CRIT("switch default that should not be possible");
  286 + }
  287 +}
  288 +
  289 +int b64_to_bin(const char * in, int size, uint8_t * out, int max_len) {
  290 + if (in == NULL) {
  291 + DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
  292 + return -1;
  293 + }
  294 + if ((size%4 == 0) && (size >= 4)) { /* potentially padded Base64 */
  295 + if (in[size-2] == code_pad) { /* 2 padding char to ignore */
  296 + return b64_to_bin_nopad(in, size-2, out, max_len);
  297 + } else if (in[size-1] == code_pad) { /* 1 padding char to ignore */
  298 + return b64_to_bin_nopad(in, size-1, out, max_len);
  299 + } else { /* no padding to ignore */
  300 + return b64_to_bin_nopad(in, size, out, max_len);
  301 + }
  302 + } else { /* treat as unpadded Base64 */
  303 + return b64_to_bin_nopad(in, size, out, max_len);
  304 + }
  305 +}
  306 +
  307 +
  308 +/* --- EOF ------------------------------------------------------------------ */
... ...
base64.h 0 โ†’ 100644
  1 +++ a/base64.h
... ... @@ -0,0 +1,62 @@
  1 +/*
  2 + / _____) _ | |
  3 +( (____ _____ ____ _| |_ _____ ____| |__
  4 + \____ \| ___ | (_ _) ___ |/ ___) _ \
  5 + _____) ) ____| | | || |_| ____( (___| | | |
  6 +(______/|_____)_|_|_| \__)_____)\____)_| |_|
  7 + (C)2013 Semtech-Cycleo
  8 +
  9 +Description:
  10 + Base64 encoding & decoding library
  11 +
  12 +License: Revised BSD License, see LICENSE.TXT file include in the project
  13 +Maintainer: Sylvain Miermont
  14 +*/
  15 +
  16 +
  17 +#ifndef _BASE64_H
  18 +#define _BASE64_H
  19 +
  20 +/* -------------------------------------------------------------------------- */
  21 +/* --- DEPENDANCIES --------------------------------------------------------- */
  22 +
  23 +#include <stdint.h> /* C99 types */
  24 +
  25 +/* -------------------------------------------------------------------------- */
  26 +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
  27 +
  28 +/**
  29 +@brief Encode binary data in Base64 string (no padding)
  30 +@param in pointer to a table of binary data
  31 +@param size number of bytes to be encoded to base64
  32 +@param out pointer to a string where the function will output encoded data
  33 +@param max_len max length of the out string (including null char)
  34 +@return >=0 length of the resulting string (w/o null char), -1 for error
  35 +*/
  36 +int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len);
  37 +
  38 +/**
  39 +@brief Decode Base64 string to binary data (no padding)
  40 +@param in string containing only base64 valid characters
  41 +@param size number of characters to be decoded from base64 (w/o null char)
  42 +@param out pointer to a data buffer where the function will output decoded data
  43 +@param out_max_len usable size of the output data buffer
  44 +@return >=0 number of bytes written to the data buffer, -1 for error
  45 +*/
  46 +int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len);
  47 +
  48 +/* === derivative functions === */
  49 +
  50 +/**
  51 +@brief Encode binary data in Base64 string (with added padding)
  52 +*/
  53 +int bin_to_b64(const uint8_t * in, int size, char * out, int max_len);
  54 +
  55 +/**
  56 +@brief Decode Base64 string to binary data (remove padding if necessary)
  57 +*/
  58 +int b64_to_bin(const char * in, int size, uint8_t * out, int max_len);
  59 +
  60 +#endif
  61 +
  62 +/* --- EOF ------------------------------------------------------------------ */
... ...
main.cpp 0 โ†’ 100644
  1 +++ a/main.cpp
... ... @@ -0,0 +1,561 @@
  1 +/*******************************************************************************
  2 + *
  3 + * Copyright (c) 2015 Thomas Telkamp
  4 + *
  5 + * All rights reserved. This program and the accompanying materials
  6 + * are made available under the terms of the Eclipse Public License v1.0
  7 + * which accompanies this distribution, and is available at
  8 + * http://www.eclipse.org/legal/epl-v10.html
  9 + *
  10 + *******************************************************************************/
  11 +
  12 +#include <string>
  13 +#include <stdio.h>
  14 +#include <sys/types.h>
  15 +#include <sys/socket.h>
  16 +#include <arpa/inet.h>
  17 +#include <string.h>
  18 +#include <iostream>
  19 +#include <cstdlib>
  20 +#include <sys/time.h>
  21 +#include <cstring>
  22 +
  23 +#include <sys/ioctl.h>
  24 +#include <net/if.h>
  25 +
  26 +using namespace std;
  27 +
  28 +#include "base64.h"
  29 +
  30 +#include <wiringPi.h>
  31 +#include <wiringPiSPI.h>
  32 +
  33 +typedef bool boolean;
  34 +typedef unsigned char byte;
  35 +
  36 +static const int CHANNEL = 0;
  37 +
  38 +byte currentMode = 0x81;
  39 +
  40 +char message[256];
  41 +char b64[256];
  42 +
  43 +byte receivedbytes;
  44 +
  45 +struct sockaddr_in si_other;
  46 +int s, slen=sizeof(si_other);
  47 +struct ifreq ifr;
  48 +
  49 +uint32_t cp_nb_rx_rcv;
  50 +uint32_t cp_nb_rx_ok;
  51 +uint32_t cp_nb_rx_bad;
  52 +uint32_t cp_nb_rx_nocrc;
  53 +uint32_t cp_up_pkt_fwd;
  54 +
  55 +enum sf_t { SF7=7, SF8, SF9, SF10, SF11, SF12 };
  56 +
  57 +/*******************************************************************************
  58 + *
  59 + * Configure these values!
  60 + *
  61 + *******************************************************************************/
  62 +
  63 +// SX1272 - Raspberry connections
  64 +int ssPin = 6;
  65 +int dio0 = 7;
  66 +int RST = 0;
  67 +
  68 +// Set spreading factor (SF7 - SF12)
  69 +sf_t sf = SF7;
  70 +
  71 +// Set center frequency
  72 +uint32_t freq = 868100000; // in Mhz! (868.1)
  73 +
  74 +// Set location
  75 +float lat=0.0;
  76 +float lon=0.0;
  77 +int alt=0;
  78 +
  79 +/* Informal status fields */
  80 +static char platform[24] = "SX1272"; /* platform definition */
  81 +static char email[40] = ""; /* used for contact email */
  82 +static char description[64] = ""; /* used for free form description */
  83 +
  84 +// define servers
  85 +// TODO: use host names and dns
  86 +#define SERVER1 "54.229.214.112" // The Things Network: croft.thethings.girovito.nl
  87 +//#define SERVER2 "192.168.1.10" // local
  88 +#define PORT 1700 // The port on which to send data
  89 +
  90 +// #############################################
  91 +// #############################################
  92 +
  93 +#define REG_FIFO 0x00
  94 +#define REG_FIFO_ADDR_PTR 0x0D
  95 +#define REG_FIFO_TX_BASE_AD 0x0E
  96 +#define REG_FIFO_RX_BASE_AD 0x0F
  97 +#define REG_RX_NB_BYTES 0x13
  98 +#define REG_OPMODE 0x01
  99 +#define REG_FIFO_RX_CURRENT_ADDR 0x10
  100 +#define REG_IRQ_FLAGS 0x12
  101 +#define REG_DIO_MAPPING_1 0x40
  102 +#define REG_DIO_MAPPING_2 0x41
  103 +#define REG_MODEM_CONFIG 0x1D
  104 +#define REG_MODEM_CONFIG2 0x1E
  105 +#define REG_SYMB_TIMEOUT_LSB 0x1F
  106 +#define REG_PKT_SNR_VALUE 0x19
  107 +#define REG_PAYLOAD_LENGTH 0x22
  108 +#define REG_IRQ_FLAGS_MASK 0x11
  109 +#define REG_MAX_PAYLOAD_LENGTH 0x23
  110 +#define REG_HOP_PERIOD 0x24
  111 +#define REG_SYNC_WORD 0x39
  112 +#define REG_VERSION 0x42
  113 +
  114 +#define SX72_MODE_RX_CONTINUOS 0x85
  115 +#define SX72_MODE_TX 0x83
  116 +#define SX72_MODE_SLEEP 0x80
  117 +#define SX72_MODE_STANDBY 0x81
  118 +
  119 +#define PAYLOAD_LENGTH 0x40
  120 +
  121 +// LOW NOISE AMPLIFIER
  122 +#define REG_LNA 0x0C
  123 +#define LNA_MAX_GAIN 0x23
  124 +#define LNA_OFF_GAIN 0x00
  125 +#define LNA_LOW_GAIN 0x20
  126 +
  127 +// CONF REG
  128 +#define REG1 0x0A
  129 +#define REG2 0x84
  130 +
  131 +#define SX72_MC2_FSK 0x00
  132 +#define SX72_MC2_SF7 0x70
  133 +#define SX72_MC2_SF8 0x80
  134 +#define SX72_MC2_SF9 0x90
  135 +#define SX72_MC2_SF10 0xA0
  136 +#define SX72_MC2_SF11 0xB0
  137 +#define SX72_MC2_SF12 0xC0
  138 +
  139 +#define SX72_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12
  140 +
  141 +// FRF
  142 +#define REG_FRF_MSB 0x06
  143 +#define REG_FRF_MID 0x07
  144 +#define REG_FRF_LSB 0x08
  145 +
  146 +#define FRF_MSB 0xD9 // 868.1 Mhz
  147 +#define FRF_MID 0x06
  148 +#define FRF_LSB 0x66
  149 +
  150 +#define BUFLEN 2048 //Max length of buffer
  151 +
  152 +#define PROTOCOL_VERSION 1
  153 +#define PKT_PUSH_DATA 0
  154 +#define PKT_PUSH_ACK 1
  155 +#define PKT_PULL_DATA 2
  156 +#define PKT_PULL_RESP 3
  157 +#define PKT_PULL_ACK 4
  158 +
  159 +#define TX_BUFF_SIZE 2048
  160 +#define STATUS_SIZE 1024
  161 +
  162 +void die(const char *s)
  163 +{
  164 + perror(s);
  165 + exit(1);
  166 +}
  167 +
  168 +void selectreceiver()
  169 +{
  170 + digitalWrite(ssPin, LOW);
  171 +}
  172 +
  173 +void unselectreceiver()
  174 +{
  175 + digitalWrite(ssPin, HIGH);
  176 +}
  177 +
  178 +byte readRegister(byte addr)
  179 +{
  180 + unsigned char spibuf[2];
  181 +
  182 + selectreceiver();
  183 + spibuf[0] = addr & 0x7F;
  184 + spibuf[1] = 0x00;
  185 + wiringPiSPIDataRW(CHANNEL, spibuf, 2);
  186 + unselectreceiver();
  187 +
  188 + return spibuf[1];
  189 +}
  190 +
  191 +void writeRegister(byte addr, byte value)
  192 +{
  193 + unsigned char spibuf[2];
  194 +
  195 + spibuf[0] = addr | 0x80;
  196 + spibuf[1] = value;
  197 + selectreceiver();
  198 + wiringPiSPIDataRW(CHANNEL, spibuf, 2);
  199 +
  200 + unselectreceiver();
  201 +}
  202 +
  203 +
  204 +boolean receivePkt(char *payload)
  205 +{
  206 +
  207 + // clear rxDone
  208 + writeRegister(REG_IRQ_FLAGS, 0x40);
  209 +
  210 + int irqflags = readRegister(REG_IRQ_FLAGS);
  211 +
  212 + cp_nb_rx_rcv++;
  213 +
  214 + // payload crc: 0x20
  215 + if((irqflags & 0x20) == 0x20)
  216 + {
  217 + printf("CRC error\n");
  218 + writeRegister(REG_IRQ_FLAGS, 0x20);
  219 + return false;
  220 + } else {
  221 +
  222 + cp_nb_rx_ok++;
  223 +
  224 + byte currentAddr = readRegister(REG_FIFO_RX_CURRENT_ADDR);
  225 + byte receivedCount = readRegister(REG_RX_NB_BYTES);
  226 + receivedbytes = receivedCount;
  227 +
  228 + writeRegister(REG_FIFO_ADDR_PTR, currentAddr);
  229 +
  230 + for(int i = 0; i < receivedCount; i++)
  231 + {
  232 + payload[i] = (char)readRegister(REG_FIFO);
  233 + }
  234 + }
  235 + return true;
  236 +}
  237 +
  238 +void SetupLoRa()
  239 +{
  240 +
  241 + byte version = readRegister(REG_VERSION);
  242 + if (version == 0x12) {
  243 + // sx1276
  244 + printf("SX1276 transceiver not yet supported.\n");
  245 + exit(1);
  246 + } else if (version == 0x22) {
  247 + // sx1272
  248 + printf("SX1272 detected, starting.\n");
  249 + } else {
  250 + printf("Unrecognized transceiver.\n");
  251 + exit(1);
  252 + }
  253 +
  254 + writeRegister(REG_OPMODE, SX72_MODE_SLEEP);
  255 +
  256 + // set frequency
  257 + uint64_t frf = ((uint64_t)freq << 19) / 32000000;
  258 + writeRegister(REG_FRF_MSB, (uint8_t)(frf>>16) );
  259 + writeRegister(REG_FRF_MID, (uint8_t)(frf>> 8) );
  260 + writeRegister(REG_FRF_LSB, (uint8_t)(frf>> 0) );
  261 +
  262 + writeRegister(REG_SYNC_WORD, 0x34); // LoRaWAN public sync word
  263 +
  264 + if (sf == SF11 || sf == SF12) {
  265 + writeRegister(REG_MODEM_CONFIG,0x0B);
  266 + } else {
  267 + writeRegister(REG_MODEM_CONFIG,0x0A);
  268 + }
  269 + writeRegister(REG_MODEM_CONFIG2,(sf<<4) | 0x04);
  270 +
  271 + if (sf == SF10 || sf == SF11 || sf == SF12) {
  272 + writeRegister(REG_SYMB_TIMEOUT_LSB,0x05);
  273 + } else {
  274 + writeRegister(REG_SYMB_TIMEOUT_LSB,0x08);
  275 + }
  276 + writeRegister(REG_MAX_PAYLOAD_LENGTH,0x80);
  277 + writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH);
  278 + writeRegister(REG_HOP_PERIOD,0xFF);
  279 + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_BASE_AD));
  280 +
  281 + // Set Continous Receive Mode
  282 + writeRegister(REG_LNA, LNA_MAX_GAIN); // max lna gain
  283 + writeRegister(REG_OPMODE, SX72_MODE_RX_CONTINUOS);
  284 +
  285 +}
  286 +
  287 +void sendudp(char *msg, int length) {
  288 +
  289 +//send the update
  290 +#ifdef SERVER1
  291 + inet_aton(SERVER1 , &si_other.sin_addr);
  292 + if (sendto(s, (char *)msg, length, 0 , (struct sockaddr *) &si_other, slen)==-1)
  293 + {
  294 + die("sendto()");
  295 + }
  296 +#endif
  297 +
  298 +#ifdef SERVER2
  299 + inet_aton(SERVER2 , &si_other.sin_addr);
  300 + if (sendto(s, (char *)msg, length , 0 , (struct sockaddr *) &si_other, slen)==-1)
  301 + {
  302 + die("sendto()");
  303 + }
  304 +#endif
  305 +}
  306 +
  307 +void sendstat() {
  308 +
  309 + static char status_report[STATUS_SIZE]; /* status report as a JSON object */
  310 + char stat_timestamp[24];
  311 + time_t t;
  312 +
  313 + int stat_index=0;
  314 +
  315 + /* pre-fill the data buffer with fixed fields */
  316 + status_report[0] = PROTOCOL_VERSION;
  317 + status_report[3] = PKT_PUSH_DATA;
  318 +
  319 + status_report[4] = (unsigned char)ifr.ifr_hwaddr.sa_data[0];
  320 + status_report[5] = (unsigned char)ifr.ifr_hwaddr.sa_data[1];
  321 + status_report[6] = (unsigned char)ifr.ifr_hwaddr.sa_data[2];
  322 + status_report[7] = 0xFF;
  323 + status_report[8] = 0xFF;
  324 + status_report[9] = (unsigned char)ifr.ifr_hwaddr.sa_data[3];
  325 + status_report[10] = (unsigned char)ifr.ifr_hwaddr.sa_data[4];
  326 + status_report[11] = (unsigned char)ifr.ifr_hwaddr.sa_data[5];
  327 +
  328 + /* start composing datagram with the header */
  329 + uint8_t token_h = (uint8_t)rand(); /* random token */
  330 + uint8_t token_l = (uint8_t)rand(); /* random token */
  331 + status_report[1] = token_h;
  332 + status_report[2] = token_l;
  333 + stat_index = 12; /* 12-byte header */
  334 +
  335 + /* get timestamp for statistics */
  336 + t = time(NULL);
  337 + strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t));
  338 +
  339 + int j = snprintf((char *)(status_report + stat_index), STATUS_SIZE-stat_index, "{\"stat\":{\"time\":\"%s\",\"lati\":%.5f,\"long\":%.5f,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u,\"pfrm\":\"%s\",\"mail\":\"%s\",\"desc\":\"%s\"}}", stat_timestamp, lat, lon, (int)alt, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, (float)0, 0, 0,platform,email,description);
  340 + stat_index += j;
  341 + status_report[stat_index] = 0; /* add string terminator, for safety */
  342 +
  343 + printf("stat update: %s\n", (char *)(status_report+12)); /* DEBUG: display JSON stat */
  344 +
  345 + //send the update
  346 + sendudp(status_report, stat_index);
  347 +
  348 +}
  349 +
  350 +void receivepacket() {
  351 +
  352 + long int SNR;
  353 +
  354 + if(digitalRead(dio0) == 1)
  355 + {
  356 + if(receivePkt(message)) {
  357 + byte value = readRegister(REG_PKT_SNR_VALUE);
  358 + if( value & 0x80 ) // The SNR sign bit is 1
  359 + {
  360 + // Invert and divide by 4
  361 + value = ( ( ~value + 1 ) & 0xFF ) >> 2;
  362 + SNR = -value;
  363 + }
  364 + else
  365 + {
  366 + // Divide by 4
  367 + SNR = ( value & 0xFF ) >> 2;
  368 + }
  369 + printf("Packet RSSI: %d, ",readRegister(0x1A)-139);
  370 + printf("RSSI: %d, ",readRegister(0x1B)-139);
  371 + printf("SNR: %li, ",SNR);
  372 + printf("Length: %i",(int)receivedbytes);
  373 + printf("\n");
  374 +
  375 + int j;
  376 + j = bin_to_b64((uint8_t *)message, receivedbytes, (char *)(b64), 341);
  377 + //fwrite(b64, sizeof(char), j, stdout);
  378 +
  379 + char buff_up[TX_BUFF_SIZE]; /* buffer to compose the upstream packet */
  380 + int buff_index=0;
  381 +
  382 + /* gateway <-> MAC protocol variables */
  383 + //static uint32_t net_mac_h; /* Most Significant Nibble, network order */
  384 + //static uint32_t net_mac_l; /* Least Significant Nibble, network order */
  385 +
  386 + /* pre-fill the data buffer with fixed fields */
  387 + buff_up[0] = PROTOCOL_VERSION;
  388 + buff_up[3] = PKT_PUSH_DATA;
  389 +
  390 + /* process some of the configuration variables */
  391 + //net_mac_h = htonl((uint32_t)(0xFFFFFFFF & (lgwm>>32)));
  392 + //net_mac_l = htonl((uint32_t)(0xFFFFFFFF & lgwm ));
  393 + //*(uint32_t *)(buff_up + 4) = net_mac_h;
  394 + //*(uint32_t *)(buff_up + 8) = net_mac_l;
  395 +
  396 + buff_up[4] = (unsigned char)ifr.ifr_hwaddr.sa_data[0];
  397 + buff_up[5] = (unsigned char)ifr.ifr_hwaddr.sa_data[1];
  398 + buff_up[6] = (unsigned char)ifr.ifr_hwaddr.sa_data[2];
  399 + buff_up[7] = 0xFF;
  400 + buff_up[8] = 0xFF;
  401 + buff_up[9] = (unsigned char)ifr.ifr_hwaddr.sa_data[3];
  402 + buff_up[10] = (unsigned char)ifr.ifr_hwaddr.sa_data[4];
  403 + buff_up[11] = (unsigned char)ifr.ifr_hwaddr.sa_data[5];
  404 +
  405 + /* start composing datagram with the header */
  406 + uint8_t token_h = (uint8_t)rand(); /* random token */
  407 + uint8_t token_l = (uint8_t)rand(); /* random token */
  408 + buff_up[1] = token_h;
  409 + buff_up[2] = token_l;
  410 + buff_index = 12; /* 12-byte header */
  411 +
  412 + // TODO: tmst can jump is time is (re)set, not good.
  413 + struct timeval now;
  414 + gettimeofday(&now, NULL);
  415 + uint32_t tmst = (uint32_t)(now.tv_sec*1000000 + now.tv_usec);
  416 +
  417 + /* start of JSON structure */
  418 + memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[", 9);
  419 + buff_index += 9;
  420 + buff_up[buff_index] = '{';
  421 + ++buff_index;
  422 + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, "\"tmst\":%u", tmst);
  423 + buff_index += j;
  424 + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf", 0, 0, (double)freq/1000000);
  425 + buff_index += j;
  426 + memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":1", 9);
  427 + buff_index += 9;
  428 + memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"LORA\"", 14);
  429 + buff_index += 14;
  430 + /* Lora datarate & bandwidth, 16-19 useful chars */
  431 + switch (sf) {
  432 + case SF7:
  433 + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF7", 12);
  434 + buff_index += 12;
  435 + break;
  436 + case SF8:
  437 + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF8", 12);
  438 + buff_index += 12;
  439 + break;
  440 + case SF9:
  441 + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF9", 12);
  442 + buff_index += 12;
  443 + break;
  444 + case SF10:
  445 + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF10", 13);
  446 + buff_index += 13;
  447 + break;
  448 + case SF11:
  449 + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF11", 13);
  450 + buff_index += 13;
  451 + break;
  452 + case SF12:
  453 + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF12", 13);
  454 + buff_index += 13;
  455 + break;
  456 + default:
  457 + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF?", 12);
  458 + buff_index += 12;
  459 + }
  460 + memcpy((void *)(buff_up + buff_index), (void *)"BW125\"", 6);
  461 + buff_index += 6;
  462 + memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/5\"", 13);
  463 + buff_index += 13;
  464 + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"lsnr\":%li", SNR);
  465 + buff_index += j;
  466 + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%d,\"size\":%u", readRegister(0x1A)-139, receivedbytes);
  467 + buff_index += j;
  468 + memcpy((void *)(buff_up + buff_index), (void *)",\"data\":\"", 9);
  469 + buff_index += 9;
  470 + j = bin_to_b64((uint8_t *)message, receivedbytes, (char *)(buff_up + buff_index), 341);
  471 + buff_index += j;
  472 + buff_up[buff_index] = '"';
  473 + ++buff_index;
  474 +
  475 + /* End of packet serialization */
  476 + buff_up[buff_index] = '}';
  477 + ++buff_index;
  478 + buff_up[buff_index] = ']';
  479 + ++buff_index;
  480 + /* end of JSON datagram payload */
  481 + buff_up[buff_index] = '}';
  482 + ++buff_index;
  483 + buff_up[buff_index] = 0; /* add string terminator, for safety */
  484 +
  485 + printf("rxpk update: %s\n", (char *)(buff_up + 12)); /* DEBUG: display JSON payload */
  486 +
  487 + //send the messages
  488 + sendudp(buff_up, buff_index);
  489 +
  490 + fflush(stdout);
  491 +
  492 + } // received a message
  493 +
  494 + } // dio0=1
  495 +}
  496 +
  497 +int main () {
  498 +
  499 + struct timeval nowtime;
  500 + uint32_t lasttime;
  501 +
  502 + wiringPiSetup () ;
  503 + pinMode(ssPin, OUTPUT);
  504 + pinMode(dio0, INPUT);
  505 + pinMode(RST, OUTPUT);
  506 +
  507 + digitalWrite(RST, HIGH);
  508 + delay(100);
  509 + digitalWrite(RST, LOW);
  510 + delay(200);
  511 +
  512 + //int fd = ;
  513 + wiringPiSPISetup(CHANNEL, 500000);
  514 + //cout << "Init result: " << fd << endl;
  515 +
  516 + SetupLoRa();
  517 +
  518 + if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
  519 + {
  520 + die("socket");
  521 + }
  522 + memset((char *) &si_other, 0, sizeof(si_other));
  523 + si_other.sin_family = AF_INET;
  524 + si_other.sin_port = htons(PORT);
  525 +
  526 + ifr.ifr_addr.sa_family = AF_INET;
  527 + strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1); // can we rely on eth0?
  528 + ioctl(s, SIOCGIFHWADDR, &ifr);
  529 +
  530 + /* display result */
  531 + printf("Gateway ID: %.2x:%.2x:%.2x:ff:ff:%.2x:%.2x:%.2x\n",
  532 + (unsigned char)ifr.ifr_hwaddr.sa_data[0],
  533 + (unsigned char)ifr.ifr_hwaddr.sa_data[1],
  534 + (unsigned char)ifr.ifr_hwaddr.sa_data[2],
  535 + (unsigned char)ifr.ifr_hwaddr.sa_data[3],
  536 + (unsigned char)ifr.ifr_hwaddr.sa_data[4],
  537 + (unsigned char)ifr.ifr_hwaddr.sa_data[5]);
  538 +
  539 + printf("Listening at SF%i on %.6lf Mhz.\n", sf,(double)freq/1000000);
  540 + printf("------------------\n");
  541 +
  542 + while(1) {
  543 +
  544 + receivepacket();
  545 +
  546 + gettimeofday(&nowtime, NULL);
  547 + uint32_t nowseconds = (uint32_t)(nowtime.tv_sec);
  548 + if (nowseconds - lasttime >= 30) {
  549 + lasttime = nowseconds;
  550 + sendstat();
  551 + cp_nb_rx_rcv = 0;
  552 + cp_nb_rx_ok = 0;
  553 + cp_up_pkt_fwd = 0;
  554 + }
  555 + delay(1);
  556 + }
  557 +
  558 + return (0);
  559 +
  560 +}
  561 +
... ...