kopia lustrzana https://github.com/SP8EBC/ParaTNC
1151 wiersze
32 KiB
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;
|
|
}
|