diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index c99c6dd7bcc..3bf401d53de 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -3,25 +3,50 @@ App( name="Sub-GHz", apptype=FlipperAppType.APP, targets=["f7"], - cdefines=["APP_SUBGHZ"], entry_point="subghz_app", - provides=[ - "subghz_start", - ], icon="A_Sub1ghz_14", stack_size=3 * 1024, - order=1, + order=10, + # Sources separation breaks linking with subghz on internal, commented for now + # sources=[ + # "*.c", + # "!subghz_cli.c", + # "!helpers/subghz_chat.c", + # "!subghz_extended_freq.c", + # ], resources="resources", - fap_libs=["assets", "hwdrivers"], + fap_libs=["hwdrivers"], + fap_icon="icon.png", fap_category="Sub-GHz", - # fap_icon_assets="images", + sdk_headers=["subghz_fap.h"], +) + +App( + appid="subghz_fap", + name="SubGHz", + apptype=FlipperAppType.EXTERNAL, + entry_point="subghz_fap", + stack_size=3 * 1024, + sources=["subghz_fap.c"], fap_icon="icon.png", + fap_category="Sub-GHz", ) + App( appid="subghz_start", targets=["f7"], apptype=FlipperAppType.STARTUP, entry_point="subghz_on_system_start", + # sources=["subghz_cli.c", "helpers/subghz_chat.c"], order=40, ) + +App( + appid="subghz_load_extended_settings", + targets=["f7"], + apptype=FlipperAppType.STARTUP, + entry_point="subghz_extended_freq", + # sources=["subghz_extended_freq.c"], + order=650, +) diff --git a/applications/main/subghz/helpers/minmea.c b/applications/main/subghz/helpers/minmea.c new file mode 100644 index 00000000000..a8be53de14c --- /dev/null +++ b/applications/main/subghz/helpers/minmea.c @@ -0,0 +1,640 @@ +/* + * Copyright © 2014 Kosma Moczek + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#include "minmea.h" + +#include +#include +#include + +#define boolstr(s) ((s) ? "true" : "false") + +static int hex2int(char c) { + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'A' && c <= 'F') return c - 'A' + 10; + if(c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; +} + +uint8_t minmea_checksum(const char* sentence) { + // Support senteces with or without the starting dollar sign. + if(*sentence == '$') sentence++; + + uint8_t checksum = 0x00; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while(*sentence && *sentence != '*') checksum ^= *sentence++; + + return checksum; +} + +bool minmea_check(const char* sentence, bool strict) { + uint8_t checksum = 0x00; + + // A valid sentence starts with "$". + if(*sentence++ != '$') return false; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while(*sentence && *sentence != '*' && isprint((unsigned char)*sentence)) + checksum ^= *sentence++; + + // If checksum is present... + if(*sentence == '*') { + // Extract checksum. + sentence++; + int upper = hex2int(*sentence++); + if(upper == -1) return false; + int lower = hex2int(*sentence++); + if(lower == -1) return false; + int expected = upper << 4 | lower; + + // Check for checksum mismatch. + if(checksum != expected) return false; + } else if(strict) { + // Discard non-checksummed frames in strict mode. + return false; + } + + // The only stuff allowed at this point is a newline. + while(*sentence == '\r' || *sentence == '\n') { + sentence++; + } + + if(*sentence) { + return false; + } + + return true; +} + +bool minmea_scan(const char* sentence, const char* format, ...) { + bool result = false; + bool optional = false; + + if(sentence == NULL) return false; + + va_list ap; + va_start(ap, format); + + const char* field = sentence; +#define next_field() \ + do { \ + /* Progress to the next field. */ \ + while(minmea_isfield(*sentence)) sentence++; \ + /* Make sure there is a field there. */ \ + if(*sentence == ',') { \ + sentence++; \ + field = sentence; \ + } else { \ + field = NULL; \ + } \ + } while(0) + + while(*format) { + char type = *format++; + + if(type == ';') { + // All further fields are optional. + optional = true; + continue; + } + + if(!field && !optional) { + // Field requested but we ran out if input. Bail out. + goto parse_error; + } + + switch(type) { + case 'c': { // Single character field (char). + char value = '\0'; + + if(field && minmea_isfield(*field)) value = *field; + + *va_arg(ap, char*) = value; + } break; + + case 'd': { // Single character direction field (int). + int value = 0; + + if(field && minmea_isfield(*field)) { + switch(*field) { + case 'N': + case 'E': + value = 1; + break; + case 'S': + case 'W': + value = -1; + break; + default: + goto parse_error; + } + } + + *va_arg(ap, int*) = value; + } break; + + case 'f': { // Fractional value with scale (struct minmea_float). + int sign = 0; + int_least32_t value = -1; + int_least32_t scale = 0; + + if(field) { + while(minmea_isfield(*field)) { + if(*field == '+' && !sign && value == -1) { + sign = 1; + } else if(*field == '-' && !sign && value == -1) { + sign = -1; + } else if(isdigit((unsigned char)*field)) { + int digit = *field - '0'; + if(value == -1) value = 0; + if(value > (INT_LEAST32_MAX - digit) / 10) { + /* we ran out of bits, what do we do? */ + if(scale) { + /* truncate extra precision */ + break; + } else { + /* integer overflow. bail out. */ + goto parse_error; + } + } + value = (10 * value) + digit; + if(scale) scale *= 10; + } else if(*field == '.' && scale == 0) { + scale = 1; + } else if(*field == ' ') { + /* Allow spaces at the start of the field. Not NMEA + * conformant, but some modules do this. PP */ + if(sign != 0 || value != -1 || scale != 0) goto parse_error; + } else { + goto parse_error; + } + field++; + } + } + + if((sign || scale) && value == -1) goto parse_error; + + if(value == -1) { + /* No digits were scanned. */ + value = 0; + scale = 0; + } else if(scale == 0) { + /* No decimal point. */ + scale = 1; + } + if(sign) value *= sign; + + *va_arg(ap, struct minmea_float*) = (struct minmea_float){value, scale}; + } break; + + case 'i': { // Integer value, default 0 (int). + int value = 0; + + if(field) { + char* endptr; + value = strtol(field, &endptr, 10); + if(minmea_isfield(*endptr)) goto parse_error; + } + + *va_arg(ap, int*) = value; + } break; + + case 's': { // String value (char *). + char* buf = va_arg(ap, char*); + + if(field) { + while(minmea_isfield(*field)) *buf++ = *field++; + } + + *buf = '\0'; + } break; + + case 't': { // NMEA talker+sentence identifier (char *). + // This field is always mandatory voss. + if(!field) goto parse_error; + + if(field[0] != '$') goto parse_error; + for(int f = 0; f < 5; f++) + if(!minmea_isfield(field[1 + f])) goto parse_error; + + char* buf = va_arg(ap, char*); + memcpy(buf, field + 1, 5); + buf[5] = '\0'; + } break; + + case 'D': { // Date (int, int, int), -1 if empty. + struct minmea_date* date = va_arg(ap, struct minmea_date*); + + int d = -1, m = -1, y = -1; + + if(field && minmea_isfield(*field)) { + // Always six digits. + for(int f = 0; f < 6; f++) + if(!isdigit((unsigned char)field[f])) goto parse_error; + + char dArr[] = {field[0], field[1], '\0'}; + char mArr[] = {field[2], field[3], '\0'}; + char yArr[] = {field[4], field[5], '\0'}; + d = strtol(dArr, NULL, 10); + m = strtol(mArr, NULL, 10); + y = strtol(yArr, NULL, 10); + } + + date->day = d; + date->month = m; + date->year = y; + } break; + + case 'T': { // Time (int, int, int, int), -1 if empty. + struct minmea_time* time_ = va_arg(ap, struct minmea_time*); + + int h = -1, i = -1, s = -1, u = -1; + + if(field && minmea_isfield(*field)) { + // Minimum required: integer time. + for(int f = 0; f < 6; f++) + if(!isdigit((unsigned char)field[f])) goto parse_error; + + char hArr[] = {field[0], field[1], '\0'}; + char iArr[] = {field[2], field[3], '\0'}; + char sArr[] = {field[4], field[5], '\0'}; + h = strtol(hArr, NULL, 10); + i = strtol(iArr, NULL, 10); + s = strtol(sArr, NULL, 10); + field += 6; + + // Extra: fractional time. Saved as microseconds. + if(*field++ == '.') { + uint32_t value = 0; + uint32_t scale = 1000000LU; + while(isdigit((unsigned char)*field) && scale > 1) { + value = (value * 10) + (*field++ - '0'); + scale /= 10; + } + u = value * scale; + } else { + u = 0; + } + } + + time_->hours = h; + time_->minutes = i; + time_->seconds = s; + time_->microseconds = u; + } break; + + case '_': { // Ignore the field. + } break; + + default: { // Unknown. + goto parse_error; + } + } + + next_field(); + } + + result = true; + +parse_error: + va_end(ap); + return result; +} + +bool minmea_talker_id(char talker[3], const char* sentence) { + char type[6]; + if(!minmea_scan(sentence, "t", type)) return false; + + talker[0] = type[0]; + talker[1] = type[1]; + talker[2] = '\0'; + + return true; +} + +enum minmea_sentence_id minmea_sentence_id(const char* sentence, bool strict) { + if(!minmea_check(sentence, strict)) return MINMEA_INVALID; + + char type[6]; + if(!minmea_scan(sentence, "t", type)) return MINMEA_INVALID; + + if(!strcmp(type + 2, "GBS")) return MINMEA_SENTENCE_GBS; + if(!strcmp(type + 2, "GGA")) return MINMEA_SENTENCE_GGA; + if(!strcmp(type + 2, "GLL")) return MINMEA_SENTENCE_GLL; + if(!strcmp(type + 2, "GSA")) return MINMEA_SENTENCE_GSA; + if(!strcmp(type + 2, "GST")) return MINMEA_SENTENCE_GST; + if(!strcmp(type + 2, "GSV")) return MINMEA_SENTENCE_GSV; + if(!strcmp(type + 2, "RMC")) return MINMEA_SENTENCE_RMC; + if(!strcmp(type + 2, "VTG")) return MINMEA_SENTENCE_VTG; + if(!strcmp(type + 2, "ZDA")) return MINMEA_SENTENCE_ZDA; + + return MINMEA_UNKNOWN; +} + +bool minmea_parse_gbs(struct minmea_sentence_gbs* frame, const char* sentence) { + // $GNGBS,170556.00,3.0,2.9,8.3,,,,*5C + char type[6]; + if(!minmea_scan( + sentence, + "tTfffifff", + type, + &frame->time, + &frame->err_latitude, + &frame->err_longitude, + &frame->err_altitude, + &frame->svid, + &frame->prob, + &frame->bias, + &frame->stddev)) + return false; + if(strcmp(type + 2, "GBS")) return false; + + return true; +} + +bool minmea_parse_rmc(struct minmea_sentence_rmc* frame, const char* sentence) { + // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 + char type[6]; + char validity; + int latitude_direction; + int longitude_direction; + int variation_direction; + if(!minmea_scan( + sentence, + "tTcfdfdffDfd", + type, + &frame->time, + &validity, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->speed, + &frame->course, + &frame->date, + &frame->variation, + &variation_direction)) + return false; + if(strcmp(type + 2, "RMC")) return false; + + frame->valid = (validity == 'A'); + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + frame->variation.value *= variation_direction; + + return true; +} + +bool minmea_parse_gga(struct minmea_sentence_gga* frame, const char* sentence) { + // $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 + char type[6]; + int latitude_direction; + int longitude_direction; + + if(!minmea_scan( + sentence, + "tTfdfdiiffcfcf_", + type, + &frame->time, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->fix_quality, + &frame->satellites_tracked, + &frame->hdop, + &frame->altitude, + &frame->altitude_units, + &frame->height, + &frame->height_units, + &frame->dgps_age)) + return false; + if(strcmp(type + 2, "GGA")) return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gsa(struct minmea_sentence_gsa* frame, const char* sentence) { + // $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39 + char type[6]; + + if(!minmea_scan( + sentence, + "tciiiiiiiiiiiiifff", + type, + &frame->mode, + &frame->fix_type, + &frame->sats[0], + &frame->sats[1], + &frame->sats[2], + &frame->sats[3], + &frame->sats[4], + &frame->sats[5], + &frame->sats[6], + &frame->sats[7], + &frame->sats[8], + &frame->sats[9], + &frame->sats[10], + &frame->sats[11], + &frame->pdop, + &frame->hdop, + &frame->vdop)) + return false; + if(strcmp(type + 2, "GSA")) return false; + + return true; +} + +bool minmea_parse_gll(struct minmea_sentence_gll* frame, const char* sentence) { + // $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41$; + char type[6]; + int latitude_direction; + int longitude_direction; + + if(!minmea_scan( + sentence, + "tfdfdTc;c", + type, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->time, + &frame->status, + &frame->mode)) + return false; + if(strcmp(type + 2, "GLL")) return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gst(struct minmea_sentence_gst* frame, const char* sentence) { + // $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58 + char type[6]; + + if(!minmea_scan( + sentence, + "tTfffffff", + type, + &frame->time, + &frame->rms_deviation, + &frame->semi_major_deviation, + &frame->semi_minor_deviation, + &frame->semi_major_orientation, + &frame->latitude_error_deviation, + &frame->longitude_error_deviation, + &frame->altitude_error_deviation)) + return false; + if(strcmp(type + 2, "GST")) return false; + + return true; +} + +bool minmea_parse_gsv(struct minmea_sentence_gsv* frame, const char* sentence) { + // $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 + // $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D + // $GPGSV,4,2,11,08,51,203,30,09,45,215,28*75 + // $GPGSV,4,4,13,39,31,170,27*40 + // $GPGSV,4,4,13*7B + char type[6]; + + if(!minmea_scan( + sentence, + "tiii;iiiiiiiiiiiiiiii", + type, + &frame->total_msgs, + &frame->msg_nr, + &frame->total_sats, + &frame->sats[0].nr, + &frame->sats[0].elevation, + &frame->sats[0].azimuth, + &frame->sats[0].snr, + &frame->sats[1].nr, + &frame->sats[1].elevation, + &frame->sats[1].azimuth, + &frame->sats[1].snr, + &frame->sats[2].nr, + &frame->sats[2].elevation, + &frame->sats[2].azimuth, + &frame->sats[2].snr, + &frame->sats[3].nr, + &frame->sats[3].elevation, + &frame->sats[3].azimuth, + &frame->sats[3].snr)) { + return false; + } + if(strcmp(type + 2, "GSV")) return false; + + return true; +} + +bool minmea_parse_vtg(struct minmea_sentence_vtg* frame, const char* sentence) { + // $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48,S + // $GPVTG,156.1,T,140.9,M,0.0,N,0.0,K*41,I + // $GPVTG,096.5,T,083.5,M,0.0,N,0.0,K,D*22,L + // $GPVTG,188.36,T,,M,0.820,N,1.519,K,A*3F,333033 + char type[6]; + char c_true, c_magnetic, c_knots, c_kph, c_faa_mode; + + if(!minmea_scan( + sentence, + "t;fcfcfcfcc", + type, + &frame->true_track_degrees, + &c_true, + &frame->magnetic_track_degrees, + &c_magnetic, + &frame->speed_knots, + &c_knots, + &frame->speed_kph, + &c_kph, + &c_faa_mode)) + return false; + if(strcmp(type + 2, "VTG")) return false; + // values are only valid with the accompanying characters + if(c_true != 'T') frame->true_track_degrees.scale = 0; + if(c_magnetic != 'M') frame->magnetic_track_degrees.scale = 0; + if(c_knots != 'N') frame->speed_knots.scale = 0; + if(c_kph != 'K') frame->speed_kph.scale = 0; + frame->faa_mode = (enum minmea_faa_mode)c_faa_mode; + + return true; +} + +bool minmea_parse_zda(struct minmea_sentence_zda* frame, const char* sentence) { + // $GPZDA,201530.00,04,07,2002,00,00*60 + char type[6]; + + if(!minmea_scan( + sentence, + "tTiiiii", + type, + &frame->time, + &frame->date.day, + &frame->date.month, + &frame->date.year, + &frame->hour_offset, + &frame->minute_offset)) + return false; + if(strcmp(type + 2, "ZDA")) return false; + + // check offsets + if(abs(frame->hour_offset) > 13 || frame->minute_offset > 59 || frame->minute_offset < 0) + return false; + + return true; +} + +int minmea_getdatetime( + struct tm* tm, + const struct minmea_date* date, + const struct minmea_time* time_) { + if(date->year == -1 || time_->hours == -1) return -1; + + memset(tm, 0, sizeof(*tm)); + if(date->year < 80) { + tm->tm_year = 2000 + date->year - 1900; // 2000-2079 + } else if(date->year >= 1900) { + tm->tm_year = date->year - 1900; // 4 digit year, use directly + } else { + tm->tm_year = date->year; // 1980-1999 + } + tm->tm_mon = date->month - 1; + tm->tm_mday = date->day; + tm->tm_hour = time_->hours; + tm->tm_min = time_->minutes; + tm->tm_sec = time_->seconds; + + return 0; +} + +int minmea_gettime( + struct timespec* ts, + const struct minmea_date* date, + const struct minmea_time* time_) { + struct tm tm; + if(minmea_getdatetime(&tm, date, time_)) return -1; + + time_t timestamp = mktime(&tm); /* See README.md if your system lacks timegm(). */ + if(timestamp != (time_t)-1) { + ts->tv_sec = timestamp; + ts->tv_nsec = time_->microseconds * 1000; + return 0; + } else { + return -1; + } +} + +/* vim: set ts=4 sw=4 et: */ diff --git a/applications/main/subghz/helpers/minmea.h b/applications/main/subghz/helpers/minmea.h new file mode 100644 index 00000000000..88eec4ae985 --- /dev/null +++ b/applications/main/subghz/helpers/minmea.h @@ -0,0 +1,295 @@ +/* + * Copyright © 2014 Kosma Moczek + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#ifndef MINMEA_H +#define MINMEA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#ifdef MINMEA_INCLUDE_COMPAT +#include +#endif + +#ifndef MINMEA_MAX_SENTENCE_LENGTH +#define MINMEA_MAX_SENTENCE_LENGTH 80 +#endif + +enum minmea_sentence_id { + MINMEA_INVALID = -1, + MINMEA_UNKNOWN = 0, + MINMEA_SENTENCE_GBS, + MINMEA_SENTENCE_GGA, + MINMEA_SENTENCE_GLL, + MINMEA_SENTENCE_GSA, + MINMEA_SENTENCE_GST, + MINMEA_SENTENCE_GSV, + MINMEA_SENTENCE_RMC, + MINMEA_SENTENCE_VTG, + MINMEA_SENTENCE_ZDA, +}; + +struct minmea_float { + int_least32_t value; + int_least32_t scale; +}; + +struct minmea_date { + int day; + int month; + int year; +}; + +struct minmea_time { + int hours; + int minutes; + int seconds; + int microseconds; +}; + +struct minmea_sentence_gbs { + struct minmea_time time; + struct minmea_float err_latitude; + struct minmea_float err_longitude; + struct minmea_float err_altitude; + int svid; + struct minmea_float prob; + struct minmea_float bias; + struct minmea_float stddev; +}; + +struct minmea_sentence_rmc { + struct minmea_time time; + bool valid; + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_float speed; + struct minmea_float course; + struct minmea_date date; + struct minmea_float variation; +}; + +struct minmea_sentence_gga { + struct minmea_time time; + struct minmea_float latitude; + struct minmea_float longitude; + int fix_quality; + int satellites_tracked; + struct minmea_float hdop; + struct minmea_float altitude; + char altitude_units; + struct minmea_float height; + char height_units; + struct minmea_float dgps_age; +}; + +enum minmea_gll_status { + MINMEA_GLL_STATUS_DATA_VALID = 'A', + MINMEA_GLL_STATUS_DATA_NOT_VALID = 'V', +}; + +// FAA mode added to some fields in NMEA 2.3. +enum minmea_faa_mode { + MINMEA_FAA_MODE_AUTONOMOUS = 'A', + MINMEA_FAA_MODE_DIFFERENTIAL = 'D', + MINMEA_FAA_MODE_ESTIMATED = 'E', + MINMEA_FAA_MODE_MANUAL = 'M', + MINMEA_FAA_MODE_SIMULATED = 'S', + MINMEA_FAA_MODE_NOT_VALID = 'N', + MINMEA_FAA_MODE_PRECISE = 'P', +}; + +struct minmea_sentence_gll { + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_time time; + char status; + char mode; +}; + +struct minmea_sentence_gst { + struct minmea_time time; + struct minmea_float rms_deviation; + struct minmea_float semi_major_deviation; + struct minmea_float semi_minor_deviation; + struct minmea_float semi_major_orientation; + struct minmea_float latitude_error_deviation; + struct minmea_float longitude_error_deviation; + struct minmea_float altitude_error_deviation; +}; + +enum minmea_gsa_mode { + MINMEA_GPGSA_MODE_AUTO = 'A', + MINMEA_GPGSA_MODE_FORCED = 'M', +}; + +enum minmea_gsa_fix_type { + MINMEA_GPGSA_FIX_NONE = 1, + MINMEA_GPGSA_FIX_2D = 2, + MINMEA_GPGSA_FIX_3D = 3, +}; + +struct minmea_sentence_gsa { + char mode; + int fix_type; + int sats[12]; + struct minmea_float pdop; + struct minmea_float hdop; + struct minmea_float vdop; +}; + +struct minmea_sat_info { + int nr; + int elevation; + int azimuth; + int snr; +}; + +struct minmea_sentence_gsv { + int total_msgs; + int msg_nr; + int total_sats; + struct minmea_sat_info sats[4]; +}; + +struct minmea_sentence_vtg { + struct minmea_float true_track_degrees; + struct minmea_float magnetic_track_degrees; + struct minmea_float speed_knots; + struct minmea_float speed_kph; + enum minmea_faa_mode faa_mode; +}; + +struct minmea_sentence_zda { + struct minmea_time time; + struct minmea_date date; + int hour_offset; + int minute_offset; +}; + +/** + * Calculate raw sentence checksum. Does not check sentence integrity. + */ +uint8_t minmea_checksum(const char* sentence); + +/** + * Check sentence validity and checksum. Returns true for valid sentences. + */ +bool minmea_check(const char* sentence, bool strict); + +/** + * Determine talker identifier. + */ +bool minmea_talker_id(char talker[3], const char* sentence); + +/** + * Determine sentence identifier. + */ +enum minmea_sentence_id minmea_sentence_id(const char* sentence, bool strict); + +/** + * Scanf-like processor for NMEA sentences. Supports the following formats: + * c - single character (char *) + * d - direction, returned as 1/-1, default 0 (int *) + * f - fractional, returned as value + scale (struct minmea_float *) + * i - decimal, default zero (int *) + * s - string (char *) + * t - talker identifier and type (char *) + * D - date (struct minmea_date *) + * T - time stamp (struct minmea_time *) + * _ - ignore this field + * ; - following fields are optional + * Returns true on success. See library source code for details. + */ +bool minmea_scan(const char* sentence, const char* format, ...); + +/* + * Parse a specific type of sentence. Return true on success. + */ +bool minmea_parse_gbs(struct minmea_sentence_gbs* frame, const char* sentence); +bool minmea_parse_rmc(struct minmea_sentence_rmc* frame, const char* sentence); +bool minmea_parse_gga(struct minmea_sentence_gga* frame, const char* sentence); +bool minmea_parse_gsa(struct minmea_sentence_gsa* frame, const char* sentence); +bool minmea_parse_gll(struct minmea_sentence_gll* frame, const char* sentence); +bool minmea_parse_gst(struct minmea_sentence_gst* frame, const char* sentence); +bool minmea_parse_gsv(struct minmea_sentence_gsv* frame, const char* sentence); +bool minmea_parse_vtg(struct minmea_sentence_vtg* frame, const char* sentence); +bool minmea_parse_zda(struct minmea_sentence_zda* frame, const char* sentence); + +/** + * Convert GPS UTC date/time representation to a UNIX calendar time. + */ +int minmea_getdatetime( + struct tm* tm, + const struct minmea_date* date, + const struct minmea_time* time_); + +/** + * Convert GPS UTC date/time representation to a UNIX timestamp. + */ +int minmea_gettime( + struct timespec* ts, + const struct minmea_date* date, + const struct minmea_time* time_); + +/** + * Rescale a fixed-point value to a different scale. Rounds towards zero. + */ +static inline int_least32_t minmea_rescale(const struct minmea_float* f, int_least32_t new_scale) { + if(f->scale == 0) return 0; + if(f->scale == new_scale) return f->value; + if(f->scale > new_scale) + return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale / new_scale / 2) / + (f->scale / new_scale); + else + return f->value * (new_scale / f->scale); +} + +/** + * Convert a fixed-point value to a floating-point value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tofloat(const struct minmea_float* f) { + if(f->scale == 0) return NAN; + return (float)f->value / (float)f->scale; +} + +/** + * Convert a raw coordinate to a floating point DD.DDD... value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tocoord(const struct minmea_float* f) { + if(f->scale == 0) return NAN; + if(f->scale > (INT_LEAST32_MAX / 100)) return NAN; + if(f->scale < (INT_LEAST32_MIN / 100)) return NAN; + int_least32_t degrees = f->value / (f->scale * 100); + int_least32_t minutes = f->value % (f->scale * 100); + return (float)degrees + (float)minutes / (60 * f->scale); +} + +/** + * Check whether a character belongs to the set of characters allowed in a + * sentence data field. + */ +static inline bool minmea_isfield(char c) { + return isprint((unsigned char)c) && c != ',' && c != '*'; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MINMEA_H */ + +/* vim: set ts=4 sw=4 et: */ diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index c3ca1134a0b..3a603dce9af 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -76,6 +76,8 @@ typedef enum { SubGhzCustomEventSceneReceiverInfoTxStart, SubGhzCustomEventSceneReceiverInfoTxStop, SubGhzCustomEventSceneReceiverInfoSave, + SubGhzCustomEventSceneReceiverInfoSats, + SubGhzCustomEventSceneReceiverInfoBack, SubGhzCustomEventSceneSaveName, SubGhzCustomEventSceneSaveSuccess, SubGhzCustomEventSceneShowErrorBack, @@ -84,6 +86,8 @@ typedef enum { SubGhzCustomEventSceneShowOnlyRX, SubGhzCustomEventSceneAnalyzerLock, SubGhzCustomEventSceneAnalyzerUnlock, + SubGhzCustomEventSceneSettingRepeater, + SubGhzCustomEventSceneSettingRemoveDuplicates, SubGhzCustomEventSceneSettingLock, SubGhzCustomEventSceneSettingResetToDefault, @@ -123,5 +127,8 @@ typedef enum { SubGhzCustomEventViewFreqAnalOkShort, SubGhzCustomEventViewFreqAnalOkLong, + SubGhzCustomEventViewRepeaterStart, + SubGhzCustomEventViewRepeaterStop, + SubGhzCustomEventByteInputDone, } SubGhzCustomEvent; diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 6551e0425bb..c198f2fe293 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -81,7 +81,6 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { uint32_t frequency = 0; float rssi_temp = 0; uint32_t frequency_temp = 0; - CC1101Status status; FuriHalSpiBusHandle* spi_bus = instance->spi_bus; const SubGhzDevice* radio_device = instance->radio_device; @@ -143,9 +142,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { frequency = cc1101_set_frequency(spi_bus, current_frequency); cc1101_calibrate(spi_bus); - do { - status = cc1101_get_status(spi_bus); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(spi_bus); furi_hal_spi_release(spi_bus); @@ -191,9 +189,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { frequency = cc1101_set_frequency(spi_bus, i); cc1101_calibrate(spi_bus); - do { - status = cc1101_get_status(spi_bus); - } while(status.STATE != CC1101StateIDLE); + + furi_check(cc1101_wait_status_state(spi_bus, CC1101StateIDLE, 10000)); cc1101_switch_to_rx(spi_bus); furi_hal_spi_release(spi_bus); diff --git a/applications/main/subghz/helpers/subghz_gps.c b/applications/main/subghz/helpers/subghz_gps.c new file mode 100644 index 00000000000..8b468efbd60 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_gps.c @@ -0,0 +1,180 @@ +#include "subghz_gps.h" + +static void subghz_gps_uart_parse_nmea(SubGhzGPS* subghz_gps, char* line) { + switch(minmea_sentence_id(line, false)) { + case MINMEA_SENTENCE_RMC: { + struct minmea_sentence_rmc frame; + if(minmea_parse_rmc(&frame, line)) { + subghz_gps->latitude = minmea_tocoord(&frame.latitude); + subghz_gps->longitude = minmea_tocoord(&frame.longitude); + subghz_gps->fix_second = frame.time.seconds; + subghz_gps->fix_minute = frame.time.minutes; + subghz_gps->fix_hour = frame.time.hours; + } + } break; + + case MINMEA_SENTENCE_GGA: { + struct minmea_sentence_gga frame; + if(minmea_parse_gga(&frame, line)) { + subghz_gps->latitude = minmea_tocoord(&frame.latitude); + subghz_gps->longitude = minmea_tocoord(&frame.longitude); + subghz_gps->satellites = frame.satellites_tracked; + subghz_gps->fix_second = frame.time.seconds; + subghz_gps->fix_minute = frame.time.minutes; + subghz_gps->fix_hour = frame.time.hours; + } + } break; + + case MINMEA_SENTENCE_GLL: { + struct minmea_sentence_gll frame; + if(minmea_parse_gll(&frame, line)) { + subghz_gps->latitude = minmea_tocoord(&frame.latitude); + subghz_gps->longitude = minmea_tocoord(&frame.longitude); + subghz_gps->fix_second = frame.time.seconds; + subghz_gps->fix_minute = frame.time.minutes; + subghz_gps->fix_hour = frame.time.hours; + } + } break; + + default: + break; + } +} + +static void subghz_gps_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { + SubGhzGPS* subghz_gps = (SubGhzGPS*)context; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(subghz_gps->rx_stream, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(subghz_gps->thread), WorkerEvtRxDone); + } +} + +static int32_t subghz_gps_uart_worker(void* context) { + SubGhzGPS* subghz_gps = (SubGhzGPS*)context; + + size_t rx_offset = 0; + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEvtStop) { + break; + } + + if(events & WorkerEvtRxDone) { + size_t len = 0; + do { + len = furi_stream_buffer_receive( + subghz_gps->rx_stream, + subghz_gps->rx_buf + rx_offset, + RX_BUF_SIZE - rx_offset, + 0); + + if(len > 0) { + rx_offset += len; + subghz_gps->rx_buf[rx_offset] = '\0'; + + char* current_line = (char*)subghz_gps->rx_buf; + while(true) { + while(*current_line == '\0' && + current_line < (char*)subghz_gps->rx_buf + rx_offset) { + current_line++; + } + + char* next_line = strchr(current_line, '\n'); + if(next_line) { + *next_line = '\0'; + subghz_gps_uart_parse_nmea(subghz_gps, current_line); + current_line = next_line + 1; + } else { + if(current_line > (char*)subghz_gps->rx_buf) { + rx_offset = 0; + while(*current_line) { + subghz_gps->rx_buf[rx_offset++] = *(current_line++); + } + } + break; + } + } + } + } while(len > 0); + } + } + + return 0; +} + +SubGhzGPS* subghz_gps_init() { + SubGhzGPS* subghz_gps = malloc(sizeof(SubGhzGPS)); + + subghz_gps->latitude = NAN; + subghz_gps->longitude = NAN; + subghz_gps->satellites = 0; + subghz_gps->fix_hour = 0; + subghz_gps->fix_minute = 0; + subghz_gps->fix_second = 0; + + subghz_gps->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); + + subghz_gps->thread = furi_thread_alloc(); + furi_thread_set_name(subghz_gps->thread, "SubGhzGPSWorker"); + furi_thread_set_stack_size(subghz_gps->thread, 1024); + furi_thread_set_context(subghz_gps->thread, subghz_gps); + furi_thread_set_callback(subghz_gps->thread, subghz_gps_uart_worker); + + if(UART_CH == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH, 9600); + } + + furi_hal_uart_set_irq_cb(UART_CH, subghz_gps_uart_on_irq_cb, subghz_gps); + furi_hal_uart_set_br(UART_CH, 9600); + + return subghz_gps; +} + +void subghz_gps_deinit(SubGhzGPS* subghz_gps) { + furi_assert(subghz_gps); + furi_thread_free(subghz_gps->thread); + + free(subghz_gps); + + furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); + if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH); + } else { + furi_hal_console_enable(); + } +} + +void subghz_gps_start(SubGhzGPS* subghz_gps) { + furi_thread_start(subghz_gps->thread); +} + +void subghz_gps_stop(SubGhzGPS* subghz_gps) { + furi_thread_flags_set(furi_thread_get_id(subghz_gps->thread), WorkerEvtStop); + furi_thread_join(subghz_gps->thread); +} + +double subghz_gps_deg2rad(double deg) { + return (deg * (double)M_PI / 180); +} + +double subghz_gps_calc_distance(double lat1d, double lon1d, double lat2d, double lon2d) { + double lat1r, lon1r, lat2r, lon2r, u, v; + lat1r = subghz_gps_deg2rad(lat1d); + lon1r = subghz_gps_deg2rad(lon1d); + lat2r = subghz_gps_deg2rad(lat2d); + lon2r = subghz_gps_deg2rad(lon2d); + u = sin((lat2r - lat1r) / 2); + v = sin((lon2r - lon1r) / 2); + return 2 * 6371 * asin(sqrt(u * u + cos((double)lat1r) * cos((double)lat2r) * v * v)); +} + +double subghz_gps_calc_angle(double lat1, double lon1, double lat2, double lon2) { + return atan2(lat1 - lat2, lon1 - lon2) * 180 / (double)M_PI; +} \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_gps.h b/applications/main/subghz/helpers/subghz_gps.h new file mode 100644 index 00000000000..8d3c207bdf3 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_gps.h @@ -0,0 +1,92 @@ +#include +#include +#include "minmea.h" + +#define UART_CH \ + (CFW_SETTINGS()->uart_nmea_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) + +#define RX_BUF_SIZE 1024 + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), +} WorkerEvtFlags; + +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) + +typedef struct { + FuriThread* thread; + FuriStreamBuffer* rx_stream; + uint8_t rx_buf[RX_BUF_SIZE]; + + FuriTimer* timer; + + float latitude; + float longitude; + int satellites; + uint8_t fix_second; + uint8_t fix_minute; + uint8_t fix_hour; +} SubGhzGPS; + +/** + * Initialize SubGhzGPS object + * + * @return SubGhzGPS* SubGhzGPS object +*/ +SubGhzGPS* subghz_gps_init(); + +/** + * Deinitialize SubGhzGPS object + * + * @param subghz_gps SubGhzGPS object + * @return void +*/ +void subghz_gps_deinit(SubGhzGPS* subghz_gps); + +/** + * Start GPS thread + * + * @param subghz_gps SubGhzGPS object + * @return void +*/ +void subghz_gps_start(SubGhzGPS* subghz_gps); + +/** + * Stop GPS thread + * + * @param subghz_gps SubGhzGPS object + * @return void +*/ +void subghz_gps_stop(SubGhzGPS* subghz_gps); + +/** + * Convert degree to radian + * + * @param deg Degree + * @return double Radian + */ +double subghz_gps_deg2rad(double deg); + +/** + * Calculate distance between two coordinates + * + * @param lat1d Latitude 1 + * @param lon1d Longitude 1 + * @param lat2d Latitude 2 + * @param lon2d Longitude 2 + * @return double Distance in km +*/ +double subghz_gps_calc_distance(double lat1d, double lon1d, double lat2d, double lon2d); + +/** + * Calculate angle between two coordinates + * + * @param lat1 Latitude 1 + * @param lon1 Longitude 1 + * @param lat2 Latitude 2 + * @param lon2 Longitude 2 + * @return double Angle in degree +*/ +double subghz_gps_calc_angle(double lat1, double lon1, double lat2, double lon2); \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c index 49b40ca3bfb..a8b9d7b802e 100644 --- a/applications/main/subghz/helpers/subghz_txrx.c +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -97,12 +97,16 @@ void subghz_txrx_set_preset( SubGhzTxRx* instance, const char* preset_name, uint32_t frequency, + float latitude, + float longitude, uint8_t* preset_data, size_t preset_data_size) { furi_assert(instance); furi_string_set(instance->preset->name, preset_name); SubGhzRadioPreset* preset = instance->preset; preset->frequency = frequency; + preset->latitude = latitude; + preset->longitude = longitude; preset->data = preset_data; preset->data_size = preset_data_size; } @@ -154,6 +158,20 @@ void subghz_txrx_get_frequency_and_modulation( } } +void subghz_txrx_get_latitude_and_longitude( + SubGhzTxRx* instance, + FuriString* latitude, + FuriString* longitude) { + furi_assert(instance); + SubGhzRadioPreset* preset = instance->preset; + if(latitude != NULL) { + furi_string_printf(latitude, "%f", (double)preset->latitude); + } + if(longitude != NULL) { + furi_string_printf(longitude, "%f", (double)preset->longitude); + } +} + static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) { furi_assert(instance); subghz_devices_reset(instance->radio_device); @@ -287,7 +305,7 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* ret = SubGhzTxRxStartTxStateErrorParserOthers; } if(ret != SubGhzTxRxStartTxStateOk) { - subghz_transmitter_free(instance->transmitter); + if(instance->transmitter) subghz_transmitter_free(instance->transmitter); if(instance->txrx_state != SubGhzTxRxStateIDLE) { subghz_txrx_idle(instance); } @@ -504,21 +522,11 @@ void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState stat instance->speaker_state = state; } -void subghz_txrx_repeater_set_state(SubGhzRepeater* instance, SubGhzRepeater state) { - furi_assert(instance); - *instance = state; -} - SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) { furi_assert(instance); return instance->speaker_state; } -SubGhzRepeater subghz_txrx_repeater_get_state(SubGhzRepeater* instance) { - furi_assert(instance); - return *instance; -} - bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) { furi_assert(instance); furi_assert(name_protocol); @@ -690,7 +698,7 @@ void subghz_txrx_set_default_preset(SubGhzTxRx* instance, uint32_t frequency) { if(frequency == 0) { frequency = subghz_setting_get_default_frequency(subghz_txrx_get_setting(instance)); } - subghz_txrx_set_preset(instance, default_modulation, frequency, NULL, 0); + subghz_txrx_set_preset(instance, default_modulation, frequency, 0, 0, NULL, 0); } const char* @@ -705,6 +713,8 @@ const char* instance, preset_name, frequency, + 0, + 0, subghz_setting_get_preset_data(setting, index), subghz_setting_get_preset_data_size(setting, index)); diff --git a/applications/main/subghz/helpers/subghz_txrx.h b/applications/main/subghz/helpers/subghz_txrx.h index a58641a17fa..fda6843787c 100644 --- a/applications/main/subghz/helpers/subghz_txrx.h +++ b/applications/main/subghz/helpers/subghz_txrx.h @@ -47,6 +47,8 @@ bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance); * @param instance Pointer to a SubGhzTxRx * @param preset_name Name of preset * @param frequency Frequency in Hz + * @param latitude Latitude in float + * @param longitude Longitude in float * @param preset_data Data of preset * @param preset_data_size Size of preset data */ @@ -54,6 +56,8 @@ void subghz_txrx_set_preset( SubGhzTxRx* instance, const char* preset_name, uint32_t frequency, + float latitude, + float longitude, uint8_t* preset_data, size_t preset_data_size); @@ -87,6 +91,18 @@ void subghz_txrx_get_frequency_and_modulation( FuriString* modulation, bool long_name); +/** + * Get string latitude and longitude + * + * @param instance Pointer to a SubGhzTxRx + * @param latitude Pointer to a string latitude + * @param longitude Pointer to a string longitude +*/ +void subghz_txrx_get_latitude_and_longitude( + SubGhzTxRx* instance, + FuriString* latitude, + FuriString* longitude); + /** * Start TX CC1101 * @@ -198,22 +214,6 @@ void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState stat */ SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance); -/** - * Set state repeater - * - * @param instance Pointer to a SubGhzTxRx - * @param state State speaker - */ -void subghz_txrx_repeater_set_state(SubGhzRepeater* instance, SubGhzRepeater state); - -/** - * Get state repeater - * - * @param instance Pointer to a SubGhzRepeater - * @return SubGhzRepeater - */ -SubGhzRepeater subghz_txrx_repeater_get_state(SubGhzRepeater* instance); - /** * load decoder by name protocol * diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c index 040aff46c07..680935fbb7a 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -25,7 +25,7 @@ bool subghz_txrx_gen_data_protocol( bool res = false; - subghz_txrx_set_preset(instance, preset_name, frequency, NULL, 0); + subghz_txrx_set_preset(instance, preset_name, frequency, 0, 0, NULL, 0); instance->decoder_result = subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_name); @@ -98,7 +98,7 @@ bool subghz_txrx_gen_keeloq_protocol( //TODO lead to a general appearance instance->transmitter = subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_txrx_set_preset(instance, preset_name, frequency, NULL, 0); + subghz_txrx_set_preset(instance, preset_name, frequency, 0, 0, NULL, 0); if(instance->transmitter && subghz_protocol_keeloq_create_data( @@ -131,7 +131,7 @@ bool subghz_txrx_gen_keeloq_bft_protocol( txrx->transmitter = subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0); + subghz_txrx_set_preset(txrx, preset_name, frequency, 0, 0, NULL, 0); if(txrx->transmitter && subghz_protocol_keeloq_bft_create_data( subghz_transmitter_get_protocol_instance(txrx->transmitter), @@ -175,7 +175,7 @@ bool subghz_txrx_gen_nice_flor_s_protocol( txrx->transmitter = subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME); - subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0); + subghz_txrx_set_preset(txrx, preset_name, frequency, 0, 0, NULL, 0); if(txrx->transmitter && subghz_protocol_nice_flor_s_create_data( subghz_transmitter_get_protocol_instance(txrx->transmitter), @@ -208,7 +208,7 @@ bool subghz_txrx_gen_faac_slh_protocol( txrx->transmitter = subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_FAAC_SLH_NAME); - subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0); + subghz_txrx_set_preset(txrx, preset_name, frequency, 0, 0, NULL, 0); if(txrx->transmitter && subghz_protocol_faac_slh_create_data( subghz_transmitter_get_protocol_instance(txrx->transmitter), @@ -251,7 +251,7 @@ bool subghz_txrx_gen_alutech_at_4n_protocol( txrx->transmitter = subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME); - subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0); + subghz_txrx_set_preset(txrx, preset_name, frequency, 0, 0, NULL, 0); if(txrx->transmitter && subghz_protocol_alutech_at_4n_create_data( subghz_transmitter_get_protocol_instance(txrx->transmitter), @@ -280,7 +280,7 @@ bool subghz_txrx_gen_came_atomo_protocol( txrx->transmitter = subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_CAME_ATOMO_NAME); - subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0); + subghz_txrx_set_preset(txrx, preset_name, frequency, 0, 0, NULL, 0); if(txrx->transmitter && subghz_protocol_came_atomo_create_data( subghz_transmitter_get_protocol_instance(txrx->transmitter), @@ -309,7 +309,7 @@ bool subghz_txrx_gen_somfy_telis_protocol( txrx->transmitter = subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME); - subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0); + subghz_txrx_set_preset(txrx, preset_name, frequency, 0, 0, NULL, 0); if(txrx->transmitter && subghz_protocol_somfy_telis_create_data( subghz_transmitter_get_protocol_instance(txrx->transmitter), @@ -338,7 +338,7 @@ bool subghz_txrx_gen_secplus_v2_protocol( bool ret = false; instance->transmitter = subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); - subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0); + subghz_txrx_set_preset(instance, name_preset, frequency, 0, 0, NULL, 0); if(instance->transmitter) { subghz_protocol_secplus_v2_create_data( subghz_transmitter_get_protocol_instance(instance->transmitter), diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 391a3759b92..77e6496d892 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -53,6 +53,7 @@ typedef enum { SubGhzRxKeyStateExit, SubGhzRxKeyStateRAWLoad, SubGhzRxKeyStateRAWSave, + SubGhzRxKeyStateTX, } SubGhzRxKeyState; /** SubGhzLoadKeyState state */ @@ -105,8 +106,8 @@ typedef enum { /** SubGhz Repeater */ typedef enum { - SubGhzRepeaterOff, - SubGhzRepeaterOn, - SubGhzRepeaterOnLong, - SubGhzRepeaterOnShort, -} SubGhzRepeater; + SubGhzRepeaterStateOff, + SubGhzRepeaterStateOn, + SubGhzRepeaterStateOnLong, + SubGhzRepeaterStateOnShort, +} SubGhzRepeaterState; diff --git a/applications/main/subghz/resources/subghz/assets/README.md b/applications/main/subghz/resources/subghz/assets/README.md index 8d8fabd765d..a09fcca5aef 100644 --- a/applications/main/subghz/resources/subghz/assets/README.md +++ b/applications/main/subghz/resources/subghz/assets/README.md @@ -8,7 +8,8 @@ Officially supported frequencies: 300-348 MHz, 387-464 MHz, and 779-928 MHz (fro Unofficially supported frequencies: 281-361 MHz, 378-481 MHz, and 749-962 MHz (from [YARD Stick One](https://greatscottgadgets.com/yardstickone/) CC1111 docs) Official & Unleashed currently do not allow anything outside of the officially supported CC1101 specs. -RogueMaster allows unofficially supported frequencies with the extend_range.txt file +RogueMaster allows unofficially supported frequencies with the extend_range.txt file and easily toggleable in the CFW Settings app. +RogueMaster is the only firmware that will alert you with an indicator icon if you are transmitting with an out of region band. **NOTE: Going outside the supported frequencies may DAMAGE YOUR FLIPPER AMP.
Please understand what you're doing if trying to break out of official frequencies.** diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index 84bbdf8a1fe..1f4f94e44be 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -22,5 +22,6 @@ ADD_SCENE(subghz, more_raw, MoreRAW) ADD_SCENE(subghz, decode_raw, DecodeRAW) ADD_SCENE(subghz, delete_raw, DeleteRAW) ADD_SCENE(subghz, need_saving, NeedSaving) -ADD_SCENE(subghz, need_saving_RX, NeedSavingRX) -ADD_SCENE(subghz, rpc, Rpc) \ No newline at end of file +ADD_SCENE(subghz, rpc, Rpc) +ADD_SCENE(subghz, show_gps, ShowGps) +ADD_SCENE(subghz, saved_show_gps, SavedShowGps) \ No newline at end of file diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 21fca935441..a14e203f4d0 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -6,7 +6,11 @@ static void subghz_scene_receiver_update_statusbar(void* context) { SubGhz* subghz = context; FuriString* history_stat_str = furi_string_alloc(); - if(!subghz_history_get_text_space_left(subghz->history, history_stat_str)) { + if(!subghz_history_get_text_space_left( + subghz->history, + history_stat_str, + subghz->gps->satellites, + subghz->last_settings->delete_old_signals)) { FuriString* frequency_str = furi_string_alloc(); FuriString* modulation_str = furi_string_alloc(); @@ -20,7 +24,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) { furi_string_get_cstr(history_stat_str), subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, - (subghz->repeater != SubGhzRepeaterOff)); + subghz->repeater); furi_string_free(frequency_str); furi_string_free(modulation_str); @@ -32,7 +36,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) { "", subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, - (subghz->repeater != SubGhzRepeaterOff)); + subghz->repeater); } furi_string_free(history_stat_str); } @@ -53,6 +57,22 @@ static void subghz_scene_add_to_history_callback( FuriString* item_time = furi_string_alloc(); uint16_t idx = subghz_history_get_item(subghz->history); SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + preset.latitude = subghz->gps->latitude; + preset.longitude = subghz->gps->longitude; + + if(subghz->last_settings->delete_old_signals && subghz_history_full(subghz->history)) { + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + + while(idx > 0 && subghz_history_full(subghz->history)) { + subghz_history_delete_item(subghz->history, 0); + subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); + idx--; + } + + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + subghz_scene_receiver_update_statusbar(subghz); + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + } if(subghz_history_add_to_history(subghz->history, decoder_base, &preset)) { furi_string_reset(item_name); @@ -60,13 +80,34 @@ static void subghz_scene_add_to_history_callback( subghz->state_notifications = SubGhzNotificationStateRxDone; + if(subghz->remove_duplicates) { + // Look in history for signal hash + uint32_t hash_data = subghz_protocol_decoder_base_get_hash_data_long(decoder_base); + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + for(uint16_t i = idx; i > 0; i--) { + i--; // Iterating in reverse with off by one + if(subghz_history_get_hash_data(subghz->history, i) == hash_data && + subghz_history_get_protocol(subghz->history, i) == decoder_base->protocol) { + // Remove previous instance and update menu index + subghz_history_delete_item(subghz->history, i); + subghz_view_receiver_delete_item(subghz->subghz_receiver, i); + idx--; + } + i++; + } + // Restore ui state + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + } + subghz_history_get_text_item_menu(subghz->history, item_name, idx); subghz_history_get_time_item_menu(subghz->history, item_time, idx); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, furi_string_get_cstr(item_name), furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(subghz->history, idx)); + subghz_history_get_type_protocol(subghz->history, idx), + subghz_history_get_repeats(subghz->history, idx)); subghz_scene_receiver_update_statusbar(subghz); } @@ -184,7 +225,8 @@ void subghz_scene_decode_raw_on_enter(void* context) { subghz->subghz_receiver, furi_string_get_cstr(item_name), furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(subghz->history, i)); + subghz_history_get_type_protocol(subghz->history, i), + subghz_history_get_repeats(subghz->history, i)); } subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); } diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 928b0677791..63ce2bd6d42 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -1,5 +1,4 @@ #include "../subghz_i.h" -#include #include "../helpers/subghz_custom_event.h" void subghz_scene_delete_success_popup_callback(void* context) { diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index fc353ca8503..64ab2cbf2ab 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -16,8 +16,7 @@ void subghz_scene_need_saving_on_enter(void* context) { SubGhz* subghz = context; widget_add_string_multiline_element( - subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to SubGhz Menu?"); - + subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Discard Signals?"); widget_add_string_multiline_element( subghz->widget, 64, @@ -30,7 +29,7 @@ void subghz_scene_need_saving_on_enter(void* context) { widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Stay", subghz_scene_need_saving_callback, subghz); widget_add_button_element( - subghz->widget, GuiButtonTypeLeft, "Exit", subghz_scene_need_saving_callback, subghz); + subghz->widget, GuiButtonTypeLeft, "Continue", subghz_scene_need_saving_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); } @@ -58,12 +57,18 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { } subghz_txrx_set_preset( - subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0); - - if(!scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneStart)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); - }; + subghz->txrx, "AM650", subghz->last_settings->frequency, 0, 0, NULL, 0); + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } else if(state == SubGhzRxKeyStateTX) { + subghz->repeater = SubGhzRepeaterStateOn; + subghz->last_settings->repeater_state = SubGhzRepeaterStateOn; + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { + subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + subghz->repeater_bin_raw_was_off = true; + } + scene_manager_previous_scene(subghz->scene_manager); } else { scene_manager_previous_scene(subghz->scene_manager); } diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving_RX.c b/applications/main/subghz/scenes/subghz_scene_need_saving_RX.c deleted file mode 100644 index 802e1c514e2..00000000000 --- a/applications/main/subghz/scenes/subghz_scene_need_saving_RX.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "../subghz_i.h" -#include "../helpers/subghz_custom_event.h" - -void subghz_scene_need_saving_RX_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - SubGhz* subghz = context; - - if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneStay); - } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); - } -} - -void subghz_scene_need_saving_RX_on_enter(void* context) { - SubGhz* subghz = context; - - widget_add_string_multiline_element( - subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Start Listening?"); - widget_add_string_multiline_element( - subghz->widget, - 64, - 32, - AlignCenter, - AlignCenter, - FontSecondary, - "All unsaved data\nwill be lost!"); - - widget_add_button_element( - subghz->widget, GuiButtonTypeLeft, "Stay", subghz_scene_need_saving_RX_callback, subghz); - widget_add_button_element( - subghz->widget, GuiButtonTypeRight, "Listen", subghz_scene_need_saving_RX_callback, subghz); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); -} - -bool subghz_scene_need_saving_RX_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeBack) { - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); - scene_manager_previous_scene(subghz->scene_manager); - return true; - } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneStay) { - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); - scene_manager_previous_scene(subghz->scene_manager); - return true; - } else if(event.event == SubGhzCustomEventSceneExit) { - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); - if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneReceiver)) { - //Scene exists, go back to it - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneReceiver); - } else { - //Scene not started, start it now. - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); - } - return true; - } - } - return false; -} - -void subghz_scene_need_saving_RX_on_exit(void* context) { - SubGhz* subghz = context; - widget_reset(subghz->widget); -} diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index 24bb30e0f8f..731eceeac2f 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -32,6 +32,16 @@ const char* const debug_pin_text[DEBUG_P_COUNT] = { "17(1W)", }; +#define GPS_COUNT 6 +const char* const gps_text[GPS_COUNT] = { + "OFF", + "9600", + "19200", + "38400", + "57600", + "115200", +}; + #define DEBUG_COUNTER_COUNT 13 const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { "+1", @@ -104,6 +114,7 @@ static void subghz_scene_reciever_config_set_ext_mod_power_amp_text(VariableItem // Set globally in furi hal furi_hal_subghz_set_ext_power_amp(subghz->last_settings->external_module_power_amp); + subghz_last_settings_save(subghz->last_settings); // reinit external device @@ -114,13 +125,50 @@ static void subghz_scene_reciever_config_set_ext_mod_power_amp_text(VariableItem } } -static void subghz_scene_receiver_config_set_timestamp_file_names(VariableItem* item) { +static void subghz_scene_receiver_config_set_gps(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, gps_text[index]); + + switch(index) { + case 0: + subghz->last_settings->gps_baudrate = 0; + break; + case 1: + subghz->last_settings->gps_baudrate = 9600; + break; + case 2: + subghz->last_settings->gps_baudrate = 19200; + break; + case 3: + subghz->last_settings->gps_baudrate = 38400; + break; + case 4: + subghz->last_settings->gps_baudrate = 57600; + break; + case 5: + subghz->last_settings->gps_baudrate = 115200; + break; + } + subghz_last_settings_save(subghz->last_settings); + + if(subghz->last_settings->gps_baudrate != 0) { + subghz_gps_stop(subghz->gps); + subghz_gps_set_baudrate(subghz->gps, subghz->last_settings->gps_baudrate); + subghz_gps_start(subghz->gps); + } else { + subghz_gps_stop(subghz->gps); + } +} + +static void subghz_scene_receiver_config_set_protocol_file_names(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, timestamp_names_text[index]); - subghz->last_settings->timestamp_file_names = (index == 1); + subghz->last_settings->protocol_file_names = (index == 1); subghz_last_settings_save(subghz->last_settings); } @@ -156,13 +204,26 @@ void subghz_scene_radio_settings_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, ext_mod_power_amp_text[value_index]); + item = variable_item_list_add( + variable_item_list, + "GPS Baudrate", + GPS_COUNT, + subghz_scene_receiver_config_set_gps, + subghz); + value_index = value_index_uint32( + subghz->last_settings->gps_baudrate, + (const uint32_t[]){0, 9600, 19200, 38400, 57600, 115200}, + GPS_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, gps_text[value_index]); + item = variable_item_list_add( variable_item_list, "Protocol Names", TIMESTAMP_NAMES_COUNT, - subghz_scene_receiver_config_set_timestamp_file_names, + subghz_scene_receiver_config_set_protocol_file_names, subghz); - value_index = subghz->last_settings->timestamp_file_names; + value_index = subghz->last_settings->protocol_file_names; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, timestamp_names_text[value_index]); @@ -182,7 +243,7 @@ void subghz_scene_radio_settings_on_enter(void* context) { variable_item_set_current_value_text(item, debug_counter_text[value_index]); item = variable_item_list_add( - subghz->variable_item_list, + variable_item_list, "Debug Pin", DEBUG_P_COUNT, subghz_scene_receiver_config_set_debug_pin, @@ -190,6 +251,8 @@ void subghz_scene_radio_settings_on_enter(void* context) { value_index = subghz_txrx_get_debug_pin_state(subghz->txrx); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, debug_pin_text[value_index]); + variable_item_set_locked( + item, !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug), "Enable\nDebug!"); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 5a61b55159c..10eb0a48192 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -8,15 +8,6 @@ #define RAW_FILE_NAME "R_" #define TAG "SubGhzSceneReadRAW" -const NotificationSequence subghz_sequence_raw_beep = { - &message_vibro_on, - &message_note_c6, - &message_delay_50, - &message_sound_off, - &message_vibro_off, - NULL, -}; - bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { bool ret = false; //set the path to read the file @@ -134,6 +125,12 @@ void subghz_scene_read_raw_on_enter(void* context) { furi_string_free(file_name); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); + + // Start sending immediately with favorites + if(subghz->fav_timeout) { + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewReadRAWSendStart); + } } bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { @@ -161,7 +158,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { //Restore default setting - if(subghz->raw_send_only || subghz->ListenAfterTX) { + if(subghz->raw_send_only) { subghz_txrx_set_default_preset(subghz->txrx, 0); } else { subghz_txrx_set_default_preset(subghz->txrx, subghz->last_settings->frequency); @@ -237,7 +234,6 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { "", subghz_threshold_rssi_get(subghz->threshold_rssi)); } else { - notification_message(subghz->notifications, &subghz_sequence_raw_beep); if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { dolphin_deed(DolphinDeedSubGhzSend); @@ -261,32 +257,14 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz->state_notifications = SubGhzNotificationStateIDLE; subghz_txrx_stop(subghz->txrx); subghz_read_raw_stop_send(subghz->subghz_read_raw); - consumed = true; - - //Go back to the Scan/Repeater if the Listen After TX flag is on. - if(subghz->ListenAfterTX) { /* && !subghz->raw_send_only) { - ^^ This is the bug fix that caused all these changes, didnt like it! */ - //We are switching to Receive, so change this now. - subghz->raw_send_only = false; - - //needed save? - if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) || - (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSavingRX); - } else { - if(scene_manager_has_previous_scene( - subghz->scene_manager, SubGhzSceneReceiver)) { - //Scene exists, go back to it - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneReceiver); - } else { - //Scene not started, start it now. - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); - } - } - }; + // Exit / stop with favorites + if(subghz->fav_timeout) { + while(scene_manager_handle_back_event(subghz->scene_manager)) + ; + view_dispatcher_stop(subghz->view_dispatcher); + } + consumed = true; break; case SubGhzCustomEventViewReadRAWIDLE: diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index aca1101aff3..dd9016ee9d2 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,18 +1,10 @@ #include "../subghz_i.h" #include #include +#include #define TAG "SubGhzSceneReceiver" -const NotificationSequence subghz_sequence_beep = { - &message_vibro_on, - &message_note_c6, - &message_delay_50, - &message_sound_off, - &message_vibro_off, - NULL, -}; - const NotificationSequence subghz_sequence_rx = { &message_green_255, @@ -28,17 +20,6 @@ const NotificationSequence subghz_sequence_rx = { NULL, }; -const NotificationSequence subghz_sequence_repeat = { - &message_red_255, - //&message_display_backlight_on, - &message_vibro_on, - &message_note_c6, - &message_delay_50, - &message_sound_off, - &message_vibro_off, - NULL, -}; - const NotificationSequence subghz_sequence_rx_locked = { &message_green_255, @@ -56,10 +37,24 @@ const NotificationSequence subghz_sequence_rx_locked = { NULL, }; +const NotificationSequence subghz_sequence_tx_beep = { + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + &message_delay_50, + NULL, +}; + static void subghz_scene_receiver_update_statusbar(void* context) { SubGhz* subghz = context; FuriString* history_stat_str = furi_string_alloc(); - if(!subghz_history_get_text_space_left(subghz->history, history_stat_str)) { + if(!subghz_history_get_text_space_left( + subghz->history, + history_stat_str, + subghz->gps->satellites, + subghz->last_settings->delete_old_signals)) { FuriString* frequency_str = furi_string_alloc(); FuriString* modulation_str = furi_string_alloc(); @@ -92,7 +87,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) { furi_string_get_cstr(history_stat_str), subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, - (subghz->repeater != SubGhzRepeaterOff)); + subghz->repeater); furi_string_free(frequency_str); furi_string_free(modulation_str); @@ -104,8 +99,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) { "", subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, - (subghz->repeater != SubGhzRepeaterOff)); - subghz->state_notifications = SubGhzNotificationStateIDLE; + subghz->repeater); } furi_string_free(history_stat_str); @@ -119,6 +113,11 @@ void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, event); } +void repeater_stop_callback(void* context) { + SubGhz* subghz = context; + scene_manager_handle_custom_event(subghz->scene_manager, SubGhzCustomEventViewRepeaterStop); +} + static void subghz_scene_add_to_history_callback( SubGhzReceiver* receiver, SubGhzProtocolDecoderBase* decoder_base, @@ -127,74 +126,69 @@ static void subghz_scene_add_to_history_callback( SubGhz* subghz = context; // The check can be moved to /lib/subghz/receiver.c, but may result in false positives - if((decoder_base->protocol->flag & subghz->ignore_filter) == 0) { + if((decoder_base->protocol->filter & subghz->ignore_filter) == 0) { SubGhzHistory* history = subghz->history; FuriString* item_name = furi_string_alloc(); FuriString* item_time = furi_string_alloc(); uint16_t idx = subghz_history_get_item(history); - //Need the preset for the repeater, move her up here! SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + preset.latitude = subghz->gps->latitude; + preset.longitude = subghz->gps->longitude; - /* This is where the repeater logic ended up going! - Instead of adding to the list, Ill beep and send the key on! - Bit of a Hack, but thats the flipper code for you! - */ + if(subghz->last_settings->delete_old_signals && subghz_history_full(subghz->history)) { + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); - if(subghz->repeater != SubGhzRepeaterOff) { - //Start TX Counter, at the moment I just send the key as fixed length. - //Next version will look at Te time in BinRAW (since its locked to that now!) - //and transmit for the same time as receive was. It works well for my purposes as is though! + while(idx > 0 && subghz_history_full(subghz->history)) { + subghz_history_delete_item(subghz->history, 0); + subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); + idx--; + } - //Stop the Radio from Receiving. - subghz_txrx_hopper_pause(subghz->txrx); - subghz_receiver_reset(receiver); + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + if(idx == 0) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + } + subghz_scene_receiver_update_statusbar(subghz); + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + } - //Get the data to send. - subghz_protocol_decoder_base_serialize(decoder_base, subghz->repeater_tx, &preset); + if(subghz_history_add_to_history(history, decoder_base, &preset)) { + furi_string_reset(item_name); + furi_string_reset(item_time); - uint32_t tmpTe = 300; - uint32_t tmpBits = 1000; - if(!flipper_format_rewind(subghz->repeater_tx)) { - FURI_LOG_E(TAG, "Rewind error"); - return; - } - if(!flipper_format_read_uint32(subghz->repeater_tx, "Bit", (uint32_t*)&tmpBits, 1)) { - FURI_LOG_E(TAG, "Missing Bit"); - return; - } else if(!flipper_format_read_uint32( - subghz->repeater_tx, "TE", (uint32_t*)&tmpTe, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - return; + //If the repeater is on, dont add to the menu, just TX the signal. + if(subghz->repeater != SubGhzRepeaterStateOff) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewRepeaterStart); } else { - //Save our TX variables now, start TX on the next tick event so the we arent tangled with the worker. - subghz->RepeaterTXLength = tmpTe * (tmpBits + 1) / 1000; - subghz->state_notifications = SubGhzNotificationStateTx; - notification_message(subghz->notifications, &subghz_sequence_repeat); - FURI_LOG_I(TAG, "Key Received, Transmitting now."); - } - } else { - if(subghz->last_settings->delete_old_signals) { - if(subghz_history_get_last_index(subghz->history) >= 54) { - subghz->state_notifications = SubGhzNotificationStateRx; + subghz->state_notifications = SubGhzNotificationStateRxDone; + if(subghz->remove_duplicates) { + // Look in history for signal hash + uint32_t hash_data = + subghz_protocol_decoder_base_get_hash_data_long(decoder_base); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); - - subghz_history_delete_item(subghz->history, 0); - subghz_view_receiver_delete_item(subghz->subghz_receiver, 0); - subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); - - subghz_scene_receiver_update_statusbar(subghz); + for(uint16_t i = idx; i > 0; i--) { + i--; // Iterating in reverse with off by one + if(subghz_history_get_hash_data(subghz->history, i) == hash_data && + subghz_history_get_protocol(subghz->history, i) == + decoder_base->protocol) { + // Remove previous instance and update menu index + subghz_history_delete_item(subghz->history, i); + subghz_view_receiver_delete_item(subghz->subghz_receiver, i); + idx--; + } + i++; + } + // Restore ui state subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - idx--; + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + if(idx == 0) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + } } - } - if(subghz_history_add_to_history(history, decoder_base, &preset)) { - furi_string_reset(item_name); - furi_string_reset(item_time); - - subghz->state_notifications = SubGhzNotificationStateRxDone; subghz_history_get_text_item_menu(history, item_name, idx); subghz_history_get_time_item_menu(history, item_time, idx); @@ -202,19 +196,51 @@ static void subghz_scene_add_to_history_callback( subghz->subghz_receiver, furi_string_get_cstr(item_name), furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(history, idx)); + subghz_history_get_type_protocol(history, idx), + subghz_history_get_repeats(history, idx)); + + if(decoder_base->protocol->flag & SubGhzProtocolFlag_Save && + subghz->last_settings->autosave) { + // File name + char file[SUBGHZ_MAX_LEN_NAME] = {0}; + const char* suf = subghz->last_settings->protocol_file_names ? + decoder_base->protocol->name : + SUBGHZ_APP_FILENAME_PREFIX; + FuriHalRtcDateTime time = subghz_history_get_datetime(history, idx); + name_generator_make_detailed_datetime(file, sizeof(file), suf, &time, true); + // Dir name + FuriString* path = furi_string_alloc_set(SUBGHZ_APP_FOLDER "/Autosave"); + char* dir = strdup(furi_string_get_cstr(path)); + // Find non-existent path + const char* ext = SUBGHZ_APP_FILENAME_EXTENSION; + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_get_next_filename(storage, dir, file, ext, path, sizeof(file)); + strlcpy(file, furi_string_get_cstr(path), sizeof(file)); + furi_string_printf(path, "%s/%s%s", dir, file, ext); + furi_record_close(RECORD_STORAGE); + free(dir); + // Save + subghz_save_protocol_to_file( + subghz, + subghz_history_get_raw_data(history, idx), + furi_string_get_cstr(path)); + furi_string_free(path); + } subghz_scene_receiver_update_statusbar(subghz); - if(subghz_history_get_text_space_left(subghz->history, NULL)) { + if(!subghz->last_settings->delete_old_signals && + subghz_history_full(subghz->history)) { + subghz->state_notifications = SubGhzNotificationStateIDLE; notification_message(subghz->notifications, &sequence_error); + } else { + subghz->state_notifications = SubGhzNotificationStateRxDone; } } - subghz_receiver_reset(receiver); - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); } + subghz_receiver_reset(receiver); furi_string_free(item_name); furi_string_free(item_time); - + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); } else { FURI_LOG_D(TAG, "%s protocol ignored", decoder_base->protocol->name); } @@ -226,8 +252,6 @@ void subghz_scene_receiver_on_enter(void* context) { FuriString* item_name = furi_string_alloc(); FuriString* item_time = furi_string_alloc(); - subghz->repeater_tx = flipper_format_string_alloc(); - subghz->RepeaterTXLength = 0; if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) { #if SUBGHZ_LAST_SETTING_SAVE_PRESET @@ -259,7 +283,8 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->subghz_receiver, furi_string_get_cstr(item_name), furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(history, i)); + subghz_history_get_type_protocol(history, i), + subghz_history_get_repeats(history, i)); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); } furi_string_free(item_name); @@ -269,46 +294,52 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); subghz_txrx_set_rx_callback(subghz->txrx, subghz_scene_add_to_history_callback, subghz); - if(!subghz_history_get_text_space_left(subghz->history, NULL)) { + if(!subghz_history_full(subghz->history)) { subghz->state_notifications = SubGhzNotificationStateRx; + } else { + subghz->state_notifications = SubGhzNotificationStateIDLE; } - // Check if hopping was enabled, and restart the radio. - subghz_txrx_hopper_set_state( - subghz->txrx, - subghz->last_settings->enable_hopping ? SubGhzHopperStateRunning : SubGhzHopperStateOFF); + // Check if hopping was enabled + if(subghz->last_settings->enable_hopping) { + subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateRunning); + } else { + subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); + } // Check if Sound was enabled, and restart the Speaker. subghz_txrx_speaker_set_state( subghz->txrx, subghz->last_settings->enable_sound ? SubGhzSpeakerStateEnable : - SubGhzSpeakerStateDisable); + SubGhzSpeakerStateShutdown); + + //Set up a timer for the repeater (recycled favorites timeout TX timer!). + if(!subghz->timer) + subghz->timer = furi_timer_alloc(repeater_stop_callback, FuriTimerTypeOnce, subghz); + + //Remember if the repeater was loaded, and do cleanups we need. + subghz->repeater = subghz->last_settings->repeater_state; + + //Did the user set BinRAW or me? + if(subghz->repeater != SubGhzRepeaterStateOff) { + //User had BinRAW on if the last settings had BinRAW on, if not, repeater is on, and BinRAW goes on, but State CHanged is false! + subghz->repeater_bin_raw_was_off = + (subghz->last_settings->filter & SubGhzProtocolFlag_BinRAW) == 0; + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { + subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + } + } else { + subghz->repeater_bin_raw_was_off = false; + } - /* Proper fix now! Start the radio again. */ subghz_txrx_rx_start(subghz->txrx); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); - subghz_txrx_hopper_unpause(subghz->txrx); //to use a universal decoder, we are looking for a link to it furi_check( subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME)); - //Remember if the repeater was loaded, need to set protocol back to BinRAW if we are repeating. - //Do this last so that I can override the BinRAW load or not! - if(subghz->last_settings->RepeaterState != SubGhzRepeaterOff) { - subghz->repeater = subghz->last_settings->RepeaterState; - - //User had BinRAW on if the last settings had BinRAW on, if not, repeater is on, and BinRAW goes on, but State CHanged is false! - subghz->BINRawStateChanged = - (subghz->last_settings->filter != - (SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW)); - subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; - subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); - } else { - subghz->repeater = SubGhzRepeaterOff; - subghz->BINRawStateChanged = false; - } - subghz_scene_receiver_update_statusbar(subghz); subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); @@ -331,81 +362,23 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz); - if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { + if(subghz_history_get_last_index(subghz->history)) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); subghz_txrx_set_default_preset(subghz->txrx, subghz->last_settings->frequency); - - //The user could have come from the Start Scene, or birect through Send RAW only then Listen TX. - //Go back to the right scene! - - //Did the user come from the Start Menu or Browser loading a Sub file? - if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { - //Scene exists, go back to it - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneStart); - } else { - //We came through transmitter or Send RAW scenes. go back to them! - //if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) { - //Load Raw TX - // subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); - if(subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), false)) { - scene_manager_previous_scene(subghz->scene_manager); - } else { - //Go to Start Scene! - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); - } - } - - //scene_manager_search_and_switch_to_previous_scene(subghz->scene_manager, SubGhzSceneStart); + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); } consumed = true; break; case SubGhzCustomEventViewReceiverOK: - //I am turning off the Radio, if the FLipper people want to fix the bug they can! - subghz->state_notifications = SubGhzNotificationStateIDLE; - subghz_txrx_stop(subghz->txrx); - subghz_txrx_hopper_pause(subghz->txrx); - // Show file info, scene: receiver_info scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); dolphin_deed(DolphinDeedSubGhzReceiverInfo); consumed = true; break; - case SubGhzCustomEventViewReceiverOKLong: - subghz_txrx_stop(subghz->txrx); - subghz_txrx_hopper_pause(subghz->txrx); - - //Start TX - if(!subghz_tx_start( - subghz, - subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) { - subghz_txrx_rx_start(subghz->txrx); - subghz_txrx_hopper_unpause(subghz->txrx); - subghz->state_notifications = SubGhzNotificationStateRx; - } else { - notification_message(subghz->notifications, &subghz_sequence_beep); - subghz->state_notifications = SubGhzNotificationStateTx; - } - consumed = true; - break; - - case SubGhzCustomEventViewReceiverOKRelease: - if(subghz->state_notifications == SubGhzNotificationStateTx) { - //CC1101 Stop Tx -> Start RX - subghz->state_notifications = SubGhzNotificationStateIDLE; - subghz_txrx_stop(subghz->txrx); - subghz_txrx_rx_start(subghz->txrx); - subghz_txrx_hopper_unpause(subghz->txrx); - if(!subghz_history_get_text_space_left(subghz->history, NULL)) { - subghz->state_notifications = SubGhzNotificationStateRx; - } - consumed = true; - break; - } - case SubGhzCustomEventViewReceiverDeleteItem: subghz->state_notifications = SubGhzNotificationStateRx; @@ -425,22 +398,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case SubGhzCustomEventViewReceiverConfig: - // Actually signals are received but SubGhzNotificationStateRx is not working inside Config Scene - - /* ^^^ This is the comment that started all this crap for me! No, signals are not received, - the decode cant send to the history in the background. Info has to be active scene. - T his could be changed, but for now lets just make the radio behave as the interface does! - - Making the worker call subghz_scene_add_to_history_callback and making it work properly probably is best in long run. - */ - - //I am turning off the Radio, if the FLipper people want to fix the bug they can! - subghz->state_notifications = SubGhzNotificationStateIDLE; - subghz_txrx_stop(subghz->txrx); - subghz_txrx_hopper_pause(subghz->txrx); - - //Start the config scene. scene_manager_set_scene_state( subghz->scene_manager, SubGhzViewIdReceiver, SubGhzCustomEventManagerSet); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); @@ -454,12 +412,78 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_unlock(subghz); consumed = true; break; + case SubGhzCustomEventViewRepeaterStart: + subghz_txrx_stop(subghz->txrx); + subghz_txrx_hopper_pause(subghz->txrx); + + FlipperFormat* key_repeat_data = subghz_history_get_raw_data( + subghz->history, subghz_history_get_last_index(subghz->history) - 1); + + uint32_t tmpTe = 300; + if(!flipper_format_rewind(key_repeat_data)) { + FURI_LOG_E(TAG, "Rewind error"); + } else if(!flipper_format_read_uint32(key_repeat_data, "TE", (uint32_t*)&tmpTe, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + } + + if(subghz_txrx_tx_start(subghz->txrx, key_repeat_data) != SubGhzTxRxStartTxStateOk) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewRepeaterStop); + } else { + subghz->state_notifications = SubGhzNotificationStateTx; + notification_message(subghz->notifications, &subghz_sequence_tx_beep); + + uint32_t repeatnormal = (tmpTe > 1000) ? 1 : 3; + uint32_t repeat_time = ((subghz->repeater & SubGhzRepeaterStateOnLong) != 0) ? + 2 * repeatnormal * tmpTe : + ((subghz->repeater & SubGhzRepeaterStateOnShort) != 0) ? + 1 * tmpTe : + repeatnormal * tmpTe; + furi_timer_start(subghz->timer, repeat_time); + } + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); + break; + case SubGhzCustomEventViewRepeaterStop: + subghz_txrx_stop(subghz->txrx); + subghz_history_delete_item( + subghz->history, subghz_history_get_last_index(subghz->history) - 1); + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + subghz->state_notifications = SubGhzNotificationStateRx; + break; + case SubGhzCustomEventViewReceiverOKLong: + subghz_txrx_stop(subghz->txrx); + subghz_txrx_hopper_pause(subghz->txrx); + if(subghz_txrx_tx_start( + subghz->txrx, + subghz_history_get_raw_data( + subghz->history, + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver))) != + SubGhzTxRxStartTxStateOk) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewReceiverOKRelease); + } else { + subghz->state_notifications = SubGhzNotificationStateTx; + notification_message(subghz->notifications, &subghz_sequence_tx_beep); + } + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); + break; + case SubGhzCustomEventViewReceiverOKRelease: + if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateTX) { + subghz_txrx_stop(subghz->txrx); + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + subghz->state_notifications = SubGhzNotificationStateRx; + break; + } + break; default: break; } } else if(event.type == SceneManagerEventTypeTick) { - if(subghz->state_notifications != SubGhzNotificationStateTx) { - //Do the receive stuff, the repeater TX logic is here now! + if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateTX) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { subghz_txrx_hopper_update(subghz->txrx); subghz_scene_receiver_update_statusbar(subghz); @@ -468,71 +492,56 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); + if(subghz->last_settings->gps_baudrate != 0) { + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + if((datetime.second - subghz->gps->fix_second) > 15) { + subghz->gps->latitude = NAN; + subghz->gps->longitude = NAN; + subghz->gps->satellites = 0; + subghz->gps->fix_hour = 0; + subghz->gps->fix_minute = 0; + subghz->gps->fix_second = 0; + } + subghz_scene_receiver_update_statusbar(subghz); + } + subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); subghz_protocol_decoder_bin_raw_data_input_rssi( (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), ret_rssi.rssi); - } else if(subghz->repeater != SubGhzRepeaterOff) { - //Start or Stop TX? - if(subghz->RepeaterStartTime == 0) { - if(subghz->RepeaterTXLength > - 0) { //We could be holding TX on a key we received, switched to repeater, and TXed wih keys on the list. - subghz->RepeaterStartTime = furi_get_tick(); - - //CC1101 Stop RX -> Start TX - if(!subghz_tx_start(subghz, subghz->repeater_tx)) { - subghz_txrx_rx_start(subghz->txrx); - subghz_txrx_hopper_unpause(subghz->txrx); - subghz->state_notifications = SubGhzNotificationStateRx; - } + } + + switch(subghz->state_notifications) { + case SubGhzNotificationStateRx: + if(subghz->last_settings->gps_baudrate != 0) { + if(subghz->gps->satellites > 0) { + notification_message(subghz->notifications, &sequence_blink_green_10); + } else { + notification_message(subghz->notifications, &sequence_blink_red_10); } } else { - uint32_t tmp = furi_get_tick() - subghz->RepeaterStartTime; - uint8_t RepeatMultiplier = (subghz->repeater == SubGhzRepeaterOnShort) ? 2 : //2 key Tx - (subghz->repeater == SubGhzRepeaterOnLong) ? 10 : //10x Repeat - 5; //5x Repeat - - if(tmp > furi_ms_to_ticks(subghz->RepeaterTXLength) * RepeatMultiplier) { - FURI_LOG_I(TAG, "TXLength: %lu TxTime: %lu", subghz->RepeaterTXLength, tmp); - - subghz_txrx_stop(subghz->txrx); - subghz->RepeaterTXLength = 0; - subghz->RepeaterStartTime = 0; - subghz->ignore_filter = 0x00; - subghz_txrx_rx_start(subghz->txrx); - subghz_txrx_hopper_unpause(subghz->txrx); - subghz->state_notifications = SubGhzNotificationStateRx; - //notification_message(subghz->notifications, &subghz_sequence_repeat); - } + notification_message(subghz->notifications, &sequence_blink_cyan_10); } + break; + case SubGhzNotificationStateRxDone: + if(!subghz_is_locked(subghz)) { + notification_message(subghz->notifications, &subghz_sequence_rx); + } else { + notification_message(subghz->notifications, &subghz_sequence_rx_locked); + } + subghz->state_notifications = SubGhzNotificationStateRx; + break; + case SubGhzNotificationStateTx: + notification_message(subghz->notifications, &sequence_blink_magenta_10); + break; + default: + break; } } - - switch(subghz->state_notifications) { - case SubGhzNotificationStateRx: - notification_message(subghz->notifications, &sequence_blink_cyan_10); - break; - case SubGhzNotificationStateRxDone: - if(!subghz_is_locked(subghz)) { - notification_message(subghz->notifications, &subghz_sequence_rx); - } else { - notification_message(subghz->notifications, &subghz_sequence_rx_locked); - } - subghz->state_notifications = SubGhzNotificationStateRx; - break; - case SubGhzNotificationStateTx: - notification_message(subghz->notifications, &sequence_blink_magenta_10); - break; - default: - break; - } - return consumed; } void subghz_scene_receiver_on_exit(void* context) { - SubGhz* subghz = context; - flipper_format_free(subghz->repeater_tx); - - //UNUSED(context); + UNUSED(context); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 14aaa6f95a6..ae3741e5303 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -5,21 +5,25 @@ enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, - SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, + SubGhzSettingIndexHopping, + SubGhzSettingIndexRAWSound = SubGhzSettingIndexHopping, SubGhzSettingIndexBinRAW, + SubGhzSettingIndexRAWRSSIThreshold = SubGhzSettingIndexBinRAW, SubGhzSettingIndexRepeater, - SubGhzSettingIndexSound, - SubGhzSettingIndexListenAfterTX, + SubGhzSettingIndexRemoveDuplicates, + SubGhzSettingIndexDeleteOldSignals, + SubGhzSettingIndexAutosave, SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexIgnoreCars, SubGhzSettingIndexIgnoreMagellan, SubGhzSettingIndexIgnorePrinceton, SubGhzSettingIndexIgnoreNiceFlorS, - SubGhzSettingIndexDeleteOldSignals, + SubGhzSettingIndexIgnoreWeather, + SubGhzSettingIndexIgnoreTPMS, + SubGhzSettingIndexSound, SubGhzSettingIndexResetToDefault, SubGhzSettingIndexLock, - SubGhzSettingIndexRAWThresholdRSSI, }; #define RAW_THRESHOLD_RSSI_COUNT 11 @@ -52,7 +56,6 @@ const float raw_threshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { }; #define COMBO_BOX_COUNT 2 -#define REPEATER_BOX_COUNT 4 const uint32_t hopping_value[COMBO_BOX_COUNT] = { SubGhzHopperStateOFF, @@ -69,32 +72,28 @@ const uint32_t bin_raw_value[COMBO_BOX_COUNT] = { SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; -const uint32_t repeater_value[REPEATER_BOX_COUNT] = { - SubGhzRepeaterOff, - SubGhzRepeaterOn, - SubGhzRepeaterOnLong, - SubGhzRepeaterOnShort, -}; - -const uint32_t listen_after_tx_value[COMBO_BOX_COUNT] = { - false, - true, -}; - const char* const combobox_text[COMBO_BOX_COUNT] = { "OFF", "ON", }; -const char* const repeater_box_text[REPEATER_BOX_COUNT] = { +#define REPEATER_COUNT 4 +const char* const repeater_text[REPEATER_COUNT] = { "OFF", "Normal", "Long", "Short", }; +const uint32_t repeater_value[REPEATER_COUNT] = { + SubGhzRepeaterStateOff, + SubGhzRepeaterStateOn, + SubGhzRepeaterStateOnLong, + SubGhzRepeaterStateOnShort, +}; -static void - subghz_scene_receiver_config_set_ignore_filter(VariableItem* item, SubGhzProtocolFlag filter) { +static void subghz_scene_receiver_config_set_ignore_filter( + VariableItem* item, + SubGhzProtocolFilter filter) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -151,8 +150,7 @@ SubGhzHopperState subghz_scene_receiver_config_hopper_value_index(void* context) return SubGhzHopperStateOFF; } else { variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig), + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexFrequency), " -----"); return SubGhzHopperStateRunning; } @@ -179,6 +177,8 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { subghz->txrx, furi_string_get_cstr(preset.name), frequency, + 0, + 0, preset.data, preset.data_size); @@ -205,6 +205,8 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) { subghz->txrx, preset_name, preset.frequency, + 0, + 0, subghz_setting_get_preset_data(setting, index), subghz_setting_get_preset_data_size(setting, index)); subghz->last_settings->preset_index = index; @@ -214,8 +216,8 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) SubGhz* subghz = variable_item_get_context(item); SubGhzHopperState index = variable_item_get_current_value_index(item); SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); - VariableItem* frequency_item = (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig); + VariableItem* frequency_item = + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexFrequency); variable_item_set_current_value_text(item, combobox_text[(uint8_t)index]); @@ -237,6 +239,8 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) subghz->txrx, furi_string_get_cstr(preset.name), frequency, + 0, + 0, preset.data, preset.data_size); variable_item_set_current_value_index( @@ -269,61 +273,57 @@ static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { // We can set here, but during subghz_last_settings_save filter was changed to ignore BinRAW subghz->last_settings->filter = subghz->filter; - subghz->BINRawStateChanged = false; + + //If the user changed BinRAW menu, dont reset it with the repeater. + subghz->repeater_bin_raw_was_off = false; } -static void subghz_scene_receiver_config_set_listen_after_tx(VariableItem* item) { +static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, combobox_text[index]); - subghz->ListenAfterTX = listen_after_tx_value[index]; + if(subghz->repeater == SubGhzRepeaterStateOff && + subghz_history_get_last_index(subghz->history)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReceiverConfig, SubGhzSettingIndexRepeater); + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneSettingRepeater); + return; + } - // We can set here, but during subghz_last_settings_save filter was changed to ignore BinRAW - subghz->last_settings->enable_listen_after_tx = subghz->ListenAfterTX; -} + //Set menu Text. + variable_item_set_current_value_text(item, repeater_text[index]); -static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); + //Save state and in last settings. + subghz->repeater = repeater_value[index]; + subghz->last_settings->repeater_state = repeater_value[index]; - variable_item_set_current_value_text(item, repeater_box_text[index]); - subghz_txrx_repeater_set_state(&subghz->repeater, repeater_value[index]); + //Get the BinRAW menu for state change. + VariableItem* bin_raw_menu = + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexBinRAW); //Change BinRAW to ON or OFF as required, and remember whether I changed it! (Put back for the user.) - if(repeater_value[index] != SubGhzRepeaterOff) { - subghz->last_settings->RepeaterState = repeater_value[index]; - - if((bin_raw_value[variable_item_get_current_value_index(subghz->BIN_Raw_menu)] & - SubGhzProtocolFlag_BinRAW) == SubGhzProtocolFlag_BinRAW) { - //BinRAW is on, repeater is on. - } else { + if(repeater_value[index] != SubGhzRepeaterStateOff) { + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { //Repeater is on, Binraw is Off. variable_item_set_current_value_index( - subghz->BIN_Raw_menu, 1 /*Index of ON in BIN_Raw menu!*/); - subghz_scene_receiver_config_set_bin_raw(subghz->BIN_Raw_menu); - subghz->BINRawStateChanged = true; + bin_raw_menu, 1 /*Index of ON in BIN_Raw menu!*/); + subghz_scene_receiver_config_set_bin_raw(bin_raw_menu); + subghz->repeater_bin_raw_was_off = true; } - //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes very key send. - variable_item_set_locked( - subghz->BIN_Raw_menu, true, "Turn off\n Repeater!\n to do\n that!"); - /* REPEATER LOCK BINRAW */ + //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes every key send. + variable_item_set_locked(bin_raw_menu, true, NULL); } else { - subghz->last_settings->RepeaterState = SubGhzRepeaterOff; - //Put BinRAW back how it was, if we changed it. - if(subghz->BINRawStateChanged) { + if(subghz->repeater_bin_raw_was_off) { variable_item_set_current_value_index( - subghz->BIN_Raw_menu, 0 /*Index of OFF in BIN_Raw menu!*/); - subghz_scene_receiver_config_set_bin_raw(subghz->BIN_Raw_menu); - subghz->BINRawStateChanged = false; + bin_raw_menu, 0 /*Index of OFF in BIN_Raw menu!*/); + subghz_scene_receiver_config_set_bin_raw(bin_raw_menu); } //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes very key send. - variable_item_set_locked( - subghz->BIN_Raw_menu, false, "Turn off\n Repeater!\n to do\n that!"); - /* REPEATER LOCK BINRAW */ + variable_item_set_locked(bin_raw_menu, false, NULL); } } @@ -337,45 +337,74 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it subghz->last_settings->rssi = raw_threshold_rssi_value[index]; } +static void subghz_scene_receiver_config_set_duplicates(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, combobox_text[index]); + + subghz->last_settings->remove_duplicates = subghz->remove_duplicates = index; + if(index) subghz_history_remove_duplicates(subghz->history); +} + +static void subghz_scene_receiver_config_set_delete_old_signals(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, combobox_text[index]); + + subghz->last_settings->delete_old_signals = index == 1; +} + +static void subghz_scene_receiver_config_set_autosave(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, combobox_text[index]); + + subghz->last_settings->autosave = index == 1; +} + static inline bool subghz_scene_receiver_config_ignore_filter_get_index( - SubGhzProtocolFlag filter, - SubGhzProtocolFlag flag) { + SubGhzProtocolFilter filter, + SubGhzProtocolFilter flag) { return READ_BIT(filter, flag) > 0; } static void subghz_scene_receiver_config_set_starline(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_StarLine); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_StarLine); } static void subghz_scene_receiver_config_set_auto_alarms(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_AutoAlarms); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_AutoAlarms); } static void subghz_scene_receiver_config_set_magellan(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Magellan); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_Magellan); } static void subghz_scene_receiver_config_set_princeton(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Princeton); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_Princeton); } static void subghz_scene_receiver_config_set_niceflors(VariableItem* item) { - subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_NiceFlorS); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_NiceFlorS); } -static void subghz_scene_receiver_config_set_delete_old_signals(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, combobox_text[index]); - - subghz->last_settings->delete_old_signals = index == 1; +static void subghz_scene_receiver_config_set_weather(VariableItem* item) { + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_Weather); +} +static void subghz_scene_receiver_config_set_tpms(VariableItem* item) { + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFilter_TPMS); } static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; - if(index == SubGhzSettingIndexLock) { + if(index == SubGhzSettingIndexRemoveDuplicates) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneSettingRemoveDuplicates); + } else if(index == SubGhzSettingIndexLock) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventSceneSettingLock); } else if(index == SubGhzSettingIndexResetToDefault) { @@ -396,18 +425,22 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz->last_settings->frequency = preset.frequency; subghz->last_settings->preset_index = preset_index; - subghz->last_settings->RepeaterState = SubGhzRepeaterOff; - subghz_txrx_repeater_set_state(&subghz->repeater, SubGhzRepeaterOff); - subghz->BINRawStateChanged = false; + subghz_threshold_rssi_set(subghz->threshold_rssi, raw_threshold_rssi_value[default_index]); subghz->filter = bin_raw_value[0]; subghz->ignore_filter = 0x00; + subghz->remove_duplicates = false; subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + subghz->last_settings->remove_duplicates = subghz->remove_duplicates; subghz->last_settings->ignore_filter = subghz->ignore_filter; subghz->last_settings->filter = subghz->filter; + subghz->last_settings->repeater_state = SubGhzRepeaterStateOff; + subghz->repeater = SubGhzRepeaterStateOff; subghz->last_settings->delete_old_signals = false; + subghz->last_settings->autosave = false; subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]); + subghz->last_settings->enable_sound = false; subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]); subghz->last_settings->enable_hopping = hopping_value[default_index]; @@ -431,12 +464,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); - //This gets checked a few times, so lets cache this. - bool raw_send_only = - (scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == - SubGhzCustomEventManagerSet); - //bool ListenAfterTX = subghz->last_settings->enable_listen_after_tx; - item = variable_item_list_add( subghz->variable_item_list, "Frequency", @@ -444,8 +471,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz_scene_receiver_config_set_frequency, subghz); value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); variable_item_set_current_value_index(item, value_index); char text_buf[10] = {0}; uint32_t frequency = subghz_setting_get_frequency(setting, value_index); @@ -469,7 +494,8 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_text( item, subghz_setting_get_preset_name(setting, value_index)); - if(!raw_send_only) { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { // Hopping item = variable_item_list_add( subghz->variable_item_list, @@ -483,10 +509,11 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_text(item, combobox_text[value_index]); } - if(!raw_send_only) { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, - "RAW Receive", + "Bin RAW", COMBO_BOX_COUNT, subghz_scene_receiver_config_set_bin_raw, subghz); @@ -494,64 +521,53 @@ void subghz_scene_receiver_config_on_enter(void* context) { value_index = value_index_uint32(subghz->filter, bin_raw_value, COMBO_BOX_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); - subghz->BIN_Raw_menu = item; - } + variable_item_set_locked( + item, subghz->repeater != SubGhzRepeaterStateOff, "Turn off\nRepeater\nto do that!"); - /* Repeater Menu. With the Repeater, you can relay a key. - To Use, select the frequency (or hopper) and enable Repeater. - Any keys decoded with BinRAW will be transmitted as soon as they are received. - Ill leave the uses of this one up to your imagination! */ - if(!raw_send_only) { item = variable_item_list_add( subghz->variable_item_list, "Repeater", - REPEATER_BOX_COUNT, + REPEATER_COUNT, subghz_scene_receiver_config_set_repeater, subghz); - value_index = value_index_uint32( - subghz_txrx_repeater_get_state(&subghz->repeater), repeater_value, REPEATER_BOX_COUNT); + + value_index = value_index_uint32(subghz->repeater, repeater_value, REPEATER_COUNT); variable_item_set_current_value_index(item, value_index); - //variable_item_set_current_value_text(item, repeater_box_text[value_index]); - //Instead of setting the text, Im calling the callback. That sets the text, and the bin raw on off! - subghz_scene_receiver_config_set_repeater(item); - //Remember if the user set BinRAW on or if I did with the repeater! - subghz->BINRawStateChanged = - (subghz->last_settings->filter != - (SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW)); - } + variable_item_set_current_value_text(item, repeater_text[value_index]); - // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) - item = variable_item_list_add( - subghz->variable_item_list, - "Sound", - COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_speaker, - subghz); - value_index = value_index_uint32( - subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, COMBO_BOX_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, combobox_text[value_index]); + item = variable_item_list_add( + subghz->variable_item_list, + "Remove Duplicates", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_duplicates, + subghz); - /* And now we have the WakeUp commmand for the Repeater. Set this, record the wake up code from the car. - take wake up command to fob, play back and be in repeater or read to get the key to open and start. - Power of Repeated command may have to be played with in future so cars dont detect too much power from key. - Im sure theres a milion other ways you can set this up to do other stuff, but the Wake Up is easy now! - Time to imagine the other uses, and make sure we can accomdateg them! - */ - //if(!subghz->raw_send_only) { //Cant switch to receive if the app is opened TX only! (We never get here either, but just to be safe!) - item = variable_item_list_add( - subghz->variable_item_list, - "Listen after TX", - COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_listen_after_tx, - subghz); - value_index = - value_index_uint32(subghz->ListenAfterTX, listen_after_tx_value, COMBO_BOX_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, combobox_text[value_index]); - //} + value_index = subghz->remove_duplicates; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Delete Old Signals on Full Memory", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_delete_old_signals, + subghz); + + value_index = subghz->last_settings->delete_old_signals; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Autosave", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_autosave, + subghz); + + value_index = subghz->last_settings->autosave; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); - if(!raw_send_only) { item = variable_item_list_add( subghz->variable_item_list, "Ignore Starline", @@ -560,7 +576,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz); value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFlag_StarLine); + subghz->ignore_filter, SubGhzProtocolFilter_StarLine); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); @@ -572,7 +588,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz); value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFlag_AutoAlarms); + subghz->ignore_filter, SubGhzProtocolFilter_AutoAlarms); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); @@ -584,7 +600,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz); value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFlag_Magellan); + subghz->ignore_filter, SubGhzProtocolFilter_Magellan); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); @@ -596,7 +612,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz); value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFlag_Princeton); + subghz->ignore_filter, SubGhzProtocolFilter_Princeton); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); @@ -608,32 +624,59 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz); value_index = subghz_scene_receiver_config_ignore_filter_get_index( - subghz->ignore_filter, SubGhzProtocolFlag_NiceFlorS); + subghz->ignore_filter, SubGhzProtocolFilter_NiceFlorS); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); item = variable_item_list_add( subghz->variable_item_list, - "Delete old signals when memory is full", + "Ignore Weather", COMBO_BOX_COUNT, - subghz_scene_receiver_config_set_delete_old_signals, + subghz_scene_receiver_config_set_weather, subghz); - value_index = subghz->last_settings->delete_old_signals; + value_index = subghz_scene_receiver_config_ignore_filter_get_index( + subghz->ignore_filter, SubGhzProtocolFilter_Weather); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Ignore TPMS", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_tpms, + subghz); + + value_index = subghz_scene_receiver_config_ignore_filter_get_index( + subghz->ignore_filter, SubGhzProtocolFilter_TPMS); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); } - if(!raw_send_only) { + // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) + item = variable_item_list_add( + subghz->variable_item_list, + "Sound", + COMBO_BOX_COUNT, + subghz_scene_receiver_config_set_speaker, + subghz); + value_index = value_index_uint32( + subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, COMBO_BOX_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, combobox_text[value_index]); + + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { // Reset to default - variable_item_list_add(subghz->variable_item_list, "Reset to default", 1, NULL, NULL); + variable_item_list_add(subghz->variable_item_list, "Reset to Default", 1, NULL, NULL); variable_item_list_set_enter_callback( subghz->variable_item_list, subghz_scene_receiver_config_var_list_enter_callback, subghz); } - if(!raw_send_only) { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { // Lock keyboard variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); variable_item_list_set_enter_callback( @@ -642,7 +685,8 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz); } - if(raw_send_only) { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == + SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, "RSSI Threshold:", @@ -656,6 +700,12 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, raw_threshold_rssi_text[value_index]); } + + variable_item_list_set_selected_item( + subghz->variable_item_list, + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReceiverConfig)); + scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneReceiverConfig, 0); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } @@ -664,7 +714,15 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneSettingLock) { + if(event.event == SubGhzCustomEventSceneSettingRepeater) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); + consumed = true; + } else if(event.event == SubGhzCustomEventSceneSettingRemoveDuplicates) { + subghz_history_remove_duplicates(subghz->history); + scene_manager_previous_scene(subghz->scene_manager); + consumed = true; + } else if(event.event == SubGhzCustomEventSceneSettingLock) { subghz_lock(subghz); scene_manager_previous_scene(subghz->scene_manager); consumed = true; @@ -679,13 +737,9 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; - //If the Repeater changed the BinRAW settings, put them back now. That will ensure it loads correctly! - if(subghz->BINRawStateChanged & (subghz->repeater != SubGhzRepeaterOff)) { - //Bit hacky, but works well. + if(subghz->repeater_bin_raw_was_off) { subghz->last_settings->filter = bin_raw_value[0 /*BinRAW Off*/]; - subghz->BINRawStateChanged = false; } - subghz->BIN_Raw_menu = NULL; variable_item_list_set_selected_item(subghz->variable_item_list, 0); variable_item_list_reset(subghz->variable_item_list); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 12dec50f011..63048e16255 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -1,19 +1,9 @@ #include "../subghz_i.h" -#include #include #define TAG "SubGhzSceneReceiverInfo" -const NotificationSequence subghz_sequence_info_beep = { - &message_vibro_on, - &message_note_c6, - &message_delay_50, - &message_sound_off, - &message_vibro_off, - NULL, -}; - void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); SubGhz* subghz = context; @@ -27,6 +17,11 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave); + } else if( + (result == GuiButtonTypeLeft) && (type == InputTypeShort) && + subghz->last_settings->gps_baudrate != 0) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSats); } } @@ -47,6 +42,8 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { subghz->txrx, furi_string_get_cstr(preset->name), preset->frequency, + 0, + 0, preset->data, preset->data_size); @@ -84,6 +81,15 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) { widget_add_string_multiline_element( subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); + if(subghz->last_settings->gps_baudrate != 0) { + widget_add_button_element( + subghz->widget, + GuiButtonTypeLeft, + "Geo", + subghz_scene_receiver_info_callback, + subghz); + } + furi_string_free(frequency_str); furi_string_free(modulation_str); furi_string_free(text); @@ -110,6 +116,8 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) { widget_add_string_element( subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); } + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); } void subghz_scene_receiver_info_on_enter(void* context) { @@ -118,15 +126,10 @@ void subghz_scene_receiver_info_on_enter(void* context) { subghz_custom_btns_reset(); subghz_scene_receiver_info_draw_widget(subghz); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); - /* This does not work. The receiving does not happen, and they know it. - So, why do we turn on the notification that we receive? - THe user thinks its receiving, dont matter if SubGHZ is technically on. - if(!subghz_history_get_text_space_left(subghz->history, NULL)) { + if(!subghz_history_full(subghz->history)) { subghz->state_notifications = SubGhzNotificationStateRx; } - */ } bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { @@ -137,63 +140,44 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) return false; } //CC1101 Stop RX -> Start TX - // subghz_txrx_hopper_pause(subghz->txrx); + subghz_txrx_hopper_pause(subghz->txrx); if(!subghz_tx_start( subghz, subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) { - // subghz_txrx_rx_start(subghz->txrx); - // subghz_txrx_hopper_unpause(subghz->txrx); - subghz->state_notifications = SubGhzNotificationStateIDLE; + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz->state_notifications = SubGhzNotificationStateRx; } else { subghz->state_notifications = SubGhzNotificationStateTx; } - notification_message(subghz->notifications, &subghz_sequence_info_beep); return true; } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { //CC1101 Stop Tx -> Start RX subghz->state_notifications = SubGhzNotificationStateIDLE; + + widget_reset(subghz->widget); + subghz_scene_receiver_info_draw_widget(subghz); + subghz_txrx_stop(subghz->txrx); + if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) { + subghz_txrx_rx_start(subghz->txrx); - //Go back to the Scan/Repeater if the Listen After TX flag is on. - if(subghz->ListenAfterTX) { - if(scene_manager_has_previous_scene( - subghz->scene_manager, SubGhzSceneReceiverInfo)) { - //Scene exists, go back to it - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneReceiver); - } else { - //Scene not started, start it now. - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); + subghz_txrx_hopper_unpause(subghz->txrx); + if(!subghz_history_full(subghz->history)) { + subghz->state_notifications = SubGhzNotificationStateRx; } - } else { - widget_reset(subghz->widget); - subghz_scene_receiver_info_draw_widget(subghz); } - - // if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) { - // subghz_txrx_rx_start(subghz->txrx); - - // subghz_txrx_hopper_unpause(subghz->txrx); - // if(!subghz_history_get_text_space_left(subghz->history, NULL)) { - // subghz->state_notifications = SubGhzNotificationStateRx; - // } - // } return true; } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { //CC1101 Stop RX -> Save subghz->state_notifications = SubGhzNotificationStateIDLE; - // subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); + subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); - // subghz_txrx_stop(subghz->txrx); + subghz_txrx_stop(subghz->txrx); if(!subghz_scene_receiver_info_update_parser(subghz)) { return false; } - //I am turning off the Radio, if the FLipper people want to fix the bug they can! - subghz->state_notifications = SubGhzNotificationStateIDLE; - subghz_txrx_stop(subghz->txrx); - subghz_txrx_hopper_pause(subghz->txrx); - if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { subghz_file_name_clear(subghz); @@ -203,6 +187,13 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); } return true; + } else if(event.event == SubGhzCustomEventSceneReceiverInfoSats) { + if(subghz->last_settings->gps_baudrate != 0) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowGps); + return true; + } else { + return false; + } } } else if(event.type == SceneManagerEventTypeTick) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { @@ -213,7 +204,15 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) notification_message(subghz->notifications, &sequence_blink_magenta_10); break; case SubGhzNotificationStateRx: - notification_message(subghz->notifications, &sequence_blink_cyan_10); + if(subghz->last_settings->gps_baudrate != 0) { + if(subghz->gps->satellites > 0) { + notification_message(subghz->notifications, &sequence_blink_green_10); + } else { + notification_message(subghz->notifications, &sequence_blink_red_10); + } + } else { + notification_message(subghz->notifications, &sequence_blink_cyan_10); + } break; case SubGhzNotificationStateRxDone: notification_message(subghz->notifications, &sequence_blink_green_100); diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index d4fb697b304..2f8a1f03f79 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -1,5 +1,4 @@ #include "../subghz_i.h" -#include #include diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index c0768de4bb5..6cb361e2834 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -27,26 +27,14 @@ void subghz_scene_save_name_on_enter(void* context) { subghz->save_datetime_set = false; if(!subghz_path_is_file(subghz->file_path)) { SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); - - bool skip_dec_is_present = false; - if(decoder_result != 0x0) { - if(decoder_result != NULL) { - if(strlen(decoder_result->protocol->name) != 0 && - subghz->last_settings->timestamp_file_names) { - if(!scene_manager_has_previous_scene( - subghz->scene_manager, SubGhzSceneSetType)) { - name_generator_make_auto_datetime( - file_name_buf, - SUBGHZ_MAX_LEN_NAME, - decoder_result->protocol->name, - datetime); - skip_dec_is_present = true; - } - } - } - } - if(!skip_dec_is_present) { - name_generator_make_auto_datetime(file_name_buf, SUBGHZ_MAX_LEN_NAME, NULL, datetime); + if(subghz->last_settings->protocol_file_names && decoder_result != NULL && + strlen(decoder_result->protocol->name) != 0 && + !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSetType)) { + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, decoder_result->protocol->name, datetime); + } else { + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX, datetime); } furi_string_set(file_name, file_name_buf); furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); @@ -62,8 +50,13 @@ void subghz_scene_save_name_on_enter(void* context) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == SubGhzCustomEventManagerSetRAW) { dev_name_empty = true; - name_generator_make_auto_datetime( - file_name_buf, SUBGHZ_MAX_LEN_NAME, "RAW", datetime); + if(subghz->last_settings->protocol_file_names) { + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, "RAW", datetime); + } else { + name_generator_make_auto_datetime( + file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX, datetime); + } furi_string_set(file_name, file_name_buf); } } @@ -186,4 +179,4 @@ void subghz_scene_save_name_on_exit(void* context) { // Clear view text_input_reset(subghz->text_input); -} \ No newline at end of file +} diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index a274bac8f55..276e5ecc053 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -1,5 +1,4 @@ #include "../subghz_i.h" -#include #include "../helpers/subghz_custom_event.h" void subghz_scene_save_success_popup_callback(void* context) { diff --git a/applications/main/subghz/scenes/subghz_scene_saved_menu.c b/applications/main/subghz/scenes/subghz_scene_saved_menu.c index a65830f4b19..5dcd1c61d20 100644 --- a/applications/main/subghz/scenes/subghz_scene_saved_menu.c +++ b/applications/main/subghz/scenes/subghz_scene_saved_menu.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexEmulate, SubmenuIndexEdit, SubmenuIndexDelete, + SubmenuIndexGeo, }; void subghz_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -34,6 +35,24 @@ void subghz_scene_saved_menu_on_enter(void* context) { subghz_scene_saved_menu_submenu_callback, subghz); + FuriString* lat_str = furi_string_alloc(); + FuriString* lon_str = furi_string_alloc(); + + subghz_txrx_get_latitude_and_longitude(subghz->txrx, lat_str, lon_str); + + if(strcmp(furi_string_get_cstr(lat_str), "nan") != 0 && + strcmp(furi_string_get_cstr(lon_str), "nan") != 0) { + submenu_add_item( + subghz->submenu, + "Geographic info", + SubmenuIndexGeo, + subghz_scene_saved_menu_submenu_callback, + subghz); + } + + furi_string_free(lon_str); + furi_string_free(lat_str); + submenu_set_selected_item( subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu)); @@ -60,6 +79,11 @@ bool subghz_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEdit); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); return true; + } else if(event.event == SubmenuIndexGeo) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexGeo); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedShowGps); + return true; } } return false; diff --git a/applications/main/subghz/scenes/subghz_scene_saved_show_gps.c b/applications/main/subghz/scenes/subghz_scene_saved_show_gps.c new file mode 100644 index 00000000000..fa085888d8c --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_saved_show_gps.c @@ -0,0 +1,107 @@ +#include "../subghz_i.h" +#include "../helpers/subghz_custom_event.h" + +void subghz_scene_saved_show_gps_draw_satellites(void* context) { + SubGhz* subghz = context; + + FuriString* text_str = furi_string_alloc(); + FuriString* lat_str = furi_string_alloc(); + FuriString* lon_str = furi_string_alloc(); + + subghz_txrx_get_latitude_and_longitude(subghz->txrx, lat_str, lon_str); + + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + if((datetime.second - subghz->gps->fix_second) > 15) { + subghz->gps->latitude = NAN; + subghz->gps->longitude = NAN; + subghz->gps->satellites = 0; + subghz->gps->fix_hour = 0; + subghz->gps->fix_minute = 0; + subghz->gps->fix_second = 0; + } + + double latitude = atof(furi_string_get_cstr(lat_str)); + double longitude = atof(furi_string_get_cstr(lon_str)); + + double distance = subghz_gps_calc_distance( + latitude, longitude, (double)subghz->gps->latitude, (double)subghz->gps->longitude); + + float angle = subghz_gps_calc_angle( + latitude, longitude, (double)subghz->gps->latitude, (double)subghz->gps->longitude); + + char* angle_str = "?"; + if(angle > -22.5 && angle <= 22.5) { + angle_str = "E"; + } else if(angle > 22.5 && angle <= 67.5) { + angle_str = "NE"; + } else if(angle > 67.5 && angle <= 112.5) { + angle_str = "N"; + } else if(angle > 112.5 && angle <= 157.5) { + angle_str = "NW"; + } else if(angle < -22.5 && angle >= -67.5) { + angle_str = "SE"; + } else if(angle < -67.5 && angle >= -112.5) { + angle_str = "S"; + } else if(angle < -112.5 && angle >= -157.5) { + angle_str = "SW"; + } else if(angle < -157.5 || angle >= 157.5) { + angle_str = "W"; + } + + furi_string_printf( + text_str, + "Captured at: %f,\r\n%f\r\n\r\nRealtime: Sats: %d\r\nDistance: %.2f%s Dir: %s\r\nGPS time: %02d:%02d:%02d UTC", + latitude, + longitude, + subghz->gps->satellites, + subghz->gps->satellites > 0 ? distance > 1 ? distance : distance * 1000 : 0, + distance > 1 ? "km" : "m", + angle_str, + subghz->gps->fix_hour, + subghz->gps->fix_minute, + subghz->gps->fix_second); + + widget_add_text_scroll_element(subghz->widget, 0, 0, 128, 64, furi_string_get_cstr(text_str)); + + furi_string_free(text_str); + furi_string_free(lat_str); + furi_string_free(lon_str); +} + +void subghz_scene_saved_show_gps_refresh_screen(void* context) { + SubGhz* subghz = context; + widget_reset(subghz->widget); + subghz_scene_saved_show_gps_draw_satellites(subghz); +} + +void subghz_scene_saved_show_gps_on_enter(void* context) { + SubGhz* subghz = context; + + subghz_scene_saved_show_gps_draw_satellites(subghz); + + if(subghz->last_settings->gps_baudrate != 0) { + subghz->gps->timer = furi_timer_alloc( + subghz_scene_saved_show_gps_refresh_screen, FuriTimerTypePeriodic, subghz); + furi_timer_start(subghz->gps->timer, 1000); + } + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); +} + +bool subghz_scene_saved_show_gps_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void subghz_scene_saved_show_gps_on_exit(void* context) { + SubGhz* subghz = context; + + if(subghz->last_settings->gps_baudrate != 0) { + furi_timer_stop(subghz->gps->timer); + furi_timer_free(subghz->gps->timer); + } + + widget_reset(subghz->widget); +} \ No newline at end of file diff --git a/applications/main/subghz/scenes/subghz_scene_show_error.c b/applications/main/subghz/scenes/subghz_scene_show_error.c index f69de8252e4..4544260ef6e 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error.c @@ -1,5 +1,4 @@ #include "../subghz_i.h" -#include #include "../helpers/subghz_custom_event.h" static const NotificationSequence subghz_sequence_sd_error = { diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 3b03431a025..0de48c442a3 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -1,5 +1,4 @@ #include "../subghz_i.h" -#include #include "../helpers/subghz_custom_event.h" void subghz_scene_show_error_sub_popup_callback(void* context) { diff --git a/applications/main/subghz/scenes/subghz_scene_show_gps.c b/applications/main/subghz/scenes/subghz_scene_show_gps.c new file mode 100644 index 00000000000..ef3d18709c6 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_show_gps.c @@ -0,0 +1,143 @@ +#include "../subghz_i.h" +#include "../helpers/subghz_custom_event.h" + +void subghz_scene_show_gps_draw_satellites(void* context) { + SubGhz* subghz = context; + + FuriString* text = furi_string_alloc(); + FuriString* time_text = furi_string_alloc(); + + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + if((datetime.second - subghz->gps->fix_second) > 15) { + subghz->gps->latitude = NAN; + subghz->gps->longitude = NAN; + subghz->gps->satellites = 0; + subghz->gps->fix_hour = 0; + subghz->gps->fix_minute = 0; + subghz->gps->fix_second = 0; + } + + subghz_history_get_time_item_menu(subghz->history, time_text, subghz->idx_menu_chosen); + + double distance = subghz_gps_calc_distance( + (double)subghz_history_get_latitude(subghz->history, subghz->idx_menu_chosen), + (double)subghz_history_get_longitude(subghz->history, subghz->idx_menu_chosen), + (double)subghz->gps->latitude, + (double)subghz->gps->longitude); + + float angle = subghz_gps_calc_angle( + (double)subghz_history_get_latitude(subghz->history, subghz->idx_menu_chosen), + (double)subghz_history_get_longitude(subghz->history, subghz->idx_menu_chosen), + (double)subghz->gps->latitude, + (double)subghz->gps->longitude); + + char* angle_str = "?"; + if(angle > -22.5 && angle <= 22.5) { + angle_str = "E"; + } else if(angle > 22.5 && angle <= 67.5) { + angle_str = "NE"; + } else if(angle > 67.5 && angle <= 112.5) { + angle_str = "N"; + } else if(angle > 112.5 && angle <= 157.5) { + angle_str = "NW"; + } else if(angle < -22.5 && angle >= -67.5) { + angle_str = "SE"; + } else if(angle < -67.5 && angle >= -112.5) { + angle_str = "S"; + } else if(angle < -112.5 && angle >= -157.5) { + angle_str = "SW"; + } else if(angle < -157.5 || angle >= 157.5) { + angle_str = "W"; + } + + FuriString* lat_str = furi_string_alloc(); + FuriString* lon_str = furi_string_alloc(); + + if(isnan(subghz_history_get_latitude(subghz->history, subghz->idx_menu_chosen))) { + furi_string_printf(lat_str, "N/A"); + } else { + furi_string_printf( + lat_str, + "%f", + (double)subghz_history_get_latitude(subghz->history, subghz->idx_menu_chosen)); + } + + if(isnan(subghz_history_get_longitude(subghz->history, subghz->idx_menu_chosen))) { + furi_string_printf(lon_str, "N/A"); + } else { + furi_string_printf( + lon_str, + "%f", + (double)subghz_history_get_longitude(subghz->history, subghz->idx_menu_chosen)); + } + + furi_string_printf( + text, + "Captured at: %s,\r\n%s Time: %s\r\n\r\nRealtime: Sats: %d\r\nDistance: %.2f%s Dir: %s\r\nGPS time: %02d:%02d:%02d UTC", + furi_string_get_cstr(lat_str), + furi_string_get_cstr(lon_str), + furi_string_get_cstr(time_text), + subghz->gps->satellites, + subghz->gps->satellites > 0 ? distance > 1 ? distance : distance * 1000 : 0, + distance > 1 ? "km" : "m", + angle_str, + subghz->gps->fix_hour, + subghz->gps->fix_minute, + subghz->gps->fix_second); + + widget_add_text_scroll_element(subghz->widget, 0, 0, 128, 64, furi_string_get_cstr(text)); + + furi_string_free(lat_str); + furi_string_free(lon_str); + + furi_string_free(text); + furi_string_free(time_text); +} + +void subghz_scene_show_gps_refresh_screen(void* context) { + SubGhz* subghz = context; + widget_reset(subghz->widget); + subghz_scene_show_gps_draw_satellites(subghz); +} + +void subghz_scene_show_gps_on_enter(void* context) { + SubGhz* subghz = context; + + subghz_scene_show_gps_draw_satellites(subghz); + + if(subghz->last_settings->gps_baudrate != 0) { + subghz->gps->timer = + furi_timer_alloc(subghz_scene_show_gps_refresh_screen, FuriTimerTypePeriodic, subghz); + furi_timer_start(subghz->gps->timer, 1000); + } +} + +bool subghz_scene_show_gps_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeTick) { + if(subghz->state_notifications == SubGhzNotificationStateRx) { + if(subghz->last_settings->gps_baudrate != 0) { + if(subghz->gps->satellites > 0) { + notification_message(subghz->notifications, &sequence_blink_green_10); + } else { + notification_message(subghz->notifications, &sequence_blink_red_10); + } + } else { + notification_message(subghz->notifications, &sequence_blink_cyan_10); + } + } + } + return false; +} + +void subghz_scene_show_gps_on_exit(void* context) { + SubGhz* subghz = context; + + if(subghz->last_settings->gps_baudrate != 0) { + furi_timer_stop(subghz->gps->timer); + furi_timer_free(subghz->gps->timer); + } + + widget_reset(subghz->widget); +} \ No newline at end of file diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 58ee69bd706..55f1c872f40 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include "../views/transmitter.h" #include +#include #include @@ -45,6 +46,12 @@ bool subghz_scene_transmitter_update_data_show(void* context) { return ret; } +void fav_timer_callback(void* context) { + SubGhz* subghz = context; + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewTransmitterSendStop); +} + void subghz_scene_transmitter_on_enter(void* context) { SubGhz* subghz = context; @@ -58,11 +65,18 @@ void subghz_scene_transmitter_on_enter(void* context) { subghz_view_transmitter_set_callback( subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz); - //Put the Listen after TX back to what the user selected.. - //subghz->ListenAfterTX = subghz->last_settings->enable_listen_after_tx; - subghz->state_notifications = SubGhzNotificationStateIDLE; view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); + + // Auto send and exit with favorites + if(subghz->fav_timeout) { + furi_check(!subghz->timer, "SubGhz fav timer exists"); + subghz->timer = furi_timer_alloc(fav_timer_callback, FuriTimerTypeOnce, subghz); + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewTransmitterSendStart); + furi_timer_start( + subghz->timer, CFW_SETTINGS()->favorite_timeout * furi_kernel_get_tick_frequency()); + } } bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { @@ -93,19 +107,11 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { subghz_txrx_stop(subghz->txrx); furi_hal_subghz_set_rolling_counter_mult(tmp_counter); } - - //Go back to the Scan/Repeater if the Listen After TX flag is on. - if(subghz->ListenAfterTX) { - if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneReceiver)) { - //Scene exists, go back to it - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneReceiver); - } else { - //Scene not started, start it now. - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); - } - }; - + if(subghz->fav_timeout) { + while(scene_manager_handle_back_event(subghz->scene_manager)) + ; + view_dispatcher_stop(subghz->view_dispatcher); + } return true; } else if(event.event == SubGhzCustomEventViewTransmitterBack) { subghz->state_notifications = SubGhzNotificationStateIDLE; diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 9ebc14caad8..622c4d45680 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -5,6 +5,10 @@ #include #include #include "subghz_i.h" +#include +#include + +#include "subghz_fap.h" #define TAG "SubGhzApp" @@ -58,6 +62,7 @@ static void subghz_load_custom_presets(SubGhzSetting* setting) { furi_assert(setting); const char* presets[][2] = { + // FM95 {"FM95", "02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"}, @@ -90,7 +95,7 @@ static void subghz_load_custom_presets(SubGhzSetting* setting) { #endif } -SubGhz* subghz_alloc(bool raw_send_only) { +SubGhz* subghz_alloc(bool alloc_for_tx_only) { SubGhz* subghz = malloc(sizeof(SubGhz)); subghz->file_path = furi_string_alloc(); @@ -119,27 +124,7 @@ SubGhz* subghz_alloc(bool raw_send_only) { #endif subghz->txrx = subghz_txrx_alloc(); - //Load settings from history now. - SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); - subghz_load_custom_presets(setting); - - // Load last used values for Read, Read RAW, etc. or default - subghz->last_settings = subghz_last_settings_alloc(); - size_t preset_count = subghz_setting_get_preset_count(setting); - subghz_last_settings_load(subghz->last_settings, preset_count); -#ifdef FURI_DEBUG - subghz_last_settings_log(subghz->last_settings); -#endif - - //If the user has Listen after TX on in the Config, we want to load as normal. - //That way, we can have all our functionality even if loaded from browser. - //If Listen after TX isnt on, the FAP will load as raw_send_only. - bool ListenAfterTX = subghz->last_settings->enable_listen_after_tx; - - //Put the Listen after TX back to what the user selected. - subghz->ListenAfterTX = ListenAfterTX; - - if(!raw_send_only || ListenAfterTX) { + if(!alloc_for_tx_only) { // SubMenu subghz->submenu = submenu_alloc(); view_dispatcher_add_view( @@ -156,7 +141,7 @@ SubGhz* subghz_alloc(bool raw_send_only) { subghz->popup = popup_alloc(); view_dispatcher_add_view( subghz->view_dispatcher, SubGhzViewIdPopup, popup_get_view(subghz->popup)); - if(!raw_send_only || ListenAfterTX) { + if(!alloc_for_tx_only) { // Text Input subghz->text_input = text_input_alloc(); view_dispatcher_add_view( @@ -185,7 +170,7 @@ SubGhz* subghz_alloc(bool raw_send_only) { subghz->view_dispatcher, SubGhzViewIdTransmitter, subghz_view_transmitter_get_view(subghz->subghz_transmitter)); - if(!raw_send_only || ListenAfterTX) { + if(!alloc_for_tx_only) { // Variable Item List subghz->variable_item_list = variable_item_list_alloc(); view_dispatcher_add_view( @@ -202,7 +187,7 @@ SubGhz* subghz_alloc(bool raw_send_only) { subghz_frequency_analyzer_get_view(subghz->subghz_frequency_analyzer)); } // Read RAW - subghz->subghz_read_raw = subghz_read_raw_alloc(raw_send_only); + subghz->subghz_read_raw = subghz_read_raw_alloc(alloc_for_tx_only); view_dispatcher_add_view( subghz->view_dispatcher, SubGhzViewIdReadRAW, @@ -214,7 +199,18 @@ SubGhz* subghz_alloc(bool raw_send_only) { //init TxRx & Protocol & History & KeyBoard subghz_unlock(subghz); - if(!raw_send_only || ListenAfterTX) { + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + + subghz_load_custom_presets(setting); + + // Load last used values for Read, Read RAW, etc. or default + subghz->last_settings = subghz_last_settings_alloc(); + size_t preset_count = subghz_setting_get_preset_count(setting); + subghz_last_settings_load(subghz->last_settings, preset_count); +#ifdef FURI_DEBUG + subghz_last_settings_log(subghz->last_settings); +#endif + if(!alloc_for_tx_only) { #if SUBGHZ_LAST_SETTING_SAVE_PRESET subghz_txrx_set_preset_internal( subghz->txrx, subghz->last_settings->frequency, subghz->last_settings->preset_index); @@ -228,23 +224,19 @@ SubGhz* subghz_alloc(bool raw_send_only) { subghz->secure_data = malloc(sizeof(SecureData)); - //Put the Speaker Back to what the user selected. - subghz_txrx_speaker_set_state( - subghz->txrx, - subghz->last_settings->enable_sound ? SubGhzSpeakerStateEnable : - SubGhzSpeakerStateDisable); - - if(!raw_send_only || ListenAfterTX) { + if(!alloc_for_tx_only) { + subghz->remove_duplicates = subghz->last_settings->remove_duplicates; subghz->ignore_filter = subghz->last_settings->ignore_filter; subghz->filter = subghz->last_settings->filter; } else { subghz->filter = SubGhzProtocolFlag_Decodable; subghz->ignore_filter = 0x0; + subghz->remove_duplicates = false; } subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz); - if(!raw_send_only || ListenAfterTX) { + if(!alloc_for_tx_only) { if(!float_is_equal(subghz->last_settings->rssi, 0)) { subghz_threshold_rssi_set(subghz->threshold_rssi, subghz->last_settings->rssi); } else { @@ -258,10 +250,16 @@ SubGhz* subghz_alloc(bool raw_send_only) { //Init Error_str subghz->error_str = furi_string_alloc(); + subghz->gps = subghz_gps_init(); + if(subghz->last_settings->gps_baudrate != 0) { + subghz_gps_set_baudrate(subghz->gps, subghz->last_settings->gps_baudrate); + subghz_gps_start(subghz->gps); + } + return subghz; } -void subghz_free(SubGhz* subghz, bool raw_send_only) { +void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) { furi_assert(subghz); if(subghz->rpc_ctx) { @@ -275,8 +273,7 @@ void subghz_free(SubGhz* subghz, bool raw_send_only) { subghz_txrx_stop(subghz->txrx); subghz_txrx_sleep(subghz->txrx); - bool ListenAfterTX = subghz->last_settings->enable_listen_after_tx; - if(!raw_send_only) { + if(!alloc_for_tx_only) { // Receiver view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReceiver); subghz_view_receiver_free(subghz->subghz_receiver); @@ -299,7 +296,7 @@ void subghz_free(SubGhz* subghz, bool raw_send_only) { // Transmitter view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); subghz_view_transmitter_free(subghz->subghz_transmitter); - if(!raw_send_only || ListenAfterTX) { + if(!alloc_for_tx_only) { // Variable Item List view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); variable_item_list_free(subghz->variable_item_list); @@ -311,7 +308,7 @@ void subghz_free(SubGhz* subghz, bool raw_send_only) { // Read RAW view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); subghz_read_raw_free(subghz->subghz_read_raw); - if(!raw_send_only || ListenAfterTX) { + if(!alloc_for_tx_only) { // Submenu view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdMenu); submenu_free(subghz->submenu); @@ -330,12 +327,10 @@ void subghz_free(SubGhz* subghz, bool raw_send_only) { furi_record_close(RECORD_GUI); subghz->gui = NULL; - subghz_last_settings_free(subghz->last_settings); - // threshold rssi subghz_threshold_rssi_free(subghz->threshold_rssi); - if(!raw_send_only || ListenAfterTX) { + if(!alloc_for_tx_only) { subghz_history_free(subghz->history); } @@ -355,23 +350,36 @@ void subghz_free(SubGhz* subghz, bool raw_send_only) { furi_string_free(subghz->file_path); furi_string_free(subghz->file_path_tmp); + // GPS + if(subghz->last_settings->gps_baudrate != 0) { + subghz_gps_stop(subghz->gps); + } + subghz_gps_deinit(subghz->gps); + + subghz_last_settings_free(subghz->last_settings); + // The rest free(subghz); } -int32_t subghz_app(void* p) { - bool raw_send_only; +int32_t subghz_app(char* p) { + bool alloc_for_tx; if(p && strlen(p)) { - raw_send_only = true; + alloc_for_tx = true; } else { - raw_send_only = false; + alloc_for_tx = false; } - SubGhz* subghz = subghz_alloc(raw_send_only); - subghz->raw_send_only = raw_send_only; - subghz->raw_send_only_old = raw_send_only; + SubGhz* subghz = subghz_alloc(alloc_for_tx); + + if(alloc_for_tx) { + subghz->raw_send_only = true; + } else { + subghz->raw_send_only = false; + } // Check argument and run corresponding scene + bool is_favorite = process_favorite_launch(&p) && CFW_SETTINGS()->favorite_timeout; if(p && strlen(p)) { uint32_t rpc_ctx = 0; @@ -388,6 +396,7 @@ int32_t subghz_app(void* p) { if(subghz_key_load(subghz, p, true)) { furi_string_set(subghz->file_path, (const char*)p); + subghz->fav_timeout = is_favorite; if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) { //Load Raw TX subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); @@ -422,9 +431,14 @@ int32_t subghz_app(void* p) { view_dispatcher_run(subghz->view_dispatcher); + if(subghz->timer) { + furi_timer_stop(subghz->timer); + furi_timer_free(subghz->timer); + } + furi_hal_power_suppress_charge_exit(); - subghz_free(subghz, subghz->raw_send_only_old); + subghz_free(subghz, alloc_for_tx); return 0; } diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 116b634743a..b4370fd3d7b 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -936,7 +936,8 @@ static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { furi_string_free(source); } -static void subghz_cli_command_chat(Cli* cli, FuriString* args) { +static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { + UNUSED(context); uint32_t frequency = 433920000; uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT @@ -1135,7 +1136,7 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "chat") == 0) { - subghz_cli_command_chat(cli, args); + subghz_cli_command_chat(cli, args, NULL); break; } diff --git a/applications/main/subghz/subghz_extended_freq.c b/applications/main/subghz/subghz_extended_freq.c new file mode 100644 index 00000000000..7f46329cb04 --- /dev/null +++ b/applications/main/subghz/subghz_extended_freq.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include + +void subghz_extended_freq() { + bool is_extended_i = false; + bool is_bypassed_i = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + if(flipper_format_file_open_existing(file, "/ext/subghz/assets/extend_range.txt")) { + flipper_format_read_bool(file, "use_ext_range_at_own_risk", &is_extended_i, 1); + flipper_format_read_bool(file, "ignore_default_tx_region", &is_bypassed_i, 1); + } + + furi_hal_subghz_set_extended_frequency(is_extended_i); + furi_hal_subghz_set_bypassed_frequency(is_bypassed_i); + + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + // Load external module power amp setting (TODO: move to other place) + // TODO: Disable this when external module is not CC1101 E07 + SubGhzLastSettings* last_settings = subghz_last_settings_alloc(); + subghz_last_settings_load(last_settings, 0); + + // Set globally in furi hal + furi_hal_subghz_set_ext_power_amp(last_settings->external_module_power_amp); + + subghz_last_settings_free(last_settings); +} diff --git a/applications/main/subghz/subghz_fap.c b/applications/main/subghz/subghz_fap.c new file mode 100644 index 00000000000..2ae06cfa133 --- /dev/null +++ b/applications/main/subghz/subghz_fap.c @@ -0,0 +1,5 @@ +#include "subghz_fap.h" + +int32_t subghz_fap(char* p) { + return subghz_app(p); +} diff --git a/applications/main/subghz/subghz_fap.h b/applications/main/subghz/subghz_fap.h new file mode 100644 index 00000000000..92f2c44b3bc --- /dev/null +++ b/applications/main/subghz/subghz_fap.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t subghz_app(char* p); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 2f0371985b0..851c90168a9 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -1,10 +1,11 @@ #include "subghz_history.h" #include +#include #include -#define SUBGHZ_HISTORY_MAX 55 -#define SUBGHZ_HISTORY_FREE_HEAP 20480 +#define SUBGHZ_HISTORY_MAX 65535 // uint16_t index max, ram limit below +#define SUBGHZ_HISTORY_FREE_HEAP (10240 * (3 - MIN(rpc_get_sessions_count(instance->rpc), 2U))) #define TAG "SubGhzHistory" typedef struct { @@ -13,6 +14,11 @@ typedef struct { uint8_t type; SubGhzRadioPreset* preset; FuriHalRtcDateTime datetime; + uint32_t hash_data; + const SubGhzProtocol* protocol; + uint16_t repeats; + float latitude; + float longitude; } SubGhzHistoryItem; ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) @@ -26,9 +32,10 @@ typedef struct { struct SubGhzHistory { uint32_t last_update_timestamp; uint16_t last_index_write; - uint8_t code_last_hash_data; + uint32_t code_last_hash_data; FuriString* tmp_string; SubGhzHistoryStruct* history; + Rpc* rpc; }; SubGhzHistory* subghz_history_alloc(void) { @@ -36,6 +43,7 @@ SubGhzHistory* subghz_history_alloc(void) { instance->tmp_string = furi_string_alloc(); instance->history = malloc(sizeof(SubGhzHistoryStruct)); SubGhzHistoryItemArray_init(instance->history->data); + instance->rpc = furi_record_open(RECORD_RPC); return instance; } @@ -52,9 +60,28 @@ void subghz_history_free(SubGhzHistory* instance) { } SubGhzHistoryItemArray_clear(instance->history->data); free(instance->history); + furi_record_close(RECORD_RPC); free(instance); } +uint32_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->hash_data; +} + +const SubGhzProtocol* subghz_history_get_protocol(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->protocol; +} + +uint16_t subghz_history_get_repeats(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->repeats; +} + uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); @@ -73,6 +100,18 @@ const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) { return furi_string_get_cstr(item->preset->name); } +float subghz_history_get_latitude(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->latitude; +} + +float subghz_history_get_longitude(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->longitude; +} + void subghz_history_reset(SubGhzHistory* instance) { furi_assert(instance); furi_string_reset(instance->tmp_string); @@ -150,18 +189,37 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx return NULL; } } -bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) { +bool subghz_history_get_text_space_left( + SubGhzHistory* instance, + FuriString* output, + uint8_t sats, + bool ignore_full) { furi_assert(instance); - if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { - if(output != NULL) furi_string_printf(output, " Free heap LOW"); - return true; + if(!ignore_full) { + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { + if(output != NULL) furi_string_printf(output, " Memory is FULL"); + return true; + } + if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { + if(output != NULL) furi_string_printf(output, " History is FULL"); + return true; + } } - if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, " Memory is FULL"); - return true; + if(output != NULL) { + if(sats == 0) { + furi_string_printf(output, "%02u", instance->last_index_write); + return false; + } else { + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + + if(furi_hal_rtc_datetime_to_timestamp(&datetime) % 2) { + furi_string_printf(output, "%02u", instance->last_index_write); + } else { + furi_string_printf(output, "%d sats", sats); + } + } } - if(output != NULL) - furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); return false; } @@ -186,18 +244,29 @@ bool subghz_history_add_to_history( furi_assert(instance); furi_assert(context); - if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false; - if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; + if(subghz_history_full(instance)) return false; SubGhzProtocolDecoderBase* decoder_base = context; - if((instance->code_last_hash_data == - subghz_protocol_decoder_base_get_hash_data(decoder_base)) && + uint32_t hash_data = subghz_protocol_decoder_base_get_hash_data_long(decoder_base); + if((instance->code_last_hash_data == hash_data) && ((furi_get_tick() - instance->last_update_timestamp) < 500)) { instance->last_update_timestamp = furi_get_tick(); return false; } - instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + uint16_t repeats = 0; + SubGhzHistoryItemArray_it_t it; + SubGhzHistoryItemArray_it_last(it, instance->history->data); + while(!SubGhzHistoryItemArray_end_p(it)) { + SubGhzHistoryItem* search = SubGhzHistoryItemArray_ref(it); + if(search->hash_data == hash_data && search->protocol == decoder_base->protocol) { + repeats = search->repeats + 1; + break; + } + SubGhzHistoryItemArray_previous(it); + } + + instance->code_last_hash_data = hash_data; instance->last_update_timestamp = furi_get_tick(); FuriString* text = furi_string_alloc(); @@ -210,6 +279,11 @@ bool subghz_history_add_to_history( item->preset->data = preset->data; item->preset->data_size = preset->data_size; furi_hal_rtc_get_datetime(&item->datetime); + item->hash_data = hash_data; + item->protocol = decoder_base->protocol; + item->repeats = repeats; + item->latitude = preset->latitude; + item->longitude = preset->longitude; item->item_str = furi_string_alloc(); item->flipper_string = flipper_format_string_alloc(); @@ -276,3 +350,32 @@ bool subghz_history_add_to_history( instance->last_index_write++; return true; } + +void subghz_history_remove_duplicates(SubGhzHistory* instance) { + furi_assert(instance); + + SubGhzHistoryItemArray_it_t it; + SubGhzHistoryItemArray_it_last(it, instance->history->data); + while(!SubGhzHistoryItemArray_end_p(it)) { + SubGhzHistoryItem* i = SubGhzHistoryItemArray_ref(it); + + SubGhzHistoryItemArray_it_t jt; + SubGhzHistoryItemArray_it_set(jt, it); + SubGhzHistoryItemArray_previous(jt); + while(!SubGhzHistoryItemArray_end_p(jt)) { + SubGhzHistoryItem* j = SubGhzHistoryItemArray_ref(jt); + + if(j->hash_data == i->hash_data && j->protocol == i->protocol) { + subghz_history_delete_item(instance, jt->index); + } + SubGhzHistoryItemArray_previous(jt); + } + SubGhzHistoryItemArray_previous(it); + } +} + +bool subghz_history_full(SubGhzHistory* instance) { + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return true; + if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return true; + return false; +} diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 2e940a8d891..90f0ca00fdc 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -29,6 +29,30 @@ void subghz_history_reset(SubGhzHistory* instance); void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx); +/** Get hash data to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - record index + * @return hash - hash data + */ +uint32_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx); + +/** Get protocol pointer to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - record index + * @return proto - protocol pointer + */ +const SubGhzProtocol* subghz_history_get_protocol(SubGhzHistory* instance, uint16_t idx); + +/** Get repeat count to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - Record index + * @return repeats - uint16_t repeat count +*/ +uint16_t subghz_history_get_repeats(SubGhzHistory* instance, uint16_t idx); + /** Get frequency to history[idx] * * @param instance - SubGhzHistory instance @@ -98,9 +122,15 @@ void subghz_history_get_time_item_menu(SubGhzHistory* instance, FuriString* outp * * @param instance - SubGhzHistory instance * @param output - FuriString* output + * @param sats - Number of satellites + * @param ignore_full - Ignore if history is full * @return bool - is FULL */ -bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output); +bool subghz_history_get_text_space_left( + SubGhzHistory* instance, + FuriString* output, + uint8_t sats, + bool ignore_full); /** Return last index * @@ -128,3 +158,25 @@ bool subghz_history_add_to_history( * @return SubGhzProtocolCommonLoad* */ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx); + +/** Get latitude to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - Record index + * @return latitude - Float +*/ +float subghz_history_get_latitude(SubGhzHistory* instance, uint16_t idx); + +/** Get longitude to history[idx] + * + * @param instance - SubGhzHistory instance + * @param idx - Record index + * @return longitude - Float +*/ +float subghz_history_get_longitude(SubGhzHistory* instance, uint16_t idx); + +// Consolidate history removing existing duplicates +void subghz_history_remove_duplicates(SubGhzHistory* instance); + +// Check if memory/history is full +bool subghz_history_full(SubGhzHistory* instance); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 0715d64485e..c60bab107d1 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -1,6 +1,6 @@ #include "subghz_i.h" -#include +#include "assets_icons.h" #include "subghz/types.h" #include #include @@ -50,8 +50,8 @@ void subghz_dialog_message_freq_error(SubGhz* subghz, bool only_rx) { message_text = "Frequency\nis outside of\ndefault range.\nCheck docs."; } - dialog_message_set_header(message, header_text, 63, 0, AlignCenter, AlignTop); - dialog_message_set_text(message, message_text, 1, 13, AlignLeft, AlignTop); + dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop); + dialog_message_set_text(message, message_text, 3, 23, AlignLeft, AlignTop); dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22); @@ -71,6 +71,8 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; FuriString* temp_str = furi_string_alloc(); uint32_t temp_data32; + float temp_lat = NAN; // NAN or 0.0?? because 0.0 is valid value + float temp_lon = NAN; do { stream_clean(fff_data_stream); @@ -136,12 +138,24 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { break; } } + + //Load latitute and longitude if present, strict mode to avoid reading the whole file twice + flipper_format_set_strict_mode(fff_data_file, true); + if(!flipper_format_read_float(fff_data_file, "Latitute", (float*)&temp_lat, 1) || + !flipper_format_read_float(fff_data_file, "Longitude", (float*)&temp_lon, 1)) { + FURI_LOG_W(TAG, "Missing Latitude and Longitude (optional)"); + flipper_format_rewind(fff_data_file); + } + flipper_format_set_strict_mode(fff_data_file, false); + size_t preset_index = subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str)); subghz_txrx_set_preset( subghz->txrx, furi_string_get_cstr(temp_str), temp_data32, + temp_lat, + temp_lon, subghz_setting_get_preset_data(setting, preset_index), subghz_setting_get_preset_data_size(setting, preset_index)); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index b97df6898c0..7b67ec34371 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -10,6 +10,7 @@ #include "views/subghz_read_raw.h" #include +#include #include #include #include @@ -38,6 +39,7 @@ #include "helpers/subghz_threshold_rssi.h" #include "helpers/subghz_txrx.h" +#include "helpers/subghz_gps.h" #define SUBGHZ_MAX_LEN_NAME 64 #define SUBGHZ_EXT_PRESET_NAME true @@ -77,7 +79,6 @@ struct SubGhz { SubGhzFrequencyAnalyzer* subghz_frequency_analyzer; SubGhzReadRAW* subghz_read_raw; bool raw_send_only; - bool raw_send_only_old; bool save_datetime_set; FuriHalRtcDateTime save_datetime; @@ -85,16 +86,10 @@ struct SubGhz { SubGhzLastSettings* last_settings; SubGhzProtocolFlag filter; - SubGhzProtocolFlag ignore_filter; + SubGhzProtocolFilter ignore_filter; + bool remove_duplicates; FuriString* error_str; SubGhzLock lock; - SubGhzRepeater repeater; - FlipperFormat* repeater_tx; - uint32_t RepeaterTXLength; - uint32_t RepeaterStartTime; - VariableItem* BIN_Raw_menu; - bool BINRawStateChanged; - bool ListenAfterTX; SecureData* secure_data; @@ -103,10 +98,16 @@ struct SubGhz { SubGhzThresholdRssi* threshold_rssi; SubGhzRxKeyState rx_key_state; SubGhzHistory* history; + SubGhzGPS* gps; + SubGhzRepeaterState repeater; + bool repeater_bin_raw_was_off; uint16_t idx_menu_chosen; SubGhzLoadTypeFile load_type_file; + bool fav_timeout; + FuriTimer* timer; + void* rpc_ctx; }; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 02e292fff1f..0ab6213067a 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -13,16 +13,18 @@ #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER "ExtPower" -#define SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES "TimestampNames" +#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES "TimestampNames" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP "ExtPowerAmp" +#define SUBGHZ_LAST_SETTING_FIELD_GPS "Gps" #define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping" +#define SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES "RemoveDuplicates" #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" #define SUBGHZ_LAST_SETTING_FIELD_REPEATER "Repeater" -#define SUBGHZ_LAST_SETTING_FIELD_LISTEN_AFTER_TX "ListenAfterTX" -#define SUBGHZ_LAST_SETTING_FIELD_SOUND "Sound" +#define SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND "Sound" #define SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD "DelOldSignals" +#define SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE "Autosave" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -46,12 +48,13 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool temp_external_module_enabled = false; bool temp_external_module_power_5v_disable = false; bool temp_external_module_power_amp = false; - bool temp_timestamp_file_names = false; + bool temp_protocol_file_names = false; bool temp_enable_hopping = false; - bool temp_enable_listen_after_tx = false; bool temp_enable_sound = false; - uint32_t temp_RepeaterState = false; + uint32_t temp_repeater_state; + bool temp_remove_duplicates = false; bool temp_delete_old_sig = false; + bool temp_autosave = false; uint32_t temp_ignore_filter = 0; uint32_t temp_filter = 0; float temp_rssi = 0; @@ -61,8 +64,13 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool rssi_was_read = false; bool filter_was_read = false; bool ignore_filter_was_read = false; + bool remove_duplicates_was_read = false; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; + bool repeater_was_read = false; + bool enable_sound_was_read = false; + + uint32_t temp_gps_baudrate = 0; if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH && flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) { @@ -92,14 +100,16 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); flipper_format_read_bool( fff_data_file, - SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, - (bool*)&temp_timestamp_file_names, + SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES, + (bool*)&temp_protocol_file_names, 1); flipper_format_read_bool( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP, (bool*)&temp_external_module_power_amp, 1); + flipper_format_read_uint32( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_GPS, (uint32_t*)&temp_gps_baudrate, 1); flipper_format_read_bool( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE, @@ -107,6 +117,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); rssi_was_read = flipper_format_read_float( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, (float*)&temp_rssi, 1); + remove_duplicates_was_read = flipper_format_read_bool( + fff_data_file, + SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES, + (bool*)&temp_remove_duplicates, + 1); ignore_filter_was_read = flipper_format_read_uint32( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER, @@ -114,17 +129,15 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); filter_was_read = flipper_format_read_uint32( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, (uint32_t*)&temp_filter, 1); - flipper_format_read_uint32( - fff_data_file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, (uint32_t*)&temp_RepeaterState, 1); - flipper_format_read_bool( - fff_data_file, - SUBGHZ_LAST_SETTING_FIELD_LISTEN_AFTER_TX, - (bool*)&temp_enable_listen_after_tx, - 1); - flipper_format_read_bool( - fff_data_file, SUBGHZ_LAST_SETTING_FIELD_SOUND, (bool*)&temp_enable_sound, 1); + repeater_was_read = flipper_format_read_uint32( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, (uint32_t*)&temp_repeater_state, 1); + enable_sound_was_read = flipper_format_read_bool( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, (bool*)&temp_enable_sound, 1); flipper_format_read_bool( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, (bool*)&temp_delete_old_sig, 1); + flipper_format_read_bool( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE, (bool*)&temp_autosave, 1); + } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); } @@ -138,17 +151,19 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; instance->external_module_enabled = false; - instance->timestamp_file_names = false; + instance->protocol_file_names = false; instance->external_module_power_amp = false; + instance->gps_baudrate = 0; instance->enable_hopping = false; - instance->enable_listen_after_tx = false; - instance->enable_sound = false; + instance->remove_duplicates = false; + instance->repeater_state = 0; + instance->enable_sound = 0; instance->delete_old_signals = false; + instance->autosave = false; instance->ignore_filter = 0x00; // See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c instance->filter = SubGhzProtocolFlag_Decodable; instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN; - instance->RepeaterState = SubGhzRepeaterOff; } else { instance->frequency = temp_frequency; instance->frequency_analyzer_feedback_level = @@ -177,18 +192,20 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->external_module_power_5v_disable = temp_external_module_power_5v_disable; - instance->timestamp_file_names = temp_timestamp_file_names; + instance->protocol_file_names = temp_protocol_file_names; instance->delete_old_signals = temp_delete_old_sig; + instance->autosave = temp_autosave; + // External power amp CC1101 instance->external_module_power_amp = temp_external_module_power_amp; instance->rssi = rssi_was_read ? temp_rssi : SUBGHZ_RAW_THRESHOLD_MIN; instance->enable_hopping = temp_enable_hopping; - instance->enable_listen_after_tx = temp_enable_listen_after_tx; - instance->enable_sound = temp_enable_sound; - instance->RepeaterState = temp_RepeaterState; + instance->repeater_state = repeater_was_read ? temp_repeater_state : 0; + instance->enable_sound = enable_sound_was_read ? temp_enable_sound : false; + instance->remove_duplicates = remove_duplicates_was_read ? temp_remove_duplicates : false; instance->ignore_filter = ignore_filter_was_read ? temp_ignore_filter : 0x00; #if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW instance->filter = filter_was_read ? temp_filter : SubGhzProtocolFlag_Decodable; @@ -203,6 +220,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count #endif // Set globally in furi hal furi_hal_subghz_set_ext_power_amp(instance->external_module_power_amp); + + instance->gps_baudrate = temp_gps_baudrate; } flipper_format_file_close(fff_data_file); @@ -270,8 +289,8 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { } if(!flipper_format_insert_or_update_bool( file, - SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, - &instance->timestamp_file_names, + SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES, + &instance->protocol_file_names, 1)) { break; } @@ -282,6 +301,10 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { 1)) { break; } + if(!flipper_format_insert_or_update_uint32( + file, SUBGHZ_LAST_SETTING_FIELD_GPS, &instance->gps_baudrate, 1)) { + break; + } if(!flipper_format_insert_or_update_bool( file, SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE, &instance->enable_hopping, 1)) { break; @@ -290,6 +313,13 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, &instance->rssi, 1)) { break; } + if(!flipper_format_insert_or_update_bool( + file, + SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES, + &instance->remove_duplicates, + 1)) { + break; + } if(!flipper_format_insert_or_update_uint32( file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER, &instance->ignore_filter, 1)) { break; @@ -299,22 +329,19 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { break; } if(!flipper_format_insert_or_update_uint32( - file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, &instance->RepeaterState, 1)) { + file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, &instance->repeater_state, 1)) { break; } if(!flipper_format_insert_or_update_bool( - file, - SUBGHZ_LAST_SETTING_FIELD_LISTEN_AFTER_TX, - &instance->enable_listen_after_tx, - 1)) { + file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, &instance->enable_sound, 1)) { break; } if(!flipper_format_insert_or_update_bool( - file, SUBGHZ_LAST_SETTING_FIELD_SOUND, &instance->enable_sound, 1)) { + file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, &instance->delete_old_signals, 1)) { break; } if(!flipper_format_insert_or_update_bool( - file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, &instance->delete_old_signals, 1)) { + file, SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE, &instance->autosave, 1)) { break; } saved = true; @@ -349,29 +376,35 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { FURI_LOG_I( TAG, "Frequency: %03ld.%02ld, FeedbackLevel: %ld, FATrigger: %.2f, External: %s, ExtPower: %s, TimestampNames: %s, ExtPowerAmp: %s,\n" - "Hopping: %s,\nPreset: %ld, RSSI: %.2f, " - "Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, BinRAW: %s, Repeater: %lu, ListenAfterTX %s, Sound: %s", + "GPSBaudrate: %ld, Hopping: %s,\nPreset: %ld, RSSI: %.2f, " + "BinRAW: %s, Repeater: %lu, Duplicates: %s, Autosave: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s, Sound: %s", instance->frequency / 1000000 % 1000, instance->frequency / 10000 % 100, instance->frequency_analyzer_feedback_level, (double)instance->frequency_analyzer_trigger, bool_to_char(instance->external_module_enabled), bool_to_char(instance->external_module_power_5v_disable), - bool_to_char(instance->timestamp_file_names), + bool_to_char(instance->protocol_file_names), bool_to_char(instance->external_module_power_amp), + instance->gps_baudrate, bool_to_char(instance->enable_hopping), instance->preset_index, (double)instance->rssi, + subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW), + instance->repeater_state, + bool_to_char(instance->remove_duplicates), + bool_to_char(instance->autosave), subghz_last_settings_log_filter_get_index( - instance->ignore_filter, SubGhzProtocolFlag_StarLine), + instance->ignore_filter, SubGhzProtocolFilter_StarLine), subghz_last_settings_log_filter_get_index( - instance->ignore_filter, SubGhzProtocolFlag_AutoAlarms), + instance->ignore_filter, SubGhzProtocolFilter_AutoAlarms), subghz_last_settings_log_filter_get_index( - instance->ignore_filter, SubGhzProtocolFlag_Magellan), + instance->ignore_filter, SubGhzProtocolFilter_Magellan), subghz_last_settings_log_filter_get_index( - instance->ignore_filter, SubGhzProtocolFlag_NiceFlorS), - subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW), - instance->RepeaterState, - bool_to_char(instance->enable_listen_after_tx), + instance->ignore_filter, SubGhzProtocolFilter_NiceFlorS), + subghz_last_settings_log_filter_get_index( + instance->ignore_filter, SubGhzProtocolFilter_Weather), + subghz_last_settings_log_filter_get_index( + instance->ignore_filter, SubGhzProtocolFilter_TPMS), bool_to_char(instance->enable_sound)); } diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index 7e9e2b5bc0e..3c03c0f116a 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -25,16 +25,17 @@ typedef struct { bool external_module_power_5v_disable; bool external_module_power_amp; // saved so as not to change the version - bool timestamp_file_names; + bool protocol_file_names; + uint32_t gps_baudrate; bool enable_hopping; - bool enable_listen_after_tx; + uint32_t repeater_state; bool enable_sound; - - uint32_t RepeaterState; + bool remove_duplicates; uint32_t ignore_filter; uint32_t filter; float rssi; bool delete_old_signals; + bool autosave; } SubGhzLastSettings; SubGhzLastSettings* subghz_last_settings_alloc(void); diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 31e4ae34064..8c060e4bbc1 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -1,10 +1,10 @@ #include "receiver.h" #include "../subghz_i.h" -#include #include #include #include +#include #include #define FRAME_HEIGHT 12 @@ -20,6 +20,7 @@ typedef struct { FuriString* item_str; FuriString* time; uint8_t type; + uint16_t repeats; } SubGhzReceiverMenuItem; ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST) @@ -64,7 +65,7 @@ typedef struct { FuriString* progress_str; bool hopping_enabled; bool bin_raw_enabled; - bool repeater_enabled; + SubGhzRepeaterState repeater_state; SubGhzReceiverHistory* history; uint16_t idx; uint16_t list_offset; @@ -176,7 +177,8 @@ void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, const char* time, - uint8_t type) { + uint8_t type, + uint16_t repeats) { furi_assert(subghz_receiver); with_view_model( subghz_receiver->view, @@ -187,6 +189,7 @@ void subghz_view_receiver_add_item_to_menu( item_menu->time = furi_string_alloc_set(time); item_menu->item_str = furi_string_alloc_set(name); item_menu->type = type; + item_menu->repeats = repeats; if((model->idx == model->history_item - 1)) { model->history_item++; model->idx++; @@ -206,7 +209,7 @@ void subghz_view_receiver_add_data_statusbar( const char* history_stat_str, bool hopping_enabled, bool bin_raw_enabled, - bool repeater_enabled) { + SubGhzRepeaterState repeater_state) { furi_assert(subghz_receiver); with_view_model( subghz_receiver->view, @@ -217,7 +220,7 @@ void subghz_view_receiver_add_data_statusbar( furi_string_set(model->history_stat_str, history_stat_str); model->hopping_enabled = hopping_enabled; model->bin_raw_enabled = bin_raw_enabled; - model->repeater_enabled = repeater_enabled; + model->repeater_state = repeater_state; }, true); } @@ -296,7 +299,15 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { if(item_menu->type == 0) { break; } - furi_string_set(str_buff, item_menu->item_str); + if(item_menu->repeats) { + furi_string_printf( + str_buff, + "x%u: %s", + item_menu->repeats + 1, + furi_string_get_cstr(item_menu->item_str)); + } else { + furi_string_set(str_buff, item_menu->item_str); + } if(model->idx == idx) { subghz_view_receiver_draw_frame(canvas, i, scrollbar); if(model->show_time) { @@ -328,14 +339,13 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { (model->device_type == SubGhzRadioDeviceTypeInternal) ? &I_Scanning_123x52 : &I_Fishing_123x52); canvas_set_font(canvas, FontPrimary); - if(model->repeater_enabled) { + if(model->repeater_state != SubGhzRepeaterStateOff) { canvas_draw_str(canvas, 59, 46, "Repeater..."); } else if(model->hopping_enabled) { canvas_draw_str(canvas, 59, 46, "Hopper scan..."); } else { canvas_draw_str(canvas, 59, 46, "Fixed scan..."); } - //canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); @@ -389,7 +399,16 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { #else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); #endif - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + if(!furi_string_empty(model->history_stat_str)) { + canvas_draw_str_aligned( + canvas, + 114, + 62, + AlignRight, + AlignBottom, + furi_string_get_cstr(model->history_stat_str)); + canvas_draw_icon(canvas, 116, 53, &I_sub1_10px); + } canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 8, 99, 48); elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); @@ -425,7 +444,16 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { #else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); #endif - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + if(!furi_string_empty(model->history_stat_str)) { + canvas_draw_str_aligned( + canvas, + 114, + 62, + AlignRight, + AlignBottom, + furi_string_get_cstr(model->history_stat_str)); + canvas_draw_icon(canvas, 116, 53, &I_sub1_10px); + } } break; } } @@ -523,25 +551,35 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { false); consumed = true; } else if(event->key == InputKeyOk) { - with_view_model( - subghz_receiver->view, - SubGhzViewReceiverModel * model, - { - if(model->history_item != 0) { - if(event->type == InputTypeShort) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOK, subghz_receiver->context); - } else if(event->type == InputTypeLong) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOKLong, subghz_receiver->context); - } else if(event->type == InputTypeRelease) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOKRelease, subghz_receiver->context); + SubGhzCustomEvent new_event; + + switch(event->type) { + case InputTypeShort: + new_event = SubGhzCustomEventViewReceiverOK; + break; + case InputTypeLong: + new_event = SubGhzCustomEventViewReceiverOKLong; + break; + case InputTypeRelease: + new_event = SubGhzCustomEventViewReceiverOKRelease; + break; + default: + new_event = 0; + break; + } + + if(new_event) { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + if(model->history_item != 0) { + subghz_receiver->callback(new_event, subghz_receiver->context); } - } - }, - false); - consumed = true; + }, + false); + consumed = true; + } } if(consumed) { @@ -571,6 +609,7 @@ void subghz_view_receiver_exit(void* context) { furi_string_free(item_menu->item_str); furi_string_free(item_menu->time); item_menu->type = 0; + item_menu->repeats = 0; } SubGhzReceiverMenuItemArray_reset(model->history->data); model->idx = 0; @@ -579,7 +618,7 @@ void subghz_view_receiver_exit(void* context) { model->nodraw = false; model->hopping_enabled = false; model->bin_raw_enabled = false; - model->repeater_enabled = false; + model->repeater_state = SubGhzRepeaterStateOff; }, false); furi_timer_stop(subghz_receiver->timer); @@ -618,7 +657,6 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { model->history = malloc(sizeof(SubGhzReceiverHistory)); model->hopping_enabled = false; model->bin_raw_enabled = false; - model->repeater_enabled = false; SubGhzReceiverMenuItemArray_init(model->history->data); }, true); @@ -645,6 +683,7 @@ void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { furi_string_free(item_menu->item_str); furi_string_free(item_menu->time); item_menu->type = 0; + item_menu->repeats = 0; } SubGhzReceiverMenuItemArray_clear(model->history->data); free(model->history); @@ -681,6 +720,7 @@ void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint1 furi_string_free(item->item_str); furi_string_free(item->time); item->type = 0; + item->repeats = 0; SubGhzReceiverMenuItemArray_remove_v(model->history->data, idx, idx + 1); if(model->history_item == 5) { @@ -696,6 +736,7 @@ void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint1 } }, true); + subghz_view_receiver_update_offset(subghz_receiver); } void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver) { @@ -718,9 +759,9 @@ void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint subghz_receiver->view, SubGhzViewReceiverModel * model, { - model->idx = idx; + model->idx = CLAMP(idx, model->history_item ? model->history_item - 1 : 0, 0); if(model->idx > 2) model->list_offset = idx - 2; }, true); subghz_view_receiver_update_offset(subghz_receiver); -} \ No newline at end of file +} diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index ce64541039d..9d66b8b4a3b 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -34,7 +34,7 @@ void subghz_view_receiver_add_data_statusbar( const char* history_stat_str, bool hopping_enabled, bool bin_raw_enabled, - bool repeater_enabled); + SubGhzRepeaterState repeater_enabled); void subghz_view_receiver_set_radio_device_type( SubGhzViewReceiver* subghz_receiver, @@ -48,7 +48,8 @@ void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, const char* time, - uint8_t type); + uint8_t type, + uint16_t repeats); uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver); diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 260605d863f..71010dea7e9 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -136,8 +136,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { SubGhzViewTransmitter* subghz_transmitter = context; bool can_be_sent = false; - if(event->key == InputKeyBack && event->type == InputTypeLong) { - // Reset view model + if(event->key == InputKeyBack && event->type == InputTypeShort) { with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, diff --git a/applications/settings/cfw_app/cfw_app.c b/applications/settings/cfw_app/cfw_app.c index 414f0e16b06..b27e9be6b25 100644 --- a/applications/settings/cfw_app/cfw_app.c +++ b/applications/settings/cfw_app/cfw_app.c @@ -62,7 +62,7 @@ bool cfw_app_apply(CfwApp* app) { while(flipper_format_delete_key(file, "Frequency")) ; FrequencyList_it(it, app->subghz_static_freqs); - for(size_t i = 0; i < FrequencyList_size(app->subghz_static_freqs); i++) { + for(size_t i = 0; i < FrequencyList_size(app->subghz_static_freqs); i++) { flipper_format_write_uint32( file, "Frequency", FrequencyList_get(app->subghz_static_freqs, i), 1); } @@ -70,7 +70,7 @@ bool cfw_app_apply(CfwApp* app) { if(!flipper_format_rewind(file)) break; while(flipper_format_delete_key(file, "Hopper_frequency")) ; - for(size_t i = 0; i < FrequencyList_size(app->subghz_hopper_freqs); i++) { + for(size_t i = 0; i < FrequencyList_size(app->subghz_hopper_freqs); i++) { flipper_format_write_uint32( file, "Hopper_frequency", FrequencyList_get(app->subghz_hopper_freqs, i), 1); } @@ -79,7 +79,24 @@ bool cfw_app_apply(CfwApp* app) { } if(app->save_subghz) { - furi_hal_subghz_set_extend_settings(app->subghz_extend, app->subghz_bypass); + FlipperFormat* file = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_always(file, "/ext/subghz/assets/extend_range.txt")) + break; + if(!flipper_format_write_header_cstr(file, "Flipper SubGhz Setting File", 1)) break; + if(!flipper_format_write_comment_cstr( + file, "Whether to allow extended ranges that can break your flipper")) + break; + if(!flipper_format_write_bool( + file, "use_ext_range_at_own_risk", &app->subghz_extend, 1)) + break; + if(!flipper_format_write_comment_cstr( + file, "Whether to ignore the default TX region settings")) + break; + if(!flipper_format_write_bool(file, "ignore_default_tx_region", &app->subghz_bypass, 1)) + break; + } while(0); + flipper_format_free(file); } if(app->save_name) { @@ -250,9 +267,14 @@ CfwApp* cfw_app_alloc() { } } while(false); flipper_format_free(file); - furi_record_close(RECORD_STORAGE); - furi_hal_subghz_get_extend_settings(&app->subghz_extend, &app->subghz_bypass); + file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, "/ext/subghz/assets/extend_range.txt")) { + flipper_format_read_bool(file, "use_ext_range_at_own_risk", &app->subghz_extend, 1); + flipper_format_read_bool(file, "ignore_default_tx_region", &app->subghz_bypass, 1); + } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); strlcpy(app->device_name, furi_hal_version_get_name_ptr(), FURI_HAL_VERSION_ARRAY_NAME_LENGTH); diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index 85d915acdcc..b71d78ff0a9 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -78,6 +78,20 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SNOP); } +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us) { + bool result = false; + CC1101Status status = {0}; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us); + while(!furi_hal_cortex_timer_is_expired(timer)) { + status = cc1101_strobe(handle, CC1101_STROBE_SNOP); + if(status.STATE == state) { + result = true; + break; + } + } + return result; +} + CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SPWD); } diff --git a/lib/drivers/cc1101.h b/lib/drivers/cc1101.h index d8ee05d5289..c8c552becee 100644 --- a/lib/drivers/cc1101.h +++ b/lib/drivers/cc1101.h @@ -59,6 +59,16 @@ CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); */ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); +/** Wait specific chip state + * + * @param handle The SPI bus handle + * @param[in] state The state to wait + * @param[in] timeout_us The timeout in microseconds + * + * @return true on success, false otherwise + */ +bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us); + /** Enable shutdown mode * * @param handle - pointer to FuriHalSpiHandle diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 43694a0b2f5..809c847eaac 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -263,11 +263,12 @@ const SubGhzProtocol subghz_protocol_kia = { .name = SUBGHZ_PROTOCOL_KIA_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | - SubGhzProtocolFlag_AutoAlarms, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_kia_decoder, .encoder = &subghz_protocol_kia_encoder, + + .filter = SubGhzProtocolFilter_AutoAlarms, }; void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment) { diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 54b6344484b..927dcf8c942 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2,6 +2,7 @@ entry,status,name,type,params Version,+,50.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/main/archive/helpers/archive_helpers_ext.h,, +Header,+,applications/main/subghz/subghz_fap.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1551,7 +1552,6 @@ Function,+,furi_hal_subghz_flush_rx,void, Function,+,furi_hal_subghz_flush_tx,void, Function,+,furi_hal_subghz_get_data_gpio,const GpioPin*, Function,+,furi_hal_subghz_get_ext_power_amp,_Bool, -Function,+,furi_hal_subghz_get_extend_settings,void,"_Bool*, _Bool*" Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_rolling_counter_mult,int8_t, Function,+,furi_hal_subghz_get_rssi,float, @@ -1570,7 +1570,6 @@ Function,+,furi_hal_subghz_rx,void, Function,+,furi_hal_subghz_rx_pipe_not_empty,_Bool, Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_ext_power_amp,void,_Bool -Function,+,furi_hal_subghz_set_extend_settings,void,"_Bool, _Bool" Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath @@ -3111,6 +3110,7 @@ Function,-,strupr,char*,char* Function,-,strverscmp,int,"const char*, const char*" Function,-,strxfrm,size_t,"char*, const char*, size_t" Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" +Function,+,subghz_app,int32_t,char* Function,+,subghz_block_generic_deserialize,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*" Function,+,subghz_block_generic_deserialize_check_count_bit,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, uint16_t" Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c index 59517e1a362..17e56d6f04d 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.c +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -37,6 +37,8 @@ volatile FuriHalSubGhz furi_hal_subghz = { .async_mirror_pin = NULL, .rolling_counter_mult = 1, .ext_power_amp = false, + .extended_frequency_i = false, + .bypassed_frequency_i = false, }; int8_t furi_hal_subghz_get_rolling_counter_mult(void) { @@ -47,6 +49,14 @@ void furi_hal_subghz_set_rolling_counter_mult(int8_t mult) { furi_hal_subghz.rolling_counter_mult = mult; } +void furi_hal_subghz_set_extended_frequency(bool state_i) { + furi_hal_subghz.extended_frequency_i = state_i; +} + +void furi_hal_subghz_set_bypassed_frequency(bool state_i) { + furi_hal_subghz.bypassed_frequency_i = state_i; +} + void furi_hal_subghz_set_ext_power_amp(bool enabled) { furi_hal_subghz.ext_power_amp = enabled; } @@ -343,47 +353,10 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { return value; } -void furi_hal_subghz_get_extend_settings(bool* extend, bool* bypass) { - *extend = false; - *bypass = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - - if(flipper_format_file_open_existing(file, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_read_bool(file, "use_ext_range_at_own_risk", extend, 1); - flipper_format_read_bool(file, "ignore_default_tx_region", bypass, 1); - } - - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); -} - -void furi_hal_subghz_set_extend_settings(bool extend, bool bypass) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - - do { - if(!flipper_format_file_open_always(file, "/ext/subghz/assets/extend_range.txt")) break; - if(!flipper_format_write_header_cstr(file, "Flipper SubGhz Setting File", 1)) break; - if(!flipper_format_write_comment_cstr( - file, "Whether to allow extended ranges that can break your flipper")) - break; - if(!flipper_format_write_bool(file, "use_ext_range_at_own_risk", &extend, 1)) break; - if(!flipper_format_write_comment_cstr( - file, "Whether to ignore the default TX region settings")) - break; - if(!flipper_format_write_bool(file, "ignore_default_tx_region", &bypass, 1)) break; - } while(0); - - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); -} - bool furi_hal_subghz_is_tx_allowed(uint32_t value) { //checking regional settings - bool is_extended = false; - bool is_allowed = false; - furi_hal_subghz_get_extend_settings(&is_extended, &is_allowed); + bool is_extended = furi_hal_subghz.extended_frequency_i; + bool is_allowed = furi_hal_subghz.bypassed_frequency_i; switch(furi_hal_version_get_hw_region()) { case FuriHalVersionRegionEuRu: diff --git a/targets/f7/furi_hal/furi_hal_subghz.h b/targets/f7/furi_hal/furi_hal_subghz.h index 2291fbab890..53ea08961e8 100644 --- a/targets/f7/furi_hal/furi_hal_subghz.h +++ b/targets/f7/furi_hal/furi_hal_subghz.h @@ -67,6 +67,8 @@ typedef struct { uint8_t rolling_counter_mult; bool ext_power_amp : 1; + bool extended_frequency_i : 1; + bool bypassed_frequency_i : 1; } FuriHalSubGhz; extern volatile FuriHalSubGhz furi_hal_subghz; @@ -205,20 +207,6 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); */ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); -/** Read extend and bypass settings values into out params - * - * @param extend pointer to bool for extend - * @param bypass pointer to bool for bypass - */ -void furi_hal_subghz_get_extend_settings(bool* extend, bool* bypass); - -/** Set extend and bypass settings values to file - * - * @param extend bool for extend - * @param bypass bool for bypass - */ -void furi_hal_subghz_set_extend_settings(bool extend, bool bypass); - /** Сheck if transmission is allowed on this frequency with your current config * * @param value frequency in Hz diff --git a/targets/f7/furi_hal/furi_hal_subghz_i.h b/targets/f7/furi_hal/furi_hal_subghz_i.h index 85532a5d368..fbc4b13e67e 100644 --- a/targets/f7/furi_hal/furi_hal_subghz_i.h +++ b/targets/f7/furi_hal/furi_hal_subghz_i.h @@ -1,3 +1,4 @@ #pragma once -void furi_hal_subghz_set_extended_frequency(bool state_i); \ No newline at end of file +void furi_hal_subghz_set_extended_frequency(bool state_i); +void furi_hal_subghz_set_bypassed_frequency(bool state_i); \ No newline at end of file