2020-04-03 21:21:35 +00:00
# include <Arduino.h>
# include <LoRa.h>
# include <APRS-Decoder.h>
# include <TinyGPS++.h>
2020-11-08 13:14:56 +00:00
# include <TimeLib.h>
# include <WiFi.h>
2020-04-03 21:21:35 +00:00
# include "display.h"
2020-11-03 19:11:10 +00:00
# include "pins.h"
2020-11-08 13:14:56 +00:00
# include "power_management.h"
2021-03-03 20:58:31 +00:00
# include "configuration.h"
Configuration Config ;
2020-04-03 21:21:35 +00:00
2020-11-08 13:14:56 +00:00
# include "power_management.h"
PowerManagement powerManagement ;
2020-04-03 21:21:35 +00:00
2021-03-03 20:58:31 +00:00
# include "logger.h"
2020-05-17 14:16:53 +00:00
HardwareSerial ss ( 1 ) ;
TinyGPSPlus gps ;
2020-11-08 13:14:56 +00:00
2021-03-06 22:47:25 +00:00
void load_config ( ) ;
2020-11-08 13:14:56 +00:00
void setup_lora ( ) ;
2020-05-17 14:16:53 +00:00
void setup_gps ( ) ;
2021-03-03 20:58:31 +00:00
2020-04-03 21:21:35 +00:00
String create_lat_aprs ( RawDegrees lat ) ;
String create_long_aprs ( RawDegrees lng ) ;
2021-06-24 12:33:52 +00:00
String create_lat_aprs_dao ( RawDegrees lat ) ;
String create_long_aprs_dao ( RawDegrees lng ) ;
String create_dao_aprs ( RawDegrees lat , RawDegrees lng ) ;
2020-11-09 22:09:58 +00:00
String createDateString ( time_t t ) ;
String createTimeString ( time_t t ) ;
2021-03-05 14:01:07 +00:00
String getSmartBeaconState ( ) ;
2021-03-19 21:13:01 +00:00
String padding ( unsigned int number , unsigned int width ) ;
2020-05-17 14:16:53 +00:00
2020-05-29 19:13:33 +00:00
// cppcheck-suppress unusedFunction
2020-04-03 21:21:35 +00:00
void setup ( )
{
Serial . begin ( 115200 ) ;
2021-03-19 21:13:01 +00:00
2020-11-08 13:14:56 +00:00
# ifdef TTGO_T_Beam_V1_0
Wire . begin ( SDA , SCL ) ;
if ( ! powerManagement . begin ( Wire ) )
{
2021-03-06 22:47:25 +00:00
logPrintlnI ( " AXP192 init done! " ) ;
2020-11-08 13:14:56 +00:00
}
else
{
2021-03-06 22:47:25 +00:00
logPrintlnE ( " AXP192 init failed! " ) ;
2020-11-08 13:14:56 +00:00
}
powerManagement . activateLoRa ( ) ;
powerManagement . activateOLED ( ) ;
powerManagement . activateGPS ( ) ;
2020-11-09 22:09:41 +00:00
powerManagement . activateMeasurement ( ) ;
2020-11-08 13:14:56 +00:00
# endif
2020-04-03 21:21:35 +00:00
delay ( 500 ) ;
2021-03-06 22:47:25 +00:00
logPrintlnI ( " LoRa APRS Tracker by OE5BPA (Peter Buchegger) " ) ;
2020-11-08 13:14:56 +00:00
setup_display ( ) ;
2021-03-05 14:01:07 +00:00
2021-03-06 22:47:25 +00:00
show_display ( " OE5BPA " , " LoRa APRS Tracker " , " by Peter Buchegger " , 2000 ) ;
load_config ( ) ;
2021-03-05 14:01:07 +00:00
2020-05-17 14:16:53 +00:00
setup_gps ( ) ;
2020-04-03 21:21:35 +00:00
setup_lora ( ) ;
2020-11-08 13:14:56 +00:00
2021-06-24 17:50:29 +00:00
if ( Config . ptt . active )
{
pinMode ( Config . ptt . io_pin , OUTPUT ) ;
digitalWrite ( Config . ptt . io_pin , Config . ptt . reverse ? HIGH : LOW ) ;
}
2020-11-08 13:14:56 +00:00
// make sure wifi and bt is off as we don't need it:
WiFi . mode ( WIFI_OFF ) ;
btStop ( ) ;
2021-03-06 22:47:25 +00:00
logPrintlnI ( " Smart Beacon is " + getSmartBeaconState ( ) ) ;
show_display ( " INFO " , " Smart Beacon is " + getSmartBeaconState ( ) , 1000 ) ;
logPrintlnI ( " setup done... " ) ;
2021-03-05 14:01:07 +00:00
delay ( 500 ) ;
2020-04-03 21:21:35 +00:00
}
2020-05-29 19:13:33 +00:00
// cppcheck-suppress unusedFunction
2020-04-03 21:21:35 +00:00
void loop ( )
{
2021-03-07 22:08:49 +00:00
if ( Config . debug )
2020-04-03 21:21:35 +00:00
{
2021-03-07 22:08:49 +00:00
while ( Serial . available ( ) > 0 )
2021-03-06 22:47:25 +00:00
{
2021-03-07 22:08:49 +00:00
char c = Serial . read ( ) ;
2021-03-06 22:47:25 +00:00
//Serial.print(c);
gps . encode ( c ) ;
}
2020-04-03 21:21:35 +00:00
}
2021-03-06 22:47:25 +00:00
else
2020-11-23 20:10:47 +00:00
{
2021-03-07 22:08:49 +00:00
while ( ss . available ( ) > 0 )
2021-03-06 22:47:25 +00:00
{
2021-03-07 22:08:49 +00:00
char c = ss . read ( ) ;
2021-03-06 22:47:25 +00:00
//Serial.print(c);
gps . encode ( c ) ;
}
2020-11-23 20:10:47 +00:00
}
2020-04-03 21:21:35 +00:00
2020-11-08 13:14:56 +00:00
bool gps_time_update = gps . time . isUpdated ( ) ;
2020-11-23 20:10:47 +00:00
bool gps_loc_update = gps . location . isUpdated ( ) ;
2020-11-09 22:09:58 +00:00
static time_t nextBeaconTimeStamp = - 1 ;
2021-03-06 22:47:25 +00:00
static bool send_update = true ;
2020-07-29 13:04:14 +00:00
2021-04-19 20:34:04 +00:00
static double currentHeading = 0 ;
static double previousHeading = 0 ;
static unsigned int rate_limit_message_text = 0 ;
2020-11-01 22:19:32 +00:00
if ( gps . time . isValid ( ) )
2020-07-29 13:04:14 +00:00
{
2021-03-06 22:47:25 +00:00
setTime ( gps . time . hour ( ) , gps . time . minute ( ) , gps . time . second ( ) , gps . date . day ( ) , gps . date . month ( ) , gps . date . year ( ) ) ;
2020-11-01 22:19:32 +00:00
2021-04-19 20:34:04 +00:00
if ( gps_loc_update & & nextBeaconTimeStamp < = now ( ) )
2020-11-01 22:19:32 +00:00
{
send_update = true ;
2021-04-19 20:34:04 +00:00
if ( Config . smart_beacon . active )
{
currentHeading = gps . course . deg ( ) ;
// enforce message text on slowest Config.smart_beacon.slow_rate
rate_limit_message_text = 0 ;
}
else
{
// enforce message text every n's Config.beacon.timeout frame
if ( Config . beacon . timeout * rate_limit_message_text > 30 )
{
rate_limit_message_text = 0 ;
}
}
2020-11-01 22:19:32 +00:00
}
2020-11-23 20:10:47 +00:00
}
2021-03-06 22:47:25 +00:00
static double lastTxLat = 0.0 ;
static double lastTxLng = 0.0 ;
static double lastTxdistance = 0.0 ;
2021-04-19 20:34:04 +00:00
static uint32_t txInterval = 60000L ; // Initial 60 secs internal
static uint32_t lastTxTime = millis ( ) ;
static int speed_zero_sent = 0 ;
2021-03-06 22:47:25 +00:00
2021-04-19 20:34:04 +00:00
static bool BatteryIsConnected = false ;
static String batteryVoltage = " " ;
static String batteryChargeCurrent = " " ;
# ifdef TTGO_T_Beam_V1_0
static unsigned int rate_limit_check_battery = 0 ;
if ( ! ( rate_limit_check_battery + + % 60 ) )
BatteryIsConnected = powerManagement . isBatteryConnect ( ) ;
if ( BatteryIsConnected ) {
batteryVoltage = String ( powerManagement . getBatteryVoltage ( ) , 2 ) ;
batteryChargeCurrent = String ( powerManagement . getBatteryChargeDischargeCurrent ( ) , 0 ) ;
}
# endif
2021-03-06 22:47:25 +00:00
2021-04-19 20:34:04 +00:00
if ( ! send_update & & gps_loc_update & & Config . smart_beacon . active )
2021-03-06 22:47:25 +00:00
{
2021-04-19 20:34:04 +00:00
uint32_t lastTx = millis ( ) - lastTxTime ;
currentHeading = gps . course . deg ( ) ;
lastTxdistance = TinyGPSPlus : : distanceBetween ( gps . location . lat ( ) , gps . location . lng ( ) , lastTxLat , lastTxLng ) ;
if ( lastTx > = txInterval )
{
// Trigger Tx Tracker when Tx interval is reach
// Will not Tx if stationary bcos speed < 5 and lastTxDistance < 20
if ( lastTxdistance > 20 )
{
send_update = true ;
}
}
if ( ! send_update )
2020-11-23 20:10:47 +00:00
{
2021-03-06 22:47:25 +00:00
// Get headings and heading delta
double headingDelta = abs ( previousHeading - currentHeading ) ;
if ( lastTx > Config . smart_beacon . min_bcn * 1000 )
2020-11-23 20:10:47 +00:00
{
2021-03-06 22:47:25 +00:00
// Check for heading more than 25 degrees
if ( headingDelta > Config . smart_beacon . turn_min & & lastTxdistance > Config . smart_beacon . min_tx_dist )
{
send_update = true ;
}
2020-11-23 20:10:47 +00:00
}
}
2020-07-29 13:04:14 +00:00
}
2021-03-06 22:47:25 +00:00
if ( send_update & & gps_loc_update )
2020-07-29 13:04:14 +00:00
{
send_update = false ;
2021-04-19 20:34:04 +00:00
nextBeaconTimeStamp = now ( ) + ( Config . smart_beacon . active ? Config . smart_beacon . slow_rate : ( Config . beacon . timeout * SECS_PER_MIN ) ) ;
2020-07-29 13:04:14 +00:00
APRSMessage msg ;
2021-06-24 12:33:52 +00:00
String lat ;
String lng ;
String dao ;
2021-03-03 20:58:31 +00:00
msg . setSource ( Config . callsign ) ;
2021-04-19 20:34:04 +00:00
msg . setDestination ( " APLT00-1 " ) ;
2021-06-24 12:33:52 +00:00
if ( ! Config . enhance_precision ) {
lat = create_lat_aprs ( gps . location . rawLat ( ) ) ;
lng = create_long_aprs ( gps . location . rawLng ( ) ) ;
} else {
lat = create_lat_aprs_dao ( gps . location . rawLat ( ) ) ;
lng = create_long_aprs_dao ( gps . location . rawLng ( ) ) ;
dao = create_dao_aprs ( gps . location . rawLat ( ) , gps . location . rawLng ( ) ) ;
}
2021-04-19 20:34:04 +00:00
String alt = " " ;
int alt_int = max ( - 99999 , min ( 999999 , ( int ) gps . altitude . feet ( ) ) ) ;
if ( alt_int < 0 )
{
alt = " /A=- " + padding ( alt_int * - 1 , 5 ) ;
}
else
{
alt = " /A= " + padding ( alt_int , 6 ) ;
}
String course_and_speed = " " ;
int speed_int = max ( 0 , min ( 999 , ( int ) gps . speed . knots ( ) ) ) ;
if ( speed_zero_sent < 3 )
{
String speed = padding ( speed_int , 3 ) ;
int course_int = max ( 0 , min ( 360 , ( int ) gps . course . deg ( ) ) ) ;
/* course in between 1..360 due to aprs spec */
if ( course_int = = 0 )
{
course_int = 360 ;
}
String course = padding ( course_int , 3 ) ;
course_and_speed = course + " / " + speed ;
}
if ( speed_int = = 0 )
{
/* speed is 0.
we send 3 packets with speed zero ( so our friends know we stand still ) .
After that , we save airtime by not sending speed / course 000 / 000.
Btw , even if speed we really do not move , measured course is changeing ( - > no useful / even wrong info )
*/
if ( speed_zero_sent < 3 )
{
speed_zero_sent + = 1 ;
}
}
else
{
speed_zero_sent = 0 ;
}
2021-03-05 14:01:07 +00:00
2021-04-19 20:34:04 +00:00
String aprsmsg ;
aprsmsg = " ! " + lat + Config . beacon . overlay + lng + Config . beacon . symbol + course_and_speed + alt ;
// message_text every 10's packet (i.e. if we have beacon rate 1min at high speed -> every 10min). May be enforced above (at expirey of smart beacon rate (i.e. every 30min), or every third packet on static rate (i.e. static rate 10 -> every third packet)
if ( ! ( rate_limit_message_text + + % 10 ) )
{
aprsmsg + = Config . beacon . message ;
}
if ( BatteryIsConnected )
{
aprsmsg + = " - _Bat.: " + batteryVoltage + " V - Cur.: " + batteryChargeCurrent + " mA " ;
}
2021-06-24 12:33:52 +00:00
if ( Config . enhance_precision ) {
aprsmsg + = " " + dao ;
}
2021-04-19 20:34:04 +00:00
msg . getAPRSBody ( ) - > setData ( aprsmsg ) ;
2020-07-29 13:04:14 +00:00
String data = msg . encode ( ) ;
2021-03-06 22:47:25 +00:00
logPrintlnD ( data ) ;
2020-07-29 13:04:14 +00:00
show_display ( " << TX >> " , data ) ;
2021-06-24 17:50:29 +00:00
if ( Config . ptt . active )
{
digitalWrite ( Config . ptt . io_pin , Config . ptt . reverse ? LOW : HIGH ) ;
delay ( Config . ptt . start_delay ) ;
}
2020-07-29 13:04:14 +00:00
LoRa . beginPacket ( ) ;
// Header:
LoRa . write ( ' < ' ) ;
LoRa . write ( 0xFF ) ;
LoRa . write ( 0x01 ) ;
// APRS Data:
LoRa . write ( ( const uint8_t * ) data . c_str ( ) , data . length ( ) ) ;
LoRa . endPacket ( ) ;
2020-11-23 20:10:47 +00:00
2021-03-05 14:01:07 +00:00
if ( Config . smart_beacon . active )
2021-03-06 22:47:25 +00:00
{
2021-03-05 14:01:07 +00:00
lastTxLat = gps . location . lat ( ) ;
lastTxLng = gps . location . lng ( ) ;
previousHeading = currentHeading ;
2021-03-06 22:47:25 +00:00
lastTxdistance = 0.0 ;
2021-03-05 14:01:07 +00:00
lastTxTime = millis ( ) ;
2021-03-06 22:47:25 +00:00
}
2021-06-24 17:50:29 +00:00
if ( Config . ptt . active )
{
delay ( Config . ptt . end_delay ) ;
digitalWrite ( Config . ptt . io_pin , Config . ptt . reverse ? HIGH : LOW ) ;
}
2020-07-29 13:04:14 +00:00
}
if ( gps_time_update )
{
2021-03-03 20:58:31 +00:00
show_display ( Config . callsign ,
2020-11-09 22:09:58 +00:00
createDateString ( now ( ) ) + " " + createTimeString ( now ( ) ) ,
String ( " Sats: " ) + gps . satellites . value ( ) + " HDOP: " + gps . hdop . hdop ( ) ,
2021-04-19 20:34:04 +00:00
String ( " Nxt Bcn: " ) + ( Config . smart_beacon . active ? " ~ " : " " ) + createTimeString ( nextBeaconTimeStamp ) ,
BatteryIsConnected ? ( String ( " Bat: " ) + batteryVoltage + " V, " + batteryChargeCurrent + " mA " ) : " Powered via USB " ,
String ( " Smart Beacon: " + getSmartBeaconState ( ) ) ) ;
2020-11-23 20:10:47 +00:00
2021-03-06 22:47:25 +00:00
if ( Config . smart_beacon . active )
{
2021-04-19 20:34:04 +00:00
// Change the Tx internal based on the current speed
int curr_speed = ( int ) gps . speed . kmph ( ) ;
if ( curr_speed < Config . smart_beacon . slow_speed )
{
txInterval = Config . smart_beacon . slow_rate * 1000 ;
}
else if ( curr_speed > Config . smart_beacon . fast_speed )
{
txInterval = Config . smart_beacon . fast_rate * 1000 ;
}
else
{
/* Interval inbetween low and high speed
min ( slow_rate , . . ) because : if slow rate is 300 s at slow speed < = 10 km / h and fast rate is 60 s at fast speed > = 100 km / h
everything below current speed 20 km / h ( 100 * 60 / 20 = 300 ) is below slow_rate .
- > In the first check , if curr speed is 5 km / h ( which is < 10 km / h ) , tx interval is 300 s , but if speed is 6 km / h , we are landing in this section ,
what leads to interval 100 * 60 / 6 = 1000 s ( 16.6 min ) - > this would lead to decrease of beacon rate in between 5 to 20 km / h . what is even below
the slow speed rate .
*/
txInterval = min ( Config . smart_beacon . slow_rate , Config . smart_beacon . fast_speed * Config . smart_beacon . fast_rate / curr_speed ) * 1000 ;
}
2021-03-05 14:01:07 +00:00
}
2020-04-03 21:21:35 +00:00
}
2021-03-05 14:01:07 +00:00
if ( ( Config . debug = = false ) & & ( millis ( ) > 5000 & & gps . charsProcessed ( ) < 10 ) )
2020-04-03 21:21:35 +00:00
{
2021-03-06 22:47:25 +00:00
logPrintlnE ( " No GPS frames detected! Try to reset the GPS Chip with this firmware: https://github.com/lora-aprs/TTGO-T-Beam_GPS-reset " ) ;
2020-04-03 21:21:35 +00:00
}
2021-03-06 22:47:25 +00:00
}
2021-03-05 14:01:07 +00:00
2021-03-06 22:47:25 +00:00
void load_config ( )
{
ConfigurationManagement confmg ( " /tracker.json " ) ;
Config = confmg . readConfiguration ( ) ;
if ( Config . callsign = = " NOCALL-10 " )
{
2021-06-21 16:21:18 +00:00
logPrintlnE ( " You have to change your settings in 'data/tracker.json' and upload it via \" Upload File System image \" ! " ) ;
show_display ( " ERROR " , " You have to change your settings in 'data/tracker.json' and upload it via \" Upload File System image \" ! " ) ;
2021-03-06 22:47:25 +00:00
while ( true )
{ }
}
2020-04-03 21:21:35 +00:00
}
void setup_lora ( )
{
2021-03-06 22:47:25 +00:00
logPrintlnI ( " Set SPI pins! " ) ;
2020-05-11 14:11:07 +00:00
SPI . begin ( LORA_SCK , LORA_MISO , LORA_MOSI , LORA_CS ) ;
2021-03-06 22:47:25 +00:00
logPrintlnI ( " Set LoRa pins! " ) ;
2020-05-11 14:11:07 +00:00
LoRa . setPins ( LORA_CS , LORA_RST , LORA_IRQ ) ;
2020-04-03 21:21:35 +00:00
2021-03-03 20:58:31 +00:00
long freq = Config . lora . frequencyTx ;
2021-03-06 22:47:25 +00:00
logPrintI ( " frequency: " ) ;
logPrintlnI ( String ( freq ) ) ;
2020-04-03 21:21:35 +00:00
if ( ! LoRa . begin ( freq ) ) {
2021-03-06 22:47:25 +00:00
logPrintlnE ( " Starting LoRa failed! " ) ;
2020-04-03 21:21:35 +00:00
show_display ( " ERROR " , " Starting LoRa failed! " ) ;
2021-03-06 22:47:25 +00:00
while ( true )
{ }
2020-04-03 21:21:35 +00:00
}
2021-03-03 20:58:31 +00:00
LoRa . setSpreadingFactor ( Config . lora . spreadingFactor ) ;
LoRa . setSignalBandwidth ( Config . lora . signalBandwidth ) ;
LoRa . setCodingRate4 ( Config . lora . codingRate4 ) ;
2020-04-03 21:21:35 +00:00
LoRa . enableCrc ( ) ;
2020-04-08 11:03:11 +00:00
2021-03-06 22:47:25 +00:00
LoRa . setTxPower ( Config . lora . power ) ;
logPrintlnI ( " LoRa init done! " ) ;
2020-04-03 21:21:35 +00:00
show_display ( " INFO " , " LoRa init done! " , 2000 ) ;
}
2020-05-17 14:16:53 +00:00
void setup_gps ( )
{
ss . begin ( 9600 , SERIAL_8N1 , GPS_TX , GPS_RX ) ;
}
2020-04-03 21:21:35 +00:00
String create_lat_aprs ( RawDegrees lat )
{
char str [ 20 ] ;
char n_s = ' N ' ;
if ( lat . negative )
{
n_s = ' S ' ;
}
sprintf ( str , " %02d%05.2f%c " , lat . deg , lat . billionths / 1000000000.0 * 60.0 , n_s ) ;
String lat_str ( str ) ;
return lat_str ;
}
2021-06-24 12:33:52 +00:00
String create_lat_aprs_dao ( RawDegrees lat )
{
//round to 4 digits and cut the last 2
char str [ 20 ] ;
char n_s = ' N ' ;
if ( lat . negative )
{
n_s = ' S ' ;
}
sprintf ( str , " %02d%07.4f%c " , lat . deg , lat . billionths / 1000000000.0 * 60.0 , n_s ) ;
String lat_str ( str ) ;
lat_str . remove ( 7 , 2 ) ;
return lat_str ;
}
2020-04-03 21:21:35 +00:00
String create_long_aprs ( RawDegrees lng )
{
char str [ 20 ] ;
char e_w = ' E ' ;
if ( lng . negative )
{
e_w = ' W ' ;
}
sprintf ( str , " %03d%05.2f%c " , lng . deg , lng . billionths / 1000000000.0 * 60.0 , e_w ) ;
String lng_str ( str ) ;
return lng_str ;
}
2020-11-08 13:14:56 +00:00
2021-06-24 12:33:52 +00:00
String create_long_aprs_dao ( RawDegrees lng )
{
//round to 4 digits and cut the last 2
char str [ 20 ] ;
char e_w = ' E ' ;
if ( lng . negative )
{
e_w = ' W ' ;
}
sprintf ( str , " %03d%07.4f%c " , lng . deg , lng . billionths / 1000000000.0 * 60.0 , e_w ) ;
String lng_str ( str ) ;
lng_str . remove ( 8 , 2 ) ;
return lng_str ;
}
String create_dao_aprs ( RawDegrees lat , RawDegrees lng )
{
// !DAO! extension, use Base91 format for best precision
// /1.1 : scale from 0-99 to 0-90 for base91, int(... + 0.5): round to nearest integer
// https://metacpan.org/dist/Ham-APRS-FAP/source/FAP.pm
// http://www.aprs.org/aprs12/datum.txt
//
// TODO: optimize ugly float to char to string to int conversion?
char str [ 10 ] ;
char lat_str [ 10 ] ;
char lng_str [ 10 ] ;
sprintf ( lat_str , " %07.4f " , lat . billionths / 1000000000.0 * 60.0 ) ;
sprintf ( lng_str , " %07.4f " , lng . billionths / 1000000000.0 * 60.0 ) ;
String lat_dao ( lat_str ) ;
String lng_dao ( lng_str ) ;
int lat_int = lat_dao . substring ( 5 , 7 ) . toInt ( ) ;
int lng_int = lng_dao . substring ( 5 , 7 ) . toInt ( ) ;
char lat_char = char ( int ( ( lat_int / 1.1 + 0.5 ) + 33 ) ) ;
char lng_char = char ( int ( ( lng_int / 1.1 + 0.5 ) + 33 ) ) ;
sprintf ( str , " !w%c%c! " , lat_char , lng_char ) ;
String dao_str ( str ) ;
return dao_str ;
}
2020-11-09 22:09:58 +00:00
String createDateString ( time_t t )
{
2021-03-19 21:13:01 +00:00
return String ( padding ( day ( t ) , 2 ) + " . " + padding ( month ( t ) , 2 ) + " . " + padding ( year ( t ) , 4 ) ) ;
2020-11-09 22:09:58 +00:00
}
String createTimeString ( time_t t )
2020-11-08 13:14:56 +00:00
{
2020-11-09 22:09:58 +00:00
if ( t = = - 1 )
{
return String ( " 00:00:00 " ) ;
}
2021-03-19 21:13:01 +00:00
return String ( padding ( hour ( t ) , 2 ) + " . " + padding ( minute ( t ) , 2 ) + " . " + padding ( second ( t ) , 2 ) ) ;
2020-11-08 13:14:56 +00:00
}
2021-03-05 14:01:07 +00:00
String getSmartBeaconState ( )
{
2021-03-06 22:47:25 +00:00
if ( Config . smart_beacon . active )
2021-03-05 14:01:07 +00:00
{
2021-03-06 22:47:25 +00:00
return " On " ;
2021-03-05 14:01:07 +00:00
}
2021-03-06 22:47:25 +00:00
return " Off " ;
2021-03-05 14:01:07 +00:00
}
2021-03-19 21:13:01 +00:00
String padding ( unsigned int number , unsigned int width )
{
String result ;
String num ( number ) ;
if ( num . length ( ) > width )
{
width = num . length ( ) ;
}
for ( unsigned int i = 0 ; i < width - num . length ( ) ; i + + )
{
result . concat ( ' 0 ' ) ;
}
result . concat ( num ) ;
return result ;
}