diff --git a/README.md b/README.md index 48ee851..c66617b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/gateway-sample.txt b/gateway-sample.txt index cc19bd8..2761144 100644 --- a/gateway-sample.txt +++ b/gateway-sample.txt @@ -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 diff --git a/gateway.c b/gateway.c index 83f4cbc..a582a54 100644 --- a/gateway.c +++ b/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 diff --git a/global.h b/global.h index 52e7cca..87ad56d 100644 --- a/global.h +++ b/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; diff --git a/habitat.c b/habitat.c deleted file mode 100644 index 479b3aa..0000000 --- a/habitat.c +++ /dev/null @@ -1,228 +0,0 @@ -#include -#include -#include -#include -#include // Standard input/output definitions -#include // String function definitions -#include // UNIX standard function definitions -#include // File control definitions -#include // Error number definitions -#include // POSIX terminal control definitions -#include -#include -#include -#include -#include -#include -#include -#include - -#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/ 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; -} diff --git a/habitat.h b/habitat.h deleted file mode 100644 index febfc3a..0000000 --- a/habitat.h +++ /dev/null @@ -1 +0,0 @@ -void *HabitatLoop( void *some_void_ptr ); diff --git a/listener.c b/listener.c deleted file mode 100644 index 0c94785..0000000 --- a/listener.c +++ /dev/null @@ -1,122 +0,0 @@ -#include -#include -#include - -#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; - } -} diff --git a/listener.h b/listener.h deleted file mode 100644 index 4f4d671..0000000 --- a/listener.h +++ /dev/null @@ -1 +0,0 @@ -void *ListenerLoop(void *ptr); \ No newline at end of file