SP8EBC-ParaTNC/src/aprsis.c

1151 wiersze
32 KiB
C

/*
* aprsis.c
*
* Created on: Feb 20, 2022
* Author: mateusz
*/
#include "aprsis.h"
#include "etc/aprsis_config.h"
#include "text.h"
#include "backup_registers.h"
#include "aprs/status.h"
#include "aprs/message.h"
#include "gsm/sim800c.h"
#include "gsm/sim800c_poolers.h"
#include "main.h"
#include "rte_main.h"
#include <stdio.h>
#include <string.h>
#ifdef UNIT_TEST
#define STATIC
#else
#define STATIC static
#endif
srl_context_t * aprsis_serial_port;
/**
* Pointer to used gsm_modem_state
*/
gsm_sim800_state_t * aprsis_gsm_modem_state;
/**
* Size of transmit buffer
*/
#define APRSIS_TX_BUFFER_LN 512
/**
* Buffer for sending packet to aprs-is
*/
static char aprsis_packet_tx_buffer[APRSIS_TX_BUFFER_LN];
/**
* Lenght of buffer
*/
#define APRSIS_TELEMETRY_BUFFER_LN 96
/**
* Buffer used to sent telemetry frames to APRS-IS. It is also
* used to construct frame with GPS connection status, which is sent
* only once after APRS-IS connection is established
*/
char aprsis_packet_telemetry_buffer[APRSIS_TELEMETRY_BUFFER_LN];
/**
*
*/
uint16_t aprsis_packet_tx_message_size = 0;
/**
* Passocde to APRS-IS server
*/
int32_t aprsis_passcode;
/**
* Buffer for generate and then permanently store login string
*/
char aprsis_login_string[64];
/**
* Default APRS-IS address to be used by 'aprsis_connect_and_login_default' function
*/
const char * aprsis_default_server_address;
/**
* String with a callsign and a ssid in aprsis format. Used sent auto beacon
* when connection to APRS-IS server is established
*/
const char * aprsis_callsign_with_ssid;
/**
* Pointer to callsign from configuration
*/
const char * aprsis_callsign;
/**
* ssid from configuration
*/
uint8_t aprsis_ssid;
/**
* Lenght of APRS-IS server address string
*/
uint16_t aprsis_default_server_address_ln = 0;
/**
* TCP port used to connect to default APRS-IS server.
*/
uint16_t aprsis_default_server_port;
/**
* Set to one if connections is established AND user is logged
*/
uint8_t aprsis_logged = 0;
/**
* Set to one if connection to server is established (but maybe not logged)
*/
uint8_t aprsis_connected = 0;
const char * aprsis_sucessfull_login = "# logresp\0";
static uint8_t aprsis_successfull_conn_counter = 0;
/**
* Counter of unsuccessful connects to APRS-IS, to trigger GSM modem reset.
* Please note that it works differently that 'aprsis_reset_on_timeout' and
* has nothing to do with a timeout of already established connection. This
* counter will trigger GSM modem reset even if no APRS-IS connection has
* been established at all. It protects against a situation when GSM modem
* is not able to register in cellular network, SIM card is not working for
* some reason etc. Of course there is no guarantee that a reset in such
* case will help, but there is nothing better to do.
*/
static uint8_t aprsis_unsucessfull_conn_counter = 0;
/**
* Set to one if the GSM modem shall be immediately reset when APRS-IS communication
* time out. Please note that this is different that 'aprsis_unsucessfull_conn_counter'
* and reseting GSM mode after many unsuccessfull connection attempts! This
* comes in when the connection has been established at least one time. It won't
* help if there is some problem with establishing connection at all.
*/
static uint32_t aprsis_reset_on_timeout = 0;
/**
* Number of RF packets igated to APRS-IS system
*/
static uint16_t aprsis_igated_counter = 0;
/**
* Counter of all packets originated from the station transmitted to APRS-IS server.
* This doesn't include igated packets!
*/
static uint16_t aprsis_tx_counter = 0;
/**
* Amount of keepalive packet received from the server. It is reset to zero
* every connect event
*/
static uint16_t aprsis_keepalive_received_counter = 0;
/**
* Amount of packets which are not keepalive
*/
static uint16_t aprsis_another_received_counter = 0;
/**
* A timestamp when server has send anything
*/
static uint32_t aprsis_last_keepalive_ts = 0;
/**
* This is the second timestamp of last keepalive message
* from APRS-IS server. It is used by 'aprsis_check_connection_attempt_alive'
* and not incremented anywhere except receive callback. Where a timeout
* calculated using this value is too long the controller is restarted.
*/
static uint32_t aprsis_last_keepalive_long_ts = 0;
/**
* A timestamp when any packet has been sent to
*/
static uint32_t aprsis_last_packet_transmit_ts = 0;
/**
*
*/
static uint32_t aprsis_last_packet_transmit_long_ts = 0;
/**
* Only for debugging purposes
*/
static uint8_t aprsis_debug_simulate_timeout = 0;
#define APRSIS_LOGIN_STRING_RECEIVED_LN 64
char aprsis_login_string_reveived[APRSIS_LOGIN_STRING_RECEIVED_LN];
#define APRSIS_TIMEOUT_MS 123000//123000
/**
* Lenght of 6 letter callsign + two digit SSID + end character like '>' or ","
*/
#define MAXIMUM_CALL_SSID_DASH_LN 10
/**
* Checks if data in a buffer contains APRS message
* @param message
* @param message_ln
* @return position at which content of message starts
*/
STATIC uint16_t aprsis_check_is_message(const uint8_t * const message, const uint16_t message_ln) {
// example message
// Details:"SP8EBC>APX216,TCPIP*,qAC,NINTH::SR9WXZ :tedt{0s}\r\n", '\0' <repeats 715 times>
// go through a buffer and look for double ':'
uint16_t message_start_position = 0;
for (int i = 0; i < message_ln; i++) {
const uint8_t * this_character = message + i;
const uint8_t * next_character = message + i + 1;
if (*this_character == 0x00) {
break;
}
if ((*this_character == ':') && (*next_character == ':')) {
message_start_position = i + 2;
break;
}
}
return message_start_position;
}
/**
*
* @param srl_context
*/
STATIC void aprsis_receive_callback(srl_context_t* srl_context) {
const uint8_t * buffer = srl_get_rx_buffer(srl_context);
const uint16_t message_ln = srl_context->srl_rx_bytes_counter;
// if something was actually received
if (srl_context->srl_rx_state == SRL_RX_DONE) {
// check if this is keepalive message
if (*buffer == '#') {
// set last timestamps
aprsis_last_keepalive_ts = main_get_master_time();
aprsis_last_keepalive_long_ts = main_get_master_time();
// increase received keepalive counter
aprsis_keepalive_received_counter++;
// restart receiving from serial port
gsm_sim800_tcpip_async_receive(aprsis_serial_port, aprsis_gsm_modem_state, 0, 61000, aprsis_receive_callback);
}
else {
// check if this is an aprs message
const int message_position = aprsis_check_is_message(buffer, message_ln);
// if yes try to decode it
if (message_position != 0) {
// prevent overwriting message received from radio channel if it hasn't been serviced yet
if (rte_main_received_message_source == MESSAGE_SOURCE_UNINITIALIZED) {
// if decoding was successfull
if (message_decode(buffer, message_ln, message_position, MESSAGE_SOURCE_APRSIS, &rte_main_received_message) == 0) {
// check if it is for me
if (message_is_for_me(aprsis_callsign, aprsis_ssid, &rte_main_received_message) == 0) {
// set a source of this message
rte_main_received_message_source = MESSAGE_SOURCE_APRSIS;
// trigger preparing ACK
rte_main_trigger_message_ack = 1;
}
}
}
}
else {
;
}
aprsis_another_received_counter++;
gsm_sim800_tcpip_async_receive(aprsis_serial_port, aprsis_gsm_modem_state, 0, 61000, aprsis_receive_callback);
}
}
}
/**
*
* @param context
* @param gsm_modem_state
* @param callsign
* @param ssid
* @param passcode
* @param default_server
* @param default_port
* @param reset_on_timeout
* @param callsign_with_ssid
*/
void aprsis_init(
srl_context_t * context,
gsm_sim800_state_t * gsm_modem_state,
const char * callsign,
uint8_t ssid,
uint32_t passcode,
const char * default_server,
const uint16_t default_port,
uint8_t reset_on_timeout,
const char * callsign_with_ssid) {
aprsis_serial_port = context;
aprsis_gsm_modem_state = gsm_modem_state;
aprsis_passcode = (int32_t)passcode;
aprsis_callsign_with_ssid = callsign_with_ssid;
memset(aprsis_login_string, 0x00, 0x40);
sprintf(aprsis_login_string, "user %s pass %ld vers ParaMETEO %s \r\n", callsign_with_ssid, aprsis_passcode, SW_VER);
aprsis_logged = 0;
aprsis_default_server_port = default_port;
aprsis_default_server_address = default_server;
aprsis_default_server_address_ln = strlen(aprsis_default_server_address);
aprsis_reset_on_timeout = reset_on_timeout;
aprsis_callsign = callsign;
aprsis_ssid = ssid;
}
/**
* Connect and login to APRS-IS server
* @param address ip or dns address to aprs-is server
* @param address_ln lenht of a buffer with an address
* @param port TCP port to use (typically 14580)
* @param auto_send_beacon
* @return
*/
aprsis_return_t aprsis_connect_and_login(const char * address, uint8_t address_ln, uint16_t port, uint8_t auto_send_beacon) {
// this function has blocking io
uint8_t out = APRSIS_WRONG_STATE;
if (aprsis_logged == 1) {
return APRSIS_ALREADY_CONNECTED;
}
if (aprsis_serial_port == 0 || aprsis_gsm_modem_state == 0) {
return APRSIS_NOT_CONFIGURED;
}
if (*aprsis_gsm_modem_state == SIM800_ALIVE) {
char port_str[6];
uint8_t * receive_buff;
int8_t retval = 0xFF;
int offset = 0;
aprsis_tx_counter = 0;
aprsis_keepalive_received_counter = 0;
aprsis_another_received_counter = 0;
aprsis_igated_counter = 0;
memset(port_str, 0x00, 0x6);
memset(aprsis_login_string_reveived, 0x00, APRSIS_LOGIN_STRING_RECEIVED_LN);
snprintf(port_str, 6, "%d", port);
// result of a disconnecting from APRS-IS server
sim800_return_t disconnection_result = SIM800_UNSET;
// connecting has blocking I/O
retval = gsm_sim800_tcpip_connect(address, address_ln, port_str, 0x6, aprsis_serial_port, aprsis_gsm_modem_state);
// if connection was successful
if (retval == SIM800_OK) {
// wait for hello message '# aprsc 2.1.10-gd72a17c'
retval = gsm_sim800_tcpip_receive(0, 0, aprsis_serial_port, aprsis_gsm_modem_state, 0, 2000);
if (retval == 0) {
receive_buff = srl_get_rx_buffer(aprsis_serial_port);
// check if receive buffer starts from printable character and needs fast forward or not
// maybe APRS-IS put a newline before hello message
offset = text_fast_forward_to_first_printable((char*)receive_buff, srl_get_num_bytes_rxed(aprsis_serial_port));
// if hello message has been received
if (offset >= 0 && (*(receive_buff + offset) == '#' && *(receive_buff + offset + 1) == ' ')) {
// send long string to server
gsm_sim800_tcpip_write((uint8_t *)aprsis_login_string, strlen(aprsis_login_string), aprsis_serial_port, aprsis_gsm_modem_state);
// store received login string (with an information about which server is connected now)
strncpy(aprsis_login_string_reveived, receive_buff + offset, APRSIS_LOGIN_STRING_RECEIVED_LN);
// wait for server response
retval = gsm_sim800_tcpip_receive(0, 0, aprsis_serial_port, aprsis_gsm_modem_state, 0, 2000);
if (retval == SIM800_OK) {
receive_buff = srl_get_rx_buffer(aprsis_serial_port);
aprsis_connected = 1;
aprsis_successfull_conn_counter++;
// fast forward to the beginning of a response
offset = text_fast_forward_to_first_printable((char*)receive_buff, srl_get_num_bytes_rxed(aprsis_serial_port));
// check if authorization has been successfull
retval = strncmp(aprsis_sucessfull_login, (const char * )(receive_buff + offset), (size_t)9);
if (retval == 0) {
aprsis_logged = 1;
// trigger GSM status APRS-IS packet, when connection is ready
rte_main_trigger_gsm_status = 1;
// set current timestamp as last
aprsis_last_keepalive_ts = master_time;
aprsis_last_keepalive_long_ts = aprsis_last_keepalive_ts;
if (auto_send_beacon != 0) {
aprsis_send_beacon(0, aprsis_callsign_with_ssid, main_string_latitude, main_symbol_f, main_string_longitude, main_symbol_s, main_config_data_basic);
}
// trigger GSM status packet
rte_main_trigger_gsm_loginstring_packet = 1;
// set timeout for aprs-is server
srl_switch_timeout(aprsis_serial_port, 1, APRSIS_TIMEOUT_MS);
// wait for consecutive data
gsm_sim800_tcpip_async_receive(aprsis_serial_port, aprsis_gsm_modem_state, 0, 61000, aprsis_receive_callback);
out = APRSIS_OK;
}
else {
// if authoruzation wasn't successfull drop a connection
disconnection_result = aprsis_disconnect();
// increase failure counter
aprsis_unsucessfull_conn_counter++;
}
}
}
else {
disconnection_result = aprsis_disconnect();
// increase failure counter
aprsis_unsucessfull_conn_counter++;
}
}
else {
disconnection_result = aprsis_disconnect();
// increase failure counter
aprsis_unsucessfull_conn_counter++;
}
}
// if a connection has been ordered to close, but there were severe errors during that
if (disconnection_result == SIM800_TCP_CLOSE_UNCERTAIN ||
//disconnection_result == SIM800_RECEIVING_TIMEOUT ||
aprsis_unsucessfull_conn_counter > APRSIS_FAILED_CONN_ATTEMPTS_TO_RESET_GSM) {
// and reset GSM modem
gsm_sim800_reset(aprsis_gsm_modem_state);
// reset unsuccesfull connection counter back to zero
aprsis_unsucessfull_conn_counter = 0;
}
}
return out;
}
/**
* Connect and login to APRS-IS using default credentials using during initialization
* @param auto_send_beacon
* @return
*/
aprsis_return_t aprsis_connect_and_login_default(uint8_t auto_send_beacon) {
return aprsis_connect_and_login(aprsis_default_server_address, aprsis_default_server_address_ln, aprsis_default_server_port, auto_send_beacon);
}
sim800_return_t aprsis_disconnect(void) {
sim800_return_t out = SIM800_UNSET;
if (aprsis_connected == 1) {
out = gsm_sim800_tcpip_close(aprsis_serial_port, aprsis_gsm_modem_state, 0);
aprsis_logged = 0;
aprsis_connected = 0;
}
return out;
}
/**
* Pooler function which check periodically if APRS-IS connection is alive.
*/
void aprsis_check_alive(void) {
const uint32_t timestamp = master_time;
uint8_t dead = 0;
if (aprsis_debug_simulate_timeout == 1) {
dead = 1;
}
if (aprsis_successfull_conn_counter > 0) {
if (timestamp > (aprsis_last_keepalive_ts + APRSIS_TIMEOUT_MS)) {
dead = 1;
}
if (timestamp > (aprsis_last_packet_transmit_ts + APRSIS_TIMEOUT_MS * 3 )) {
dead = 1;
}
}
// check if connection is alive
if (dead == 1){
// reset the flag
aprsis_logged = 0;
aprsis_connected = 0;
aprsis_debug_simulate_timeout = 0;
aprsis_last_keepalive_ts = master_time;
aprsis_last_packet_transmit_ts = master_time;
if (rte_main_curret_powersave_mode != PWSAVE_AGGRESV) {
// send a status message that APRS-IS connectios is gone
status_send_aprsis_timeout(aprsis_unsucessfull_conn_counter);
}
// check if it is intended to reset GSM modem in case of timeout
if (aprsis_reset_on_timeout == 0) {
// close connection with force flag as it is uncertain if a remote server
// finished connection explicitly, or the connection is stuck for
// some other reason
gsm_sim800_tcpip_close(aprsis_serial_port, aprsis_gsm_modem_state, 1);
}
else {
gsm_sim800_reset(aprsis_gsm_modem_state);
}
}
}
/**
* This is another alive check which is fully independent from
* if the connection has been even already established and how many times
* it waas. The intention here is to reset the whole controller if
* for some reason APRS-++IS connection cannot be established for very long time
* @return
*/
int aprsis_check_connection_attempt_alive(void) {
int out = 0;
const uint32_t timestamp = main_get_master_time();
if (timestamp > (aprsis_last_keepalive_long_ts + APRSIS_TIMEOUT_MS * 6)) {
out = 1;
}
if (timestamp > (aprsis_last_packet_transmit_long_ts + APRSIS_TIMEOUT_MS * 6 )) {
out = 1;
}
return out;
}
/**
*
* @param windspeed
* @param windgusts
* @param winddirection
* @param temperatura
* @param cisnienie
* @param humidity
* @param callsign_with_ssid
* @param string_latitude
* @param string_longitude
* @param config_data_basic
*/
void aprsis_send_wx_frame(
uint16_t windspeed,
uint16_t windgusts,
uint16_t winddirection,
float temperatura,
float cisnienie,
uint8_t humidity,
const char * callsign_with_ssid,
const char * string_latitude,
const char * string_longitude,
const config_data_basic_t * config_data_basic) {
if (aprsis_logged == 0) {
return;
}
if (gsm_sim800_tcpip_tx_busy() == 1) {
// will die here
backup_assert(BACKUP_REG_ASSERT_CONCURENT_ACCES_APRSIS_WX);
}
aprsis_tx_counter++;
float max_wind_speed = 0.0f, temp = 0.0f;
uint8_t wind_speed_mph = 0, wind_gusts_mph = 0;
uint32_t pressure = 0;
uint16_t direction = winddirection;
// windspeed is stored as an increment of 0.1 meters per second in 16bit unsigned integer
temp = ((float)windspeed / 10.0f);
max_wind_speed = ((float)windgusts / 10.0f);
temp /= 0.45; // Konwersja na mile na godzine
max_wind_speed /= 0.45;
if ((temp - (short)temp) >= 0.5) // Zaokraglanie wartosci
/* Odejmuje od wartosci zmiennoprzecinkowej w milach nad godzine wartosc
po zrzutowaniu na typ short czyli z odcienta czescia po przecinku.
Jezeli wynik jest wiekszy albo rowny 0.5 to trzeba zaokraglic w gore */
wind_speed_mph = (short)temp + 1;
else
/* A jezeli jest mniejsza niz 0.5 to zaokraglamy w dol */
wind_speed_mph = (short)temp;
if ((max_wind_speed - (short)max_wind_speed) >= 0.5)
/* Analogiczna procedura ma miejsce dla porywow wiatru*/
wind_gusts_mph = (short)max_wind_speed + 1;
else
wind_gusts_mph = (short)max_wind_speed;
pressure = (unsigned)(cisnienie * 10);
memset(aprsis_packet_tx_buffer, 0x00, sizeof(aprsis_packet_tx_buffer));
// main_own_aprs_msg_len = sprintf(main_own_aprs_msg, "=%s%c%c%s%c%c %s", main_string_latitude, main_config_data_basic->n_or_s, main_symbol_f, main_string_longitude, main_config_data_basic->e_or_w, main_symbol_s, main_config_data_basic->comment);
aprsis_packet_tx_message_size = snprintf(
aprsis_packet_tx_buffer,
APRSIS_TX_BUFFER_LN,
"%s>AKLPRZ,qAR,%s:!%s%c%c%s%c%c%03d/%03dg%03dt%03dr...p...P...b%05ldh%02d\r\n",
callsign_with_ssid,
callsign_with_ssid,
string_latitude,
config_data_basic->n_or_s,
'/',
string_longitude,
config_data_basic->e_or_w,
'_',
/* kierunek */direction,
/* predkosc*/(int)wind_speed_mph,
/* porywy */(short)(wind_gusts_mph),
/*temperatura */(short)(temperatura*1.8+32),
pressure,
humidity);
aprsis_packet_tx_buffer[aprsis_packet_tx_message_size] = 0;
aprsis_last_packet_transmit_ts = main_get_master_time();
aprsis_last_packet_transmit_long_ts = main_get_master_time();
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
/**
* Sends beacon packet to APRS-IS
* @param async zero for blocking io, which lock this function during transmission.
* non zero for non blocking io, function will return immediately and sending will be done in background
*/
void aprsis_send_beacon(
uint8_t async,
const char * callsign_with_ssid,
const char * string_latitude,
char symbol_f,
const char * string_longitude,
char symbol_s,
const config_data_basic_t * config_data_basic
) {
if (aprsis_logged == 0) {
return;
}
if (gsm_sim800_tcpip_tx_busy() == 1) {
// will die here
backup_assert(BACKUP_REG_ASSERT_CONCURENT_ACCES_APRSIS_BEACON);
}
aprsis_tx_counter++;
aprsis_packet_tx_message_size = snprintf(
aprsis_packet_tx_buffer,
APRSIS_TX_BUFFER_LN,
"%s>AKLPRZ,qAR,%s:=%s%c%c%s%c%c %s\r\n",
callsign_with_ssid,
callsign_with_ssid,
string_latitude,
config_data_basic->n_or_s,
symbol_f,
string_longitude,
config_data_basic->e_or_w,
symbol_s,
config_data_basic->comment);
aprsis_packet_tx_buffer[aprsis_packet_tx_message_size] = 0;
if (async > 0) {
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
else {
gsm_sim800_tcpip_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
aprsis_last_packet_transmit_ts = main_get_master_time();
}
/**
* Prepare telemetry packets to be sent later to the APRS-IS. Just store a string
* with all values, which will be then embedded into packet to be sent to APRS-IS
* @param _telemetry_counter
* @param _rx_pkts
* @param _tx_pkts
* @param _digi_pkts
* @param _scaled_vbatt_voltage
* @param _viscous_drop_pkts
* @param _scaled_temperature
* @param _telemetry_qf
* @param _telemetry_degr
* @param _telemetry_nav
* @param _telemetry_pressure_qf_navaliable
* @param _telemetry_humidity_qf_navaliable
* @param _telemetry_anemometer_degradated
* @param _telemetry_anemometer_navble
* @param _telemetry_vbatt_low
* @param _config_mode
*/
void aprsis_prepare_telemetry(
uint16_t _telemetry_counter,
uint8_t _rx_pkts,
uint8_t _tx_pkts,
uint8_t _digi_pkts,
uint8_t _scaled_vbatt_voltage,
uint8_t _viscous_drop_pkts,
uint8_t _scaled_temperature,
char _telemetry_qf,
char _telemetry_degr,
char _telemetry_nav,
char _telemetry_pressure_qf_navaliable,
char _telemetry_humidity_qf_navaliable,
char _telemetry_anemometer_degradated,
char _telemetry_anemometer_navble,
char _telemetry_vbatt_low,
const config_data_mode_t * const _config_mode) {
// clear buffer before doin anything
memset(aprsis_packet_telemetry_buffer, 0x00, APRSIS_TELEMETRY_BUFFER_LN);
// string lenght returned by snprintf
int snprintf_size = 0;
if (_config_mode->digi_viscous == 0) {
snprintf_size = snprintf(
aprsis_packet_telemetry_buffer,
APRSIS_TELEMETRY_BUFFER_LN,
"T#%03d,%03d,%03d,%03d,%03d,%03d,%c%c%c%c%c%c%c%c\r\n",
_telemetry_counter,
_rx_pkts,
_tx_pkts,
_digi_pkts,
_scaled_vbatt_voltage,
_scaled_temperature,
_telemetry_qf,
_telemetry_degr,
_telemetry_nav,
_telemetry_pressure_qf_navaliable,
_telemetry_humidity_qf_navaliable,
_telemetry_anemometer_degradated,
_telemetry_anemometer_navble,
_telemetry_vbatt_low);
}
else {
snprintf_size = snprintf(
aprsis_packet_telemetry_buffer,
APRSIS_TELEMETRY_BUFFER_LN,
"T#%03d,%03d,%03d,%03d,%03d,%03d,%c%c%c%c%c%c%c%c\r\n",
_telemetry_counter,
_rx_pkts,
_viscous_drop_pkts,
_digi_pkts,
_scaled_vbatt_voltage,
_scaled_temperature,
_telemetry_qf,
_telemetry_degr,
_telemetry_nav,
_telemetry_pressure_qf_navaliable,
_telemetry_humidity_qf_navaliable,
_telemetry_anemometer_degradated,
_telemetry_anemometer_navble,
_telemetry_vbatt_low);
}
aprsis_packet_telemetry_buffer[snprintf_size] = 0;
}
/**
* Sends to APRS-IS prepared telemetry frame prepared in advance
* @param async
* @param callsign_with_ssid
*/
void aprsis_send_telemetry(uint8_t async, const char * callsign_with_ssid) {
// exif if APRSIS is not logged
if (aprsis_logged == 0) {
return;
}
// exit if message is empty
if (*aprsis_packet_telemetry_buffer == 0) {
return;
}
if (gsm_sim800_tcpip_tx_busy() == 1) {
// will die here
backup_assert(BACKUP_REG_ASSERT_CONCURENT_ACCES_APRSIS_TELEMETRY);
}
aprsis_tx_counter++;
aprsis_packet_tx_message_size = snprintf(
aprsis_packet_tx_buffer,
APRSIS_TX_BUFFER_LN,
"%s>AKLPRZ,qAR,%s:%s\r\n",
callsign_with_ssid,
callsign_with_ssid,
aprsis_packet_telemetry_buffer);
aprsis_packet_tx_buffer[aprsis_packet_tx_message_size] = 0;
if (async > 0) {
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
else {
gsm_sim800_tcpip_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
aprsis_last_packet_transmit_ts = main_get_master_time();
// clear buffer after it has been used
memset(aprsis_packet_telemetry_buffer, 0x00, APRSIS_TELEMETRY_BUFFER_LN);
}
/**
* Sends
* @param async
* @param what kind of telemetry description packet should be sent now
* @param callsign_with_ssid
* @return what kind of telemetry description should be sent next
*/
telemetry_description_t aprsis_send_description_telemetry(uint8_t async,
const telemetry_description_t what,
const config_data_basic_t * const config_basic,
const config_data_mode_t * const config_mode,
const char * callsign_with_ssid) {
telemetry_description_t next = TELEMETRY_NOTHING;
// check what we want to send and what
switch (what) {
case TELEMETRY_PV_PARM: next = TELEMETRY_PV_EQNS; break;
case TELEMETRY_PV_EQNS: next = TELEMETRY_PV_UNIT; break;
case TELEMETRY_PV_UNIT: next = TELEMETRY_NOTHING; break;
case TELEMETRY_NORMAL_PARAM: next = TELEMETRY_NORMAL_EQNS; break;
case TELEMETRY_NORMAL_EQNS: next = TELEMETRY_NORMAL_UNIT; break;
case TELEMETRY_NORMAL_UNIT: next = TELEMETRY_NOTHING; break;
case TELEMETRY_VISCOUS_PARAM: next = TELEMETRY_VISCOUS_EQNS; break;
case TELEMETRY_VISCOUS_EQNS: next = TELEMETRY_VISCOUS_UNIT; break;
case TELEMETRY_VISCOUS_UNIT: next = TELEMETRY_NOTHING; break;
case TELEMETRY_NOTHING:
default: next = TELEMETRY_NOTHING; break;
}
// exif if APRSIS is not logged
if (aprsis_logged == 0) {
return next;
}
if (gsm_sim800_tcpip_tx_busy() == 1) {
// will die here
backup_assert(BACKUP_REG_ASSERT_CONCURENT_ACCES_APRSIS_DESCR);
}
telemetry_create_description_string(config_basic, what, main_own_aprs_msg, OWN_APRS_MSG_LN);
aprsis_tx_counter++;
aprsis_packet_tx_message_size = snprintf(
aprsis_packet_tx_buffer,
APRSIS_TX_BUFFER_LN,
"%s>AKLPRZ,qAR,%s:%s\r\n",
callsign_with_ssid,
callsign_with_ssid,
main_own_aprs_msg);
aprsis_packet_tx_buffer[aprsis_packet_tx_message_size] = 0;
if (async > 0) {
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
else {
gsm_sim800_tcpip_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
return next;
}
void aprsis_igate_to_aprsis(AX25Msg *msg, const char * callsign_with_ssid) {
// iterator to move across tx buffer
uint16_t tx_buffer_it = 0;
uint16_t payload_ln = 0;
// string lenght returned by snprintf
int snprintf_size = 0;
// exif if APRSIS is not logged
if (aprsis_logged == 0 || msg == 0) {
return;
}
if (gsm_sim800_tcpip_tx_busy() == 1) {
// will die here
backup_assert(BACKUP_REG_ASSERT_CONCURENT_ACCES_APRSIS_IGATE);
}
aprsis_igated_counter++;
// prepare buffer for message
memset(aprsis_packet_tx_buffer, 0x00, APRSIS_TX_BUFFER_LN);
// put callsign
strncat(aprsis_packet_tx_buffer, msg->src.call, 6);
// put source call
if ((msg->src.ssid & 0xF) != 0) {
snprintf_size = snprintf(aprsis_packet_tx_buffer, MAXIMUM_CALL_SSID_DASH_LN, "%.6s-%d>", msg->src.call, msg->src.ssid & 0xF);
}
else {
// callsign without SSID
snprintf_size = snprintf(aprsis_packet_tx_buffer, MAXIMUM_CALL_SSID_DASH_LN, "%.6s>", msg->src.call);
}
// move iterator forward
tx_buffer_it = (uint8_t) snprintf_size & 0xFFU;
// put destination call - for sake of simplicity ignore SSID
snprintf_size = snprintf(aprsis_packet_tx_buffer + tx_buffer_it, MAXIMUM_CALL_SSID_DASH_LN, "%.6s,", msg->dst.call);
// move iterator
tx_buffer_it += (uint8_t) snprintf_size & 0xFFU;
// put original path
for (int i = 0; i < msg->rpt_cnt; i++) {
if ((msg->rpt_lst[i].ssid & 0x0F) == 0) {
if ((msg->rpt_lst[i].ssid & 0x40) == 0x40)
snprintf_size = sprintf(aprsis_packet_tx_buffer + tx_buffer_it, "%.6s*,", msg->rpt_lst[i].call);
else
snprintf_size = sprintf(aprsis_packet_tx_buffer + tx_buffer_it, "%.6s,", msg->rpt_lst[i].call);
}
else {
if ((msg->rpt_lst[i].ssid & 0x40) == 0x40)
snprintf_size = sprintf(aprsis_packet_tx_buffer + tx_buffer_it, "%.6s-%d*,", msg->rpt_lst[i].call, (msg->rpt_lst[i].ssid & 0x0F));
else
snprintf_size = sprintf(aprsis_packet_tx_buffer + tx_buffer_it, "%.6s-%d,", msg->rpt_lst[i].call, (msg->rpt_lst[i].ssid & 0x0F));
}
// move iterator
tx_buffer_it += (uint8_t) snprintf_size & 0xFFU;
}
snprintf_size = snprintf(aprsis_packet_tx_buffer + tx_buffer_it, MAXIMUM_CALL_SSID_DASH_LN + 5, "qAR,%s:", callsign_with_ssid);
// move iterator
tx_buffer_it += (uint8_t) snprintf_size & 0xFFU;
// cut the data field if it is too long to fit in transmission buffer
if (msg->len + tx_buffer_it >= APRSIS_TX_BUFFER_LN) {
payload_ln = APRSIS_TX_BUFFER_LN - tx_buffer_it - 1; // keep one byte for null terminator
}
else {
payload_ln = msg->len;
}
memcpy(aprsis_packet_tx_buffer + tx_buffer_it, msg->info, payload_ln);
// move iterator forward by payload size
tx_buffer_it += payload_ln;
// put newline at the end
aprsis_packet_tx_buffer[tx_buffer_it++] = '\r';
aprsis_packet_tx_buffer[tx_buffer_it++] = '\n';
aprsis_packet_tx_message_size = strlen(aprsis_packet_tx_buffer);
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
void aprsis_send_server_comm_counters(const char * callsign_with_ssid) {
if (aprsis_logged == 0) {
return;
}
if (gsm_sim800_tcpip_tx_busy() == 1) {
// will die here
backup_assert(BACKUP_REG_ASSERT_CONCURENT_ACCES_APRSIS_CNTRS);
}
memset (aprsis_packet_tx_buffer, 0x00, APRSIS_TX_BUFFER_LN);
aprsis_tx_counter++;
aprsis_packet_tx_message_size = snprintf(
aprsis_packet_tx_buffer,
APRSIS_TX_BUFFER_LN - 1,
"%s>AKLPRZ,qAR,%s:>[aprsis][igated: %d][transmited: %d][keepalive: %d][another: %d]\r\n",
callsign_with_ssid,
callsign_with_ssid,
aprsis_igated_counter,
aprsis_tx_counter,
aprsis_keepalive_received_counter,
aprsis_another_received_counter);
aprsis_last_packet_transmit_ts = main_get_master_time();
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
void aprsis_send_loginstring(const char * callsign_with_ssid, uint8_t rtc_ok, uint16_t voltage) {
if (aprsis_logged == 0) {
return;
}
if (gsm_sim800_tcpip_tx_busy() == 1) {
// will die here
backup_assert(BACKUP_REG_ASSERT_CONCURENT_ACCES_APRSIS_LOGINSTRING);
}
memset (aprsis_packet_tx_buffer, 0x00, APRSIS_TX_BUFFER_LN);
aprsis_tx_counter++;
aprsis_packet_tx_message_size = snprintf(
aprsis_packet_tx_buffer,
APRSIS_TX_BUFFER_LN - 1,
"%s>AKLPRZ,qAR,%s:>[rtc_ok: %d][vbat: %d][aprsis]%s\r\n",
callsign_with_ssid,
callsign_with_ssid,
rtc_ok,
voltage,
aprsis_login_string_reveived);
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
void aprsis_send_gsm_status(const char * callsign_with_ssid) {
if (aprsis_logged == 0) {
return;
}
memset (aprsis_packet_tx_buffer, 0x00, APRSIS_TX_BUFFER_LN);
aprsis_tx_counter++;
// reuse a buffer for telemetry for this one occasion
gsm_sim800_create_status(aprsis_packet_telemetry_buffer, APRSIS_TELEMETRY_BUFFER_LN);
aprsis_packet_tx_message_size = snprintf(
aprsis_packet_tx_buffer,
APRSIS_TX_BUFFER_LN - 1,
"%s>AKLPRZ,qAR,%s:%s\r\n",
callsign_with_ssid,
callsign_with_ssid,
aprsis_packet_telemetry_buffer);
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
}
/**
*
* @param message
*/
void aprsis_send_ack_for_message(const message_t * const message) {
aprsis_packet_tx_message_size = message_create_ack_for((uint8_t*)aprsis_packet_tx_buffer, APRSIS_TX_BUFFER_LN - 1, message, MESSAGE_SOURCE_APRSIS);
gsm_sim800_tcpip_async_write((uint8_t *)aprsis_packet_tx_buffer, aprsis_packet_tx_message_size, aprsis_serial_port, aprsis_gsm_modem_state);
rte_main_received_message_source = MESSAGE_SOURCE_UNINITIALIZED;
}
#ifdef UNIT_TEST
char * aprsis_get_tx_buffer(void) {
return aprsis_packet_tx_buffer;
}
#endif
uint8_t aprsis_get_aprsis_logged(void) {
return aprsis_logged;
}
void aprsis_debug_set_simulate_timeout(void) {
aprsis_debug_simulate_timeout = 1;
}