diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6bc539e --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +Applicable to base64.h and base64.c: + +Copyright (C) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..228e00f --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +# single_chan_pkt_fwd +# Single Channel LoRaWAN Gateway + +CC=g++ +CFLAGS=-c -Wall +LIBS=-lwiringPi + +all: single_chan_pkt_fwd + +single_chan_pkt_fwd: base64.o main.o + $(CC) main.o base64.o $(LIBS) -o single_chan_pkt_fwd + +main.o: main.cpp + $(CC) $(CFLAGS) main.cpp + +base64.o: base64.c + $(CC) $(CFLAGS) base64.c + +clean: + rm *.o single_chan_pkt_fwd \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f2d955 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +Single Channel LoRaWAN Gateway +============================== +This repository contains a proof-of-concept implementation of a single +channel LoRaWAN gateway. + +It has been tested on the Raspberry Pi platform, using a Semtech SX1272 +transceiver (HopeRF RFM92W). + +The code is for testing and development purposes only, and is not meant +for production usage. + +Part of the source has been copied from the Semtech Packet Forwarder +(with permission). + +Maintainer: Thomas Telkamp + +Features +-------- +- listen on configurable frequency and spreading factor +- SF7 to SF12 +- status updates +- can forward to two servers + +Not (yet) supported: +- PACKET_PUSH_ACK processing +- SX1276 +- SF7BW250 modulation +- FSK modulation +- downstream messages (tx) + +Dependencies +------------ +- SPI needs to be enabled on the Raspberry Pi (use raspi-config) +- WiringPi: a GPIO access library written in C for the BCM2835 + used in the Raspberry Pi. + sudo apt-get install wiringpi + see http://wiringpi.com +- Run packet forwarder as root + +Connections +----------- +SX1272 - Raspberry + +3.3V - 3.3V (header pin #1) +GND - GND (pin #6) +MISO - MISO (pin #21) +MOSI - MOSI (pin #19) +SCK - CLK (pin #23) +NSS - GPIO6 (pin #22) +DIO0 - GPIO7 (pin #7) +RST - GPIO0 (in #11) + +Configuration +------------- + +Defaults: + +- LoRa: SF7 at 868.1 Mhz +- Server: 54.229.214.112, port 1700 (The Things Network: croft.thethings.girovito.nl) + +Edit source node (main.cpp) to change configuration (look for: "Configure these values!"). + +Please set location, email and description. + +License +------- +The source files in this repository are made available under the Eclipse +Public License v1.0, except for the base64 implementation, that has been +copied from the Semtech Packet Forwader. diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..bef7165 --- /dev/null +++ b/base64.c @@ -0,0 +1,308 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Base64 encoding & decoding library + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include +#include +#include + +#include "base64.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define CRIT(a) fprintf(stderr, "\nCRITICAL file:%s line:%u msg:%s\n", __FILE__, __LINE__,a);exit(EXIT_FAILURE) + +//#define DEBUG(args...) fprintf(stderr,"debug: " args) /* diagnostic message that is destined to the user */ +#define DEBUG(args...) + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MODULE-WIDE VARIABLES ---------------------------------------- */ + +static char code_62 = '+'; /* RFC 1421 standard character for code 62 */ +static char code_63 = '/'; /* RFC 1421 standard character for code 63 */ +static char code_pad = '='; /* RFC 1421 padding character if padding */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +/** +@brief Convert a code in the range 0-63 to an ASCII character +*/ +char code_to_char(uint8_t x); + +/** +@brief Convert an ASCII character to a code in the range 0-63 +*/ +uint8_t char_to_code(char x); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +char code_to_char(uint8_t x) { + if (x <= 25) { + return 'A' + x; + } else if ((x >= 26) && (x <= 51)) { + return 'a' + (x-26); + } else if ((x >= 52) && (x <= 61)) { + return '0' + (x-52); + } else if (x == 62) { + return code_62; + } else if (x == 63) { + return code_63; + } else { + DEBUG("ERROR: %i IS OUT OF RANGE 0-63 FOR BASE64 ENCODING\n", x); + exit(EXIT_FAILURE); + } //TODO: improve error management +} + +uint8_t char_to_code(char x) { + if ((x >= 'A') && (x <= 'Z')) { + return (uint8_t)x - (uint8_t)'A'; + } else if ((x >= 'a') && (x <= 'z')) { + return (uint8_t)x - (uint8_t)'a' + 26; + } else if ((x >= '0') && (x <= '9')) { + return (uint8_t)x - (uint8_t)'0' + 52; + } else if (x == code_62) { + return 62; + } else if (x == code_63) { + return 63; + } else { + DEBUG("ERROR: %c (0x%x) IS INVALID CHARACTER FOR BASE64 DECODING\n", x, x); + exit(EXIT_FAILURE); + } //TODO: improve error management +} + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len) { + int i; + int result_len; /* size of the result */ + int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */ + int last_bytes; /* number of unsigned chars <3 in the last block */ + int last_chars; /* number of characters <4 in the last block */ + uint32_t b; + + /* check input values */ + if ((out == NULL) || (in == NULL)) { + DEBUG("ERROR: NULL POINTER AS OUTPUT IN BIN_TO_B64\n"); + return -1; + } + if (size == 0) { + *out = 0; /* null string */ + return 0; + } + + /* calculate the number of base64 'blocks' */ + full_blocks = size / 3; + last_bytes = size % 3; + switch (last_bytes) { + case 0: /* no byte left to encode */ + last_chars = 0; + break; + case 1: /* 1 byte left to encode -> +2 chars */ + last_chars = 2; + break; + case 2: /* 2 bytes left to encode -> +3 chars */ + last_chars = 3; + break; + default: + CRIT("switch default that should not be possible"); + } + + /* check if output buffer is big enough */ + result_len = (4*full_blocks) + last_chars; + if (max_len < (result_len + 1)) { /* 1 char added for string terminator */ + DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN BIN_TO_B64\n"); + return -1; + } + + /* process all the full blocks */ + for (i=0; i < full_blocks; ++i) { + b = (0xFF & in[3*i] ) << 16; + b |= (0xFF & in[3*i + 1]) << 8; + b |= 0xFF & in[3*i + 2]; + out[4*i + 0] = code_to_char((b >> 18) & 0x3F); + out[4*i + 1] = code_to_char((b >> 12) & 0x3F); + out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F); + out[4*i + 3] = code_to_char( b & 0x3F); + } + + /* process the last 'partial' block and terminate string */ + i = full_blocks; + if (last_chars == 0) { + out[4*i] = 0; /* null character to terminate string */ + } else if (last_chars == 2) { + b = (0xFF & in[3*i] ) << 16; + out[4*i + 0] = code_to_char((b >> 18) & 0x3F); + out[4*i + 1] = code_to_char((b >> 12) & 0x3F); + out[4*i + 2] = 0; /* null character to terminate string */ + } else if (last_chars == 3) { + b = (0xFF & in[3*i] ) << 16; + b |= (0xFF & in[3*i + 1]) << 8; + out[4*i + 0] = code_to_char((b >> 18) & 0x3F); + out[4*i + 1] = code_to_char((b >> 12) & 0x3F); + out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F); + out[4*i + 3] = 0; /* null character to terminate string */ + } + + return result_len; +} + +int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len) { + int i; + int result_len; /* size of the result */ + int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */ + int last_chars; /* number of characters <4 in the last block */ + int last_bytes; /* number of unsigned chars <3 in the last block */ + uint32_t b; + ; + + /* check input values */ + if ((out == NULL) || (in == NULL)) { + DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n"); + return -1; + } + if (size == 0) { + return 0; + } + + /* calculate the number of base64 'blocks' */ + full_blocks = size / 4; + last_chars = size % 4; + switch (last_chars) { + case 0: /* no char left to decode */ + last_bytes = 0; + break; + case 1: /* only 1 char left is an error */ + DEBUG("ERROR: ONLY ONE CHAR LEFT IN B64_TO_BIN\n"); + return -1; + case 2: /* 2 chars left to decode -> +1 byte */ + last_bytes = 1; + break; + case 3: /* 3 chars left to decode -> +2 bytes */ + last_bytes = 2; + break; + default: + CRIT("switch default that should not be possible"); + } + + /* check if output buffer is big enough */ + result_len = (3*full_blocks) + last_bytes; + if (max_len < result_len) { + DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN B64_TO_BIN\n"); + return -1; + } + + /* process all the full blocks */ + for (i=0; i < full_blocks; ++i) { + b = (0x3F & char_to_code(in[4*i] )) << 18; + b |= (0x3F & char_to_code(in[4*i + 1])) << 12; + b |= (0x3F & char_to_code(in[4*i + 2])) << 6; + b |= 0x3F & char_to_code(in[4*i + 3]); + out[3*i + 0] = (b >> 16) & 0xFF; + out[3*i + 1] = (b >> 8 ) & 0xFF; + out[3*i + 2] = b & 0xFF; + } + + /* process the last 'partial' block */ + i = full_blocks; + if (last_bytes == 1) { + b = (0x3F & char_to_code(in[4*i] )) << 18; + b |= (0x3F & char_to_code(in[4*i + 1])) << 12; + out[3*i + 0] = (b >> 16) & 0xFF; + if (((b >> 12) & 0x0F) != 0) { + DEBUG("WARNING: last character contains unusable bits\n"); + } + } else if (last_bytes == 2) { + b = (0x3F & char_to_code(in[4*i] )) << 18; + b |= (0x3F & char_to_code(in[4*i + 1])) << 12; + b |= (0x3F & char_to_code(in[4*i + 2])) << 6; + out[3*i + 0] = (b >> 16) & 0xFF; + out[3*i + 1] = (b >> 8 ) & 0xFF; + if (((b >> 6) & 0x03) != 0) { + DEBUG("WARNING: last character contains unusable bits\n"); + } + } + + return result_len; +} + +int bin_to_b64(const uint8_t * in, int size, char * out, int max_len) { + int ret; + + ret = bin_to_b64_nopad(in, size, out, max_len); + + if (ret == -1) { + return -1; + } + switch (ret%4) { + case 0: /* nothing to do */ + return ret; + case 1: + DEBUG("ERROR: INVALID UNPADDED BASE64 STRING\n"); + return -1; + case 2: /* 2 chars in last block, must add 2 padding char */ + if (max_len > (ret + 2 + 1)) { + out[ret] = code_pad; + out[ret+1] = code_pad; + out[ret+2] = 0; + return ret+2; + } else { + DEBUG("ERROR: not enough room to add padding in bin_to_b64\n"); + return -1; + } + case 3: /* 3 chars in last block, must add 1 padding char */ + if (max_len > (ret + 1 + 1)) { + out[ret] = code_pad; + out[ret+1] = 0; + return ret+1; + } else { + DEBUG("ERROR: not enough room to add padding in bin_to_b64\n"); + return -1; + } + default: + CRIT("switch default that should not be possible"); + } +} + +int b64_to_bin(const char * in, int size, uint8_t * out, int max_len) { + if (in == NULL) { + DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n"); + return -1; + } + if ((size%4 == 0) && (size >= 4)) { /* potentially padded Base64 */ + if (in[size-2] == code_pad) { /* 2 padding char to ignore */ + return b64_to_bin_nopad(in, size-2, out, max_len); + } else if (in[size-1] == code_pad) { /* 1 padding char to ignore */ + return b64_to_bin_nopad(in, size-1, out, max_len); + } else { /* no padding to ignore */ + return b64_to_bin_nopad(in, size, out, max_len); + } + } else { /* treat as unpadded Base64 */ + return b64_to_bin_nopad(in, size, out, max_len); + } +} + + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..c0369aa --- /dev/null +++ b/base64.h @@ -0,0 +1,62 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Base64 encoding & decoding library + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +#ifndef _BASE64_H +#define _BASE64_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include /* C99 types */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief Encode binary data in Base64 string (no padding) +@param in pointer to a table of binary data +@param size number of bytes to be encoded to base64 +@param out pointer to a string where the function will output encoded data +@param max_len max length of the out string (including null char) +@return >=0 length of the resulting string (w/o null char), -1 for error +*/ +int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len); + +/** +@brief Decode Base64 string to binary data (no padding) +@param in string containing only base64 valid characters +@param size number of characters to be decoded from base64 (w/o null char) +@param out pointer to a data buffer where the function will output decoded data +@param out_max_len usable size of the output data buffer +@return >=0 number of bytes written to the data buffer, -1 for error +*/ +int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len); + +/* === derivative functions === */ + +/** +@brief Encode binary data in Base64 string (with added padding) +*/ +int bin_to_b64(const uint8_t * in, int size, char * out, int max_len); + +/** +@brief Decode Base64 string to binary data (remove padding if necessary) +*/ +int b64_to_bin(const char * in, int size, uint8_t * out, int max_len); + +#endif + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..2381f45 --- /dev/null +++ b/main.cpp @@ -0,0 +1,561 @@ +/******************************************************************************* + * + * Copyright (c) 2015 Thomas Telkamp + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + *******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +#include "base64.h" + +#include +#include + +typedef bool boolean; +typedef unsigned char byte; + +static const int CHANNEL = 0; + +byte currentMode = 0x81; + +char message[256]; +char b64[256]; + +byte receivedbytes; + +struct sockaddr_in si_other; +int s, slen=sizeof(si_other); +struct ifreq ifr; + +uint32_t cp_nb_rx_rcv; +uint32_t cp_nb_rx_ok; +uint32_t cp_nb_rx_bad; +uint32_t cp_nb_rx_nocrc; +uint32_t cp_up_pkt_fwd; + +enum sf_t { SF7=7, SF8, SF9, SF10, SF11, SF12 }; + +/******************************************************************************* + * + * Configure these values! + * + *******************************************************************************/ + +// SX1272 - Raspberry connections +int ssPin = 6; +int dio0 = 7; +int RST = 0; + +// Set spreading factor (SF7 - SF12) +sf_t sf = SF7; + +// Set center frequency +uint32_t freq = 868100000; // in Mhz! (868.1) + +// Set location +float lat=0.0; +float lon=0.0; +int alt=0; + +/* Informal status fields */ +static char platform[24] = "SX1272"; /* platform definition */ +static char email[40] = ""; /* used for contact email */ +static char description[64] = ""; /* used for free form description */ + +// define servers +// TODO: use host names and dns +#define SERVER1 "54.229.214.112" // The Things Network: croft.thethings.girovito.nl +//#define SERVER2 "192.168.1.10" // local +#define PORT 1700 // The port on which to send data + +// ############################################# +// ############################################# + +#define REG_FIFO 0x00 +#define REG_FIFO_ADDR_PTR 0x0D +#define REG_FIFO_TX_BASE_AD 0x0E +#define REG_FIFO_RX_BASE_AD 0x0F +#define REG_RX_NB_BYTES 0x13 +#define REG_OPMODE 0x01 +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS 0x12 +#define REG_DIO_MAPPING_1 0x40 +#define REG_DIO_MAPPING_2 0x41 +#define REG_MODEM_CONFIG 0x1D +#define REG_MODEM_CONFIG2 0x1E +#define REG_SYMB_TIMEOUT_LSB 0x1F +#define REG_PKT_SNR_VALUE 0x19 +#define REG_PAYLOAD_LENGTH 0x22 +#define REG_IRQ_FLAGS_MASK 0x11 +#define REG_MAX_PAYLOAD_LENGTH 0x23 +#define REG_HOP_PERIOD 0x24 +#define REG_SYNC_WORD 0x39 +#define REG_VERSION 0x42 + +#define SX72_MODE_RX_CONTINUOS 0x85 +#define SX72_MODE_TX 0x83 +#define SX72_MODE_SLEEP 0x80 +#define SX72_MODE_STANDBY 0x81 + +#define PAYLOAD_LENGTH 0x40 + +// LOW NOISE AMPLIFIER +#define REG_LNA 0x0C +#define LNA_MAX_GAIN 0x23 +#define LNA_OFF_GAIN 0x00 +#define LNA_LOW_GAIN 0x20 + +// CONF REG +#define REG1 0x0A +#define REG2 0x84 + +#define SX72_MC2_FSK 0x00 +#define SX72_MC2_SF7 0x70 +#define SX72_MC2_SF8 0x80 +#define SX72_MC2_SF9 0x90 +#define SX72_MC2_SF10 0xA0 +#define SX72_MC2_SF11 0xB0 +#define SX72_MC2_SF12 0xC0 + +#define SX72_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12 + +// FRF +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 + +#define FRF_MSB 0xD9 // 868.1 Mhz +#define FRF_MID 0x06 +#define FRF_LSB 0x66 + +#define BUFLEN 2048 //Max length of buffer + +#define PROTOCOL_VERSION 1 +#define PKT_PUSH_DATA 0 +#define PKT_PUSH_ACK 1 +#define PKT_PULL_DATA 2 +#define PKT_PULL_RESP 3 +#define PKT_PULL_ACK 4 + +#define TX_BUFF_SIZE 2048 +#define STATUS_SIZE 1024 + +void die(const char *s) +{ + perror(s); + exit(1); +} + +void selectreceiver() +{ + digitalWrite(ssPin, LOW); +} + +void unselectreceiver() +{ + digitalWrite(ssPin, HIGH); +} + +byte readRegister(byte addr) +{ + unsigned char spibuf[2]; + + selectreceiver(); + spibuf[0] = addr & 0x7F; + spibuf[1] = 0x00; + wiringPiSPIDataRW(CHANNEL, spibuf, 2); + unselectreceiver(); + + return spibuf[1]; +} + +void writeRegister(byte addr, byte value) +{ + unsigned char spibuf[2]; + + spibuf[0] = addr | 0x80; + spibuf[1] = value; + selectreceiver(); + wiringPiSPIDataRW(CHANNEL, spibuf, 2); + + unselectreceiver(); +} + + +boolean receivePkt(char *payload) +{ + + // clear rxDone + writeRegister(REG_IRQ_FLAGS, 0x40); + + int irqflags = readRegister(REG_IRQ_FLAGS); + + cp_nb_rx_rcv++; + + // payload crc: 0x20 + if((irqflags & 0x20) == 0x20) + { + printf("CRC error\n"); + writeRegister(REG_IRQ_FLAGS, 0x20); + return false; + } else { + + cp_nb_rx_ok++; + + byte currentAddr = readRegister(REG_FIFO_RX_CURRENT_ADDR); + byte receivedCount = readRegister(REG_RX_NB_BYTES); + receivedbytes = receivedCount; + + writeRegister(REG_FIFO_ADDR_PTR, currentAddr); + + for(int i = 0; i < receivedCount; i++) + { + payload[i] = (char)readRegister(REG_FIFO); + } + } + return true; +} + +void SetupLoRa() +{ + + byte version = readRegister(REG_VERSION); + if (version == 0x12) { + // sx1276 + printf("SX1276 transceiver not yet supported.\n"); + exit(1); + } else if (version == 0x22) { + // sx1272 + printf("SX1272 detected, starting.\n"); + } else { + printf("Unrecognized transceiver.\n"); + exit(1); + } + + writeRegister(REG_OPMODE, SX72_MODE_SLEEP); + + // set frequency + uint64_t frf = ((uint64_t)freq << 19) / 32000000; + writeRegister(REG_FRF_MSB, (uint8_t)(frf>>16) ); + writeRegister(REG_FRF_MID, (uint8_t)(frf>> 8) ); + writeRegister(REG_FRF_LSB, (uint8_t)(frf>> 0) ); + + writeRegister(REG_SYNC_WORD, 0x34); // LoRaWAN public sync word + + if (sf == SF11 || sf == SF12) { + writeRegister(REG_MODEM_CONFIG,0x0B); + } else { + writeRegister(REG_MODEM_CONFIG,0x0A); + } + writeRegister(REG_MODEM_CONFIG2,(sf<<4) | 0x04); + + if (sf == SF10 || sf == SF11 || sf == SF12) { + writeRegister(REG_SYMB_TIMEOUT_LSB,0x05); + } else { + writeRegister(REG_SYMB_TIMEOUT_LSB,0x08); + } + writeRegister(REG_MAX_PAYLOAD_LENGTH,0x80); + writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH); + writeRegister(REG_HOP_PERIOD,0xFF); + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_BASE_AD)); + + // Set Continous Receive Mode + writeRegister(REG_LNA, LNA_MAX_GAIN); // max lna gain + writeRegister(REG_OPMODE, SX72_MODE_RX_CONTINUOS); + +} + +void sendudp(char *msg, int length) { + +//send the update +#ifdef SERVER1 + inet_aton(SERVER1 , &si_other.sin_addr); + if (sendto(s, (char *)msg, length, 0 , (struct sockaddr *) &si_other, slen)==-1) + { + die("sendto()"); + } +#endif + +#ifdef SERVER2 + inet_aton(SERVER2 , &si_other.sin_addr); + if (sendto(s, (char *)msg, length , 0 , (struct sockaddr *) &si_other, slen)==-1) + { + die("sendto()"); + } +#endif +} + +void sendstat() { + + static char status_report[STATUS_SIZE]; /* status report as a JSON object */ + char stat_timestamp[24]; + time_t t; + + int stat_index=0; + + /* pre-fill the data buffer with fixed fields */ + status_report[0] = PROTOCOL_VERSION; + status_report[3] = PKT_PUSH_DATA; + + status_report[4] = (unsigned char)ifr.ifr_hwaddr.sa_data[0]; + status_report[5] = (unsigned char)ifr.ifr_hwaddr.sa_data[1]; + status_report[6] = (unsigned char)ifr.ifr_hwaddr.sa_data[2]; + status_report[7] = 0xFF; + status_report[8] = 0xFF; + status_report[9] = (unsigned char)ifr.ifr_hwaddr.sa_data[3]; + status_report[10] = (unsigned char)ifr.ifr_hwaddr.sa_data[4]; + status_report[11] = (unsigned char)ifr.ifr_hwaddr.sa_data[5]; + + /* start composing datagram with the header */ + uint8_t token_h = (uint8_t)rand(); /* random token */ + uint8_t token_l = (uint8_t)rand(); /* random token */ + status_report[1] = token_h; + status_report[2] = token_l; + stat_index = 12; /* 12-byte header */ + + /* get timestamp for statistics */ + t = time(NULL); + strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t)); + + 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); + stat_index += j; + status_report[stat_index] = 0; /* add string terminator, for safety */ + + printf("stat update: %s\n", (char *)(status_report+12)); /* DEBUG: display JSON stat */ + + //send the update + sendudp(status_report, stat_index); + +} + +void receivepacket() { + + long int SNR; + + if(digitalRead(dio0) == 1) + { + if(receivePkt(message)) { + byte value = readRegister(REG_PKT_SNR_VALUE); + if( value & 0x80 ) // The SNR sign bit is 1 + { + // Invert and divide by 4 + value = ( ( ~value + 1 ) & 0xFF ) >> 2; + SNR = -value; + } + else + { + // Divide by 4 + SNR = ( value & 0xFF ) >> 2; + } + printf("Packet RSSI: %d, ",readRegister(0x1A)-139); + printf("RSSI: %d, ",readRegister(0x1B)-139); + printf("SNR: %li, ",SNR); + printf("Length: %i",(int)receivedbytes); + printf("\n"); + + int j; + j = bin_to_b64((uint8_t *)message, receivedbytes, (char *)(b64), 341); + //fwrite(b64, sizeof(char), j, stdout); + + char buff_up[TX_BUFF_SIZE]; /* buffer to compose the upstream packet */ + int buff_index=0; + + /* gateway <-> MAC protocol variables */ + //static uint32_t net_mac_h; /* Most Significant Nibble, network order */ + //static uint32_t net_mac_l; /* Least Significant Nibble, network order */ + + /* pre-fill the data buffer with fixed fields */ + buff_up[0] = PROTOCOL_VERSION; + buff_up[3] = PKT_PUSH_DATA; + + /* process some of the configuration variables */ + //net_mac_h = htonl((uint32_t)(0xFFFFFFFF & (lgwm>>32))); + //net_mac_l = htonl((uint32_t)(0xFFFFFFFF & lgwm )); + //*(uint32_t *)(buff_up + 4) = net_mac_h; + //*(uint32_t *)(buff_up + 8) = net_mac_l; + + buff_up[4] = (unsigned char)ifr.ifr_hwaddr.sa_data[0]; + buff_up[5] = (unsigned char)ifr.ifr_hwaddr.sa_data[1]; + buff_up[6] = (unsigned char)ifr.ifr_hwaddr.sa_data[2]; + buff_up[7] = 0xFF; + buff_up[8] = 0xFF; + buff_up[9] = (unsigned char)ifr.ifr_hwaddr.sa_data[3]; + buff_up[10] = (unsigned char)ifr.ifr_hwaddr.sa_data[4]; + buff_up[11] = (unsigned char)ifr.ifr_hwaddr.sa_data[5]; + + /* start composing datagram with the header */ + uint8_t token_h = (uint8_t)rand(); /* random token */ + uint8_t token_l = (uint8_t)rand(); /* random token */ + buff_up[1] = token_h; + buff_up[2] = token_l; + buff_index = 12; /* 12-byte header */ + + // TODO: tmst can jump is time is (re)set, not good. + struct timeval now; + gettimeofday(&now, NULL); + uint32_t tmst = (uint32_t)(now.tv_sec*1000000 + now.tv_usec); + + /* start of JSON structure */ + memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[", 9); + buff_index += 9; + buff_up[buff_index] = '{'; + ++buff_index; + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, "\"tmst\":%u", tmst); + buff_index += j; + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf", 0, 0, (double)freq/1000000); + buff_index += j; + memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":1", 9); + buff_index += 9; + memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"LORA\"", 14); + buff_index += 14; + /* Lora datarate & bandwidth, 16-19 useful chars */ + switch (sf) { + case SF7: + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF7", 12); + buff_index += 12; + break; + case SF8: + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF8", 12); + buff_index += 12; + break; + case SF9: + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF9", 12); + buff_index += 12; + break; + case SF10: + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF10", 13); + buff_index += 13; + break; + case SF11: + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF11", 13); + buff_index += 13; + break; + case SF12: + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF12", 13); + buff_index += 13; + break; + default: + memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF?", 12); + buff_index += 12; + } + memcpy((void *)(buff_up + buff_index), (void *)"BW125\"", 6); + buff_index += 6; + memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/5\"", 13); + buff_index += 13; + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"lsnr\":%li", SNR); + buff_index += j; + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%d,\"size\":%u", readRegister(0x1A)-139, receivedbytes); + buff_index += j; + memcpy((void *)(buff_up + buff_index), (void *)",\"data\":\"", 9); + buff_index += 9; + j = bin_to_b64((uint8_t *)message, receivedbytes, (char *)(buff_up + buff_index), 341); + buff_index += j; + buff_up[buff_index] = '"'; + ++buff_index; + + /* End of packet serialization */ + buff_up[buff_index] = '}'; + ++buff_index; + buff_up[buff_index] = ']'; + ++buff_index; + /* end of JSON datagram payload */ + buff_up[buff_index] = '}'; + ++buff_index; + buff_up[buff_index] = 0; /* add string terminator, for safety */ + + printf("rxpk update: %s\n", (char *)(buff_up + 12)); /* DEBUG: display JSON payload */ + + //send the messages + sendudp(buff_up, buff_index); + + fflush(stdout); + + } // received a message + + } // dio0=1 +} + +int main () { + + struct timeval nowtime; + uint32_t lasttime; + + wiringPiSetup () ; + pinMode(ssPin, OUTPUT); + pinMode(dio0, INPUT); + pinMode(RST, OUTPUT); + + digitalWrite(RST, HIGH); + delay(100); + digitalWrite(RST, LOW); + delay(200); + + //int fd = ; + wiringPiSPISetup(CHANNEL, 500000); + //cout << "Init result: " << fd << endl; + + SetupLoRa(); + + if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + { + die("socket"); + } + memset((char *) &si_other, 0, sizeof(si_other)); + si_other.sin_family = AF_INET; + si_other.sin_port = htons(PORT); + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1); // can we rely on eth0? + ioctl(s, SIOCGIFHWADDR, &ifr); + + /* display result */ + printf("Gateway ID: %.2x:%.2x:%.2x:ff:ff:%.2x:%.2x:%.2x\n", + (unsigned char)ifr.ifr_hwaddr.sa_data[0], + (unsigned char)ifr.ifr_hwaddr.sa_data[1], + (unsigned char)ifr.ifr_hwaddr.sa_data[2], + (unsigned char)ifr.ifr_hwaddr.sa_data[3], + (unsigned char)ifr.ifr_hwaddr.sa_data[4], + (unsigned char)ifr.ifr_hwaddr.sa_data[5]); + + printf("Listening at SF%i on %.6lf Mhz.\n", sf,(double)freq/1000000); + printf("------------------\n"); + + while(1) { + + receivepacket(); + + gettimeofday(&nowtime, NULL); + uint32_t nowseconds = (uint32_t)(nowtime.tv_sec); + if (nowseconds - lasttime >= 30) { + lasttime = nowseconds; + sendstat(); + cp_nb_rx_rcv = 0; + cp_nb_rx_ok = 0; + cp_up_pkt_fwd = 0; + } + delay(1); + } + + return (0); + +} + -- libgit2 0.21.2