kopia lustrzana https://github.com/PiInTheSky/lora-gateway
V1.10.0 - Removed all Habitat/HABHUB support
rodzic
2afab21f05
commit
76638b8c2b
|
@ -1,7 +1,9 @@
|
|||
Habitat LoRa Gateway
|
||||
====================
|
||||
|
||||
Part of the LoRa Balloon Tracking System
|
||||
Part of the LoRa Balloon Tracking System. Receives balloon telemetry and uploads to Sondehub and/or MQTT.
|
||||
|
||||
** ALL HABHUB SUPPORT HAS BEEN REMOVED since the server has been decommissioned **
|
||||
|
||||
Runs on a Raspberry Pi with 1 or 2 RFM98HW modules attached to the SPI port.
|
||||
Also works with other compatible HopeRF and Semtec LoRa devices.
|
||||
|
@ -92,8 +94,7 @@ Configuration
|
|||
The configuration is in the file gateway.txt. Example:
|
||||
|
||||
tracker=M0RPI
|
||||
EnableHabitat=N
|
||||
EnableSondehub=N
|
||||
EnableSondehub=Y
|
||||
EnableSSDV=Y
|
||||
LogTelemetry=Y
|
||||
LogPackets=Y
|
||||
|
|
|
@ -10,15 +10,13 @@ Antenna=Your antenna
|
|||
|
||||
##### Config Options #####
|
||||
|
||||
EnableHabitat=Y
|
||||
EnableSondehub=Y
|
||||
EnableSSDV=Y
|
||||
JPGFolder=ssdv
|
||||
LogTelemetry=Y
|
||||
LogPackets=Y
|
||||
CallingTimeout=60
|
||||
ServerPort=6004
|
||||
#SMSFolder=./
|
||||
EnableDev=N
|
||||
|
||||
NetworkLED=22
|
||||
InternetLED=23
|
||||
|
|
58
gateway.c
58
gateway.c
|
@ -32,7 +32,6 @@
|
|||
#include "base64.h"
|
||||
#include "ssdv.h"
|
||||
#include "ftp.h"
|
||||
#include "habitat.h"
|
||||
#include "sondehub.h"
|
||||
#include "mqtt.h"
|
||||
#include "network.h"
|
||||
|
@ -42,12 +41,11 @@
|
|||
#include "gateway.h"
|
||||
#include "config.h"
|
||||
#include "gui.h"
|
||||
#include "listener.h"
|
||||
#include "habpack.h"
|
||||
#include "udpclient.h"
|
||||
#include "lifo_buffer.h"
|
||||
|
||||
#define VERSION "V1.9.6"
|
||||
#define VERSION "V1.10.0"
|
||||
bool run = TRUE;
|
||||
|
||||
// RFM98
|
||||
|
@ -196,7 +194,6 @@ struct TBinaryPacket {
|
|||
|
||||
#pragma pack(pop)
|
||||
|
||||
lifo_buffer_t Habitat_Upload_Buffer;
|
||||
lifo_buffer_t MQTT_Upload_Buffer;
|
||||
|
||||
// Create pipes for inter proces communication
|
||||
|
@ -1143,22 +1140,6 @@ int ProcessTelemetryMessage(int Channel, received_t *Received)
|
|||
strcpy(Config.LoRaDevices[Channel].Telemetry, startmessage);
|
||||
Config.LoRaDevices[Channel].TelemetryCount++;
|
||||
|
||||
if (Config.EnableHabitat)
|
||||
{
|
||||
// Add to Habitat upload queue
|
||||
received_t *queueReceived = malloc(sizeof(received_t));
|
||||
if(queueReceived != NULL)
|
||||
{
|
||||
strcpy(Received->HabitatString, startmessage);
|
||||
memcpy(queueReceived, Received, sizeof(received_t));
|
||||
/* We haven't copied the linked list, this'll be free()ed later, so remove pointer */
|
||||
queueReceived->Telemetry.habpack_extra = NULL;
|
||||
|
||||
/* Push pointer onto upload queue */
|
||||
lifo_buffer_push(&Habitat_Upload_Buffer, (void *)queueReceived);
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.EnableMQTT)
|
||||
{
|
||||
// Add to MQTT upload queue
|
||||
|
@ -2059,7 +2040,6 @@ void LoadConfigFile(void)
|
|||
LogMessage( "Tracker = '%s'\n", Config.Tracker );
|
||||
|
||||
// Enable uploads
|
||||
RegisterConfigBoolean(MainSection, -1, "EnableHabitat", &Config.EnableHabitat, NULL);
|
||||
RegisterConfigBoolean(MainSection, -1, "EnableSSDV", &Config.EnableSSDV, NULL);
|
||||
RegisterConfigBoolean(MainSection, -1, "EnableSondehub", &Config.EnableSondehub, NULL);
|
||||
|
||||
|
@ -2316,7 +2296,7 @@ WINDOW *InitDisplay(void)
|
|||
|
||||
char buffer[80];
|
||||
|
||||
sprintf( buffer, "LoRa Habitat and SSDV Gateway by M0RPI, M0DNY, M0RJX - " VERSION);
|
||||
sprintf( buffer, "LoRa HAB Sondehub, SSDV and MQTT Gateway by M0RPI, M0DNY, M0RJX - " VERSION);
|
||||
|
||||
// Title bar
|
||||
mvaddstr(0, ( 80 - strlen( buffer ) ) / 2, buffer );
|
||||
|
@ -2656,7 +2636,7 @@ int main( int argc, char **argv )
|
|||
int ch;
|
||||
int LoopPeriod, MSPerLoop;
|
||||
int Channel;
|
||||
pthread_t SSDVThread, FTPThread, NetworkThread, HabitatThread, SondehubThread, ServerThread, TelnetThread, ListenerThread, DataportThread, ChatportThread, MQTTThread;
|
||||
pthread_t SSDVThread, FTPThread, NetworkThread, SondehubThread, ServerThread, TelnetThread, DataportThread, ChatportThread, MQTTThread;
|
||||
struct TServerInfo JSONInfo, TelnetInfo, DataportInfo, ChatportInfo;
|
||||
|
||||
atexit(bye);
|
||||
|
@ -2742,17 +2722,6 @@ int main( int argc, char **argv )
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (Config.EnableHabitat)
|
||||
{
|
||||
lifo_buffer_init(&Habitat_Upload_Buffer, 1024);
|
||||
|
||||
if ( pthread_create (&HabitatThread, NULL, HabitatLoop, NULL))
|
||||
{
|
||||
fprintf( stderr, "Error creating Habitat thread\n" );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.EnableMQTT)
|
||||
{
|
||||
lifo_buffer_init(&MQTT_Upload_Buffer, 1024);
|
||||
|
@ -2843,15 +2812,6 @@ int main( int argc, char **argv )
|
|||
}
|
||||
}
|
||||
|
||||
if (( Config.latitude >= -90) && (Config.latitude <= 90) && (Config.longitude >= -180) && (Config.longitude <= 180))
|
||||
{
|
||||
if ( pthread_create( &ListenerThread, NULL, ListenerLoop, NULL ) )
|
||||
{
|
||||
fprintf( stderr, "Error creating Listener thread\n" );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the structure used for storing calling mode settings
|
||||
callingModeSettings[0].Channel = -1;
|
||||
callingModeSettings[1].Channel = -1;
|
||||
|
@ -3004,9 +2964,6 @@ int main( int argc, char **argv )
|
|||
LogMessage( "Stopping SSDV thread\n" );
|
||||
stsv.parent_status = STOPPED;
|
||||
|
||||
LogMessage( "Stopping Habitat thread\n" );
|
||||
lifo_buffer_quitwait(&Habitat_Upload_Buffer);
|
||||
|
||||
if (Config.EnableSSDV)
|
||||
{
|
||||
LogMessage( "Waiting for SSDV thread to close ...\n" );
|
||||
|
@ -3014,15 +2971,6 @@ int main( int argc, char **argv )
|
|||
LogMessage( "SSDV thread closed\n" );
|
||||
}
|
||||
|
||||
if (Config.EnableHabitat)
|
||||
{
|
||||
LogMessage( "Waiting for Habitat thread to close ...\n" );
|
||||
pthread_join( HabitatThread, NULL );
|
||||
LogMessage( "Habitat thread closed\n" );
|
||||
}
|
||||
|
||||
// CloseDisplay( mainwin );
|
||||
|
||||
pthread_mutex_destroy( &var );
|
||||
|
||||
curl_global_cleanup( ); // RJH thread safe
|
||||
|
|
1
global.h
1
global.h
|
@ -115,7 +115,6 @@ struct TConfig
|
|||
char Tracker[16]; // Callsign or name of receiver
|
||||
double latitude, longitude, altitude; // Receiver's location
|
||||
|
||||
int EnableHabitat;
|
||||
int EnableSSDV;
|
||||
int EnableHablink;
|
||||
int EnableSondehub;
|
||||
|
|
228
habitat.c
228
habitat.c
|
@ -1,228 +0,0 @@
|
|||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h> // Standard input/output definitions
|
||||
#include <string.h> // String function definitions
|
||||
#include <unistd.h> // UNIX standard function definitions
|
||||
#include <fcntl.h> // File control definitions
|
||||
#include <errno.h> // Error number definitions
|
||||
#include <termios.h> // POSIX terminal control definitions
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <dirent.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <curl/curl.h>
|
||||
#include <wiringPi.h>
|
||||
|
||||
#include "base64.h"
|
||||
#include "habitat.h"
|
||||
#include "global.h"
|
||||
#include "sha256.h"
|
||||
#include "wiringPi.h"
|
||||
#include "gateway.h"
|
||||
#include "lifo_buffer.h"
|
||||
|
||||
extern lifo_buffer_t Habitat_Upload_Buffer;
|
||||
|
||||
extern int telem_pipe_fd[2];
|
||||
extern pthread_mutex_t var;
|
||||
extern void ChannelPrintf( int Channel, int row, int column,
|
||||
const char *format, ... );
|
||||
|
||||
size_t habitat_write_data( void *buffer, size_t size, size_t nmemb, void *userp )
|
||||
{
|
||||
// LogMessage("%s\n", (char *)buffer);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
void hash_to_hex( unsigned char *hash, char *line )
|
||||
{
|
||||
int idx;
|
||||
|
||||
for ( idx = 0; idx < 32; idx++ )
|
||||
{
|
||||
sprintf( &( line[idx * 2] ), "%02x", hash[idx] );
|
||||
}
|
||||
line[64] = '\0';
|
||||
}
|
||||
|
||||
bool UploadTelemetryPacket( received_t * t )
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
char curl_error[CURL_ERROR_SIZE];
|
||||
|
||||
/* get a curl handle */
|
||||
curl = curl_easy_init( );
|
||||
if ( curl )
|
||||
{
|
||||
bool result;
|
||||
char url[200];
|
||||
char base64_data[1000];
|
||||
size_t base64_length;
|
||||
SHA256_CTX ctx;
|
||||
unsigned char hash[32];
|
||||
char doc_id[100];
|
||||
char json[1000], now[32], doc_time[32];
|
||||
char Sentence[512];
|
||||
struct curl_slist *headers = NULL;
|
||||
time_t rawtime;
|
||||
struct tm *tm, *doc_tm;
|
||||
int retries;
|
||||
long int http_resp;
|
||||
|
||||
// Get formatted timestamp for now
|
||||
time( &rawtime );
|
||||
tm = gmtime( &rawtime );
|
||||
strftime( now, sizeof( now ), "%Y-%0m-%0dT%H:%M:%SZ", tm );
|
||||
|
||||
// Get formatted timestamp for doc timestamp
|
||||
doc_tm = gmtime( &t->Metadata.Timestamp );
|
||||
strftime( doc_time, sizeof( doc_time ), "%Y-%0m-%0dT%H:%M:%SZ", doc_tm );
|
||||
|
||||
// Grab current telemetry string and append a linefeed
|
||||
sprintf(Sentence, "%s\n", t->HabitatString);
|
||||
|
||||
// Convert sentence to base64
|
||||
base64_encode( Sentence, strlen( Sentence ), &base64_length,
|
||||
base64_data );
|
||||
base64_data[base64_length] = '\0';
|
||||
|
||||
// Take SHA256 hash of the base64 version and express as hex. This will be the document ID
|
||||
sha256_init( &ctx );
|
||||
sha256_update( &ctx, base64_data, base64_length );
|
||||
sha256_final( &ctx, hash );
|
||||
hash_to_hex( hash, doc_id );
|
||||
|
||||
// Create json with the base64 data in hex, the tracker callsign and the current timestamp
|
||||
sprintf( json,
|
||||
"{\"data\": {\"_raw\": \"%s\"},\"receivers\": {\"%s\": {\"time_created\": \"%s\",\"time_uploaded\": \"%s\",\"rig_info\": {\"frequency\":%.0f}}}}",
|
||||
base64_data, Config.Tracker, doc_time, now, (t->Metadata.Frequency + t->Metadata.FrequencyError) * 1000000 );
|
||||
|
||||
// Set the URL that is about to receive our PUT
|
||||
sprintf( url, "http://habitat.habhub.org/habitat/_design/payload_telemetry/_update/add_listener/%s", doc_id);
|
||||
|
||||
// So that the response to the curl PUT doesn't mess up my finely crafted display!
|
||||
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, habitat_write_data );
|
||||
|
||||
// Set the timeout
|
||||
curl_easy_setopt( curl, CURLOPT_TIMEOUT, 15 );
|
||||
|
||||
// RJH capture http errors and report
|
||||
// curl_easy_setopt( curl, CURLOPT_FAILONERROR, 1 );
|
||||
curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, curl_error );
|
||||
|
||||
// Avoid curl library bug that happens if above timeout occurs (sigh)
|
||||
curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 );
|
||||
|
||||
// Set the headers
|
||||
headers = NULL;
|
||||
headers = curl_slist_append(headers, "Accept: application/json");
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
headers = curl_slist_append(headers, "charsets: utf-8" );
|
||||
|
||||
// PUT to http://habitat.habhub.org/habitat/_design/payload_telemetry/_update/add_listener/<doc_id> with content-type application/json
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json);
|
||||
|
||||
retries = 0;
|
||||
do
|
||||
{
|
||||
// Perform the request, res will get the return code
|
||||
res = curl_easy_perform( curl );
|
||||
|
||||
// Check for errors
|
||||
if ( res == CURLE_OK )
|
||||
{
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp);
|
||||
if (http_resp != 201 && http_resp != 403 && http_resp != 409)
|
||||
{
|
||||
LogMessage("Unexpected HTTP response %ld for URL '%s'\n", http_resp, url);
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Everything performing nominally (even if we didn't successfully insert this time) */
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
http_resp = 0;
|
||||
LogMessage("Failed for URL '%s'\n", url);
|
||||
LogMessage("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
LogMessage("error: %s\n", curl_error);
|
||||
/* Likely a network error, so return false to requeue */
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (http_resp == 409)
|
||||
{
|
||||
// conflict between us and another uploader at the same time
|
||||
// wait for a random period before trying again
|
||||
delay(random() & 255); // 0-255 ms
|
||||
}
|
||||
} while ((http_resp == 409) && (++retries < 5));
|
||||
|
||||
// always cleanup
|
||||
curl_slist_free_all( headers );
|
||||
curl_easy_cleanup( curl );
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* CURL error, return false so we requeue */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *HabitatLoop( void *vars )
|
||||
{
|
||||
if ( Config.EnableHabitat )
|
||||
{
|
||||
received_t *dequeued_telemetry_ptr;
|
||||
|
||||
// Keep looping until the parent quits
|
||||
while ( true )
|
||||
{
|
||||
dequeued_telemetry_ptr = lifo_buffer_waitpop(&Habitat_Upload_Buffer);
|
||||
|
||||
if(dequeued_telemetry_ptr != NULL)
|
||||
{
|
||||
ChannelPrintf( dequeued_telemetry_ptr->Metadata.Channel, 6, 1, "Habitat (%d queued)", lifo_buffer_queued(&Habitat_Upload_Buffer) );
|
||||
|
||||
if(UploadTelemetryPacket( dequeued_telemetry_ptr ))
|
||||
{
|
||||
ChannelPrintf( dequeued_telemetry_ptr->Metadata.Channel, 6, 1, " " );
|
||||
free(dequeued_telemetry_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Network / CURL Error, requeue packet */
|
||||
ChannelPrintf( dequeued_telemetry_ptr->Metadata.Channel, 6, 1, "Habitat Net Error! " );
|
||||
|
||||
if(!lifo_buffer_requeue(&Habitat_Upload_Buffer, dequeued_telemetry_ptr))
|
||||
{
|
||||
/* Requeue failed, drop packet */
|
||||
free(dequeued_telemetry_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* NULL returned: We've been asked to quit */
|
||||
/* Don't bother free()ing stuff, as application is quitting */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
void *HabitatLoop( void *some_void_ptr );
|
122
listener.c
122
listener.c
|
@ -1,122 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <wiringPi.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "global.h"
|
||||
#include "gateway.h"
|
||||
|
||||
#define LISTENER_UPDATE_INTERVAL 30 // Minutes
|
||||
#define LISTENER_LOOP_SLEEP 1000 // Milliseconds
|
||||
|
||||
size_t write_data( void *buffer, size_t size, size_t nmemb, void *userp )
|
||||
{
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
void UploadListenerTelemetry( char *callsign, time_t gps_time, float gps_lat, float gps_lon, char *radio, char *antenna )
|
||||
{
|
||||
char time_string[20];
|
||||
struct tm * time_info;
|
||||
|
||||
time_info = localtime (&gps_time);
|
||||
strftime(time_string, sizeof(time_string), "%H:%M:%S", time_info);
|
||||
|
||||
if ( Config.EnableHabitat )
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
char PostFields[300];
|
||||
char JsonData[200];
|
||||
|
||||
/* In windows, this will init the winsock stuff */
|
||||
|
||||
/* get a curl handle */
|
||||
curl = curl_easy_init( );
|
||||
if ( curl )
|
||||
{
|
||||
// So that the response to the curl POST doesn;'t mess up my finely crafted display!
|
||||
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data );
|
||||
|
||||
// Set the URL that is about to receive our POST
|
||||
curl_easy_setopt( curl, CURLOPT_URL,
|
||||
"http://habitat.habhub.org/transition/listener_telemetry" );
|
||||
|
||||
// Now specify the POST data
|
||||
sprintf( JsonData, "{\"latitude\": %f, \"longitude\": %f}",
|
||||
gps_lat, gps_lon );
|
||||
sprintf( PostFields, "callsign=%s&time=%d&data=%s", callsign,
|
||||
(int)gps_time, JsonData );
|
||||
curl_easy_setopt( curl, CURLOPT_POSTFIELDS, PostFields );
|
||||
|
||||
// Perform the request, res will get the return code
|
||||
res = curl_easy_perform( curl );
|
||||
|
||||
// Check for errors
|
||||
if ( res == CURLE_OK )
|
||||
{
|
||||
LogMessage( "Uploaded listener %s %s,%f,%f\n",
|
||||
callsign, time_string, gps_lat, gps_lon );
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage( "curl_easy_perform() failed: %s\n",
|
||||
curl_easy_strerror( res ) );
|
||||
}
|
||||
|
||||
// always cleanup
|
||||
curl_easy_cleanup( curl );
|
||||
}
|
||||
|
||||
/* In windows, this will init the winsock stuff */
|
||||
|
||||
/* get a curl handle */
|
||||
curl = curl_easy_init( );
|
||||
if ( curl )
|
||||
{
|
||||
// So that the response to the curl POST doesn;'t mess up my finely crafted display!
|
||||
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data );
|
||||
|
||||
// Set the URL that is about to receive our POST
|
||||
curl_easy_setopt( curl, CURLOPT_URL,
|
||||
"http://habitat.habhub.org/transition/listener_information" );
|
||||
|
||||
// Now specify the POST data
|
||||
sprintf( JsonData, "{\"radio\": \"%s\", \"antenna\": \"%s\"}",
|
||||
radio, antenna );
|
||||
sprintf( PostFields, "callsign=%s&time=%d&data=%s", callsign, (int)gps_time, JsonData );
|
||||
curl_easy_setopt( curl, CURLOPT_POSTFIELDS, PostFields );
|
||||
|
||||
// Perform the request, res will get the return code
|
||||
res = curl_easy_perform( curl );
|
||||
|
||||
// Check for errors
|
||||
if ( res != CURLE_OK )
|
||||
{
|
||||
LogMessage( "curl_easy_perform() failed: %s\n",
|
||||
curl_easy_strerror( res ) );
|
||||
}
|
||||
|
||||
// always cleanup
|
||||
curl_easy_cleanup( curl );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
void *ListenerLoop(void *ptr)
|
||||
{
|
||||
(void) ptr;
|
||||
|
||||
uint32_t LoopPeriod = LISTENER_UPDATE_INTERVAL*60*1000; // So we upload listener at start, as well as every LISTENER_UPDATE_INTERVAL minutes thereafter
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (LoopPeriod > LISTENER_UPDATE_INTERVAL*60*1000)
|
||||
{
|
||||
UploadListenerTelemetry( Config.Tracker, time(NULL), Config.latitude, Config.longitude, Config.radio, Config.antenna );
|
||||
LoopPeriod = 0;
|
||||
}
|
||||
|
||||
delay(LISTENER_LOOP_SLEEP);
|
||||
LoopPeriod += LISTENER_LOOP_SLEEP;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
void *ListenerLoop(void *ptr);
|
Ładowanie…
Reference in New Issue