// Tracker for LoRA APRS // // TTGO T-Beam includes GPS module + optional DHT22 (not yet DONE) // // can be used as tracker only, tracker plus weather reports (temperature and humidity) or weather reports station only // // updated from OE1ACM sketch by OE3CJB to enable WX data to be sent via LoRa APRS. // one package is with position and battery voltage // the next is with weather data in APRS format // // licensed under CC BY-NC-SA // // version: V1.2 // last update: 02.01.2020 // // change history // added course change to smart Beaconing // code cleaned // change of mode with KEY (without display but with LED only) // change of symbol with KEY (without display but with LED only) // // version V1.1 // added HW Version V1.0 support // added presetting in the header TTGO...config.h to prevent long initial setup at first boot up // added "SPACE" to allowed letters for callsign for shorter callsigns - has to be added at the end // added smart beaconing // // version V1.0beta // first released version// #define DEBUG false // used for debugging purposes , e.g. turning on special serial or display logging // Includes #include // to config user parameters #include #include #include #include #include // library from OE1ACM #include #include #ifdef DS18B20 #include // libraries for DS18B20 #include #else #include // library from https://github.com/beegee-tokyo/DHTesp for DHT22 #endif #include #include #include #include #include #include #include #include #include //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //PINs used for HW extensions // Pin for battery voltage -> bei T-Beam ADC1_CHANNEL_7 // #define ANALOG_PIN_0 35 // connected to battery // I2C LINES #define I2C_SDA 21 #define I2C_SCL 22 // DISPLAY address #define SSD1306_ADDRESS 0x3C // AXP192 address #define AXP192_SLAVE_ADDRESS 0x34 /* for feather32u4 #define RFM95_CS 8 #define RFM95_RST 4 #define RFM95_INT 7 */ // Variables for DHT22 temperature and humidity sensor int chk; boolean hum_temp = false; float hum=0; //Stores humidity value float temp=99.99; //Stores temperature value float tempf=99.99; //Stores temperature value //other global Variables String Textzeile1, Textzeile2; int button=0; int button_ctr=0; // int version=0; // 0 = V0.7, 1 = V1.0 // bool ssd1306_found = false; // bool axp192_found = false; enum Tx_Mode {TRACKER, WX_TRACKER, WX_MOVE, WX_FIXED}; // Position from GPS for TRACKER and WX_TRACKER // Position for WX_ONLY from Headerfile!!! Tx_Mode tracker_mode; // Pins for GPS #ifdef T_BEAM_V1_0 static const int RXPin = 12, TXPin = 34; // changed BG A3 A2 #else static const int RXPin = 15, TXPin = 12; // changed BG A3 A2 #endif static const uint32_t GPSBaud = 9600; //GPS const byte TX_en = 0; const byte RX_en = 0; //TX/RX enable 1W modul // LED for signalling #ifdef T_BEAM_V1_0 const byte TXLED = 33; //pin number for LED on TX Tracker #else const byte TXLED = 14; //pin number for LED on TX Tracker #endif // Button of TTGO T-Beam #ifdef T_BEAM_V1_0 // const byte BUTTON = 38; //pin number for Button on TTGO T-Beam #define BUTTON 38 //pin number for Button on TTGO T-Beam #else #define BUTTON 39 //pin number for Button on TTGO T-Beam #endif // const byte GPSLED = 6; // pin gps & Heartbeat // const byte GPSLED1 = 9; // pin gps & Heartbeat // Pins for LoRa module //#ifdef T_BEAM_V1_0 // const byte lora_PReset = 14; //pin where LoRa device reset line is connected // const byte lora_PNSS = 18; //pin number where the NSS line for the LoRa device is connected. //#else const byte lora_PReset = 23; //pin where LoRa device reset line is connected const byte lora_PNSS = 18; //pin number where the NSS line for the LoRa device is connected. //#endif // pin 11 MOSI // pin 12 MISO // pin 13 SCLK // #define ModemConfig BG_RF95::Bw125Cr45Sf4096 #define DHTPIN 25 // the DHT22 is connected to PIN25 #define ONE_WIRE_BUS 25 // the DS18B20 is connected to PIN25 // Variables for APRS packaging String Tcall; //your Call Sign for normal position reports String wxTcall; //your Call Sign for weather reports String sTable="/"; //Primer String wxTable="/"; //Primer String wxSymbol="_"; //Symbol Code Weather Station // Tracker setting: use these lines to modify the tracker behaviour #define TXFREQ 433.775 // Transmit frequency in MHz #define TXdbmW 18 // Transmit power in dBm #define TXenablePA 0 // switch internal power amplifier on (1) or off (0) // Variables and Constants Preferences prefs; String InputString = ""; //data on buff is copied to this string String Outputstring = ""; String outString=""; //The new Output String with GPS Conversion RAW String LongShown=""; String LatShown=""; String LongFixed=""; String LatFixed=""; String TxSymbol=""; boolean wx; //byte arrays byte lora_TXBUFF[128]; //buffer for packet to send //byte Variables byte lora_TXStart; //start of packet data in TXbuff byte lora_TXEnd; //end of packet data in TXbuff byte lora_FTXOK; //flag, set to 1 if TX OK byte lora_TXPacketType; //type number of packet to send byte lora_TXDestination; //destination address of packet to send byte lora_TXSource; //source address of packet received byte lora_FDeviceError; //flag, set to 1 if RFM98 device error byte lora_TXPacketL; //length of packet to send, includes source, destination and packet type. unsigned long lastTX = 0L; float BattVolts; // variables for smart beaconing float average_speed[5] = {0,0,0,0,0}, average_speed_final=0, max_speed=30, min_speed=0; float average_course[3] = {0,0,0}; float old_course = 0, new_course = 0; int point_avg_speed = 0, point_avg_course = 0; ulong min_time_to_nextTX=60000L; // minimum time period between TX = 60000ms = 60secs = 1min ulong nextTX=60000L; // preset time period between TX = 60000ms = 60secs = 1min static const adc_atten_t atten = ADC_ATTEN_DB_6; static const adc_unit_t unit = ADC_UNIT_1; static void smartDelay(unsigned long); void recalcGPS(void); void sendpacket(void); void loraSend(byte, byte, byte, byte, byte, long, byte, float); void batt_read(void); void writedisplaytext(String, String, String, String, String, String, int); void setup_data(void); #ifdef DS18B20 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); #else DHTesp dht; // Initialize DHT sensor for normal 16mhz Arduino #endif // SoftwareSerial ss(RXPin, TXPin); // The serial connection to the GPS device HardwareSerial ss(1); // TTGO has HW serial TinyGPSPlus gps; // The TinyGPS++ object #ifdef T_BEAM_V1_0 AXP20X_Class axp; #endif // checkRX uint8_t buf[BG_RF95_MAX_MESSAGE_LEN]; uint8_t len = sizeof(buf); // Singleton instance of the radio driver BG_RF95 rf95(18, 26); // TTGO T-Beam has NSS @ Pin 18 and Interrupt IO @ Pin26 // initialize OLED display #define OLED_RESET 4 // not used Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); // +---------------------------------------------------------------------+// // + SETUP --------------------------------------------------------------+// // +---------------------------------------------------------------------+// void setup() { prefs.begin("nvs", false); tracker_mode = (Tx_Mode)prefs.getChar("tracker_mode", 0); prefs.end(); //tracker_mode = current_mode; ///////////////// // hier muss aus dem RAM der Modus gelesen werden! ///////////////// if (tracker_mode == WX_TRACKER) { wx = true; } else { wx = false; } pinMode(TXLED, OUTPUT); pinMode(BUTTON, INPUT); digitalWrite(TXLED, LOW); // turn blue LED off Serial.begin(115200); Wire.begin(I2C_SDA, I2C_SCL); #ifdef T_BEAM_V1_0 // initialize power controller - new on HW V1.0 if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) { Serial.println("LoRa-APRS / Init / AXP192 Begin PASS"); } else { Serial.println("LoRa-APRS / Init / AXP192 Begin FAIL"); } axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); if (tracker_mode != WX_FIXED) { axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // switch on GPS in all modes except WX_FIXED } else { axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // switch off GPS in WX_FIXED mode } axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON); axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON); axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); axp.setDCDC1Voltage(3300); #endif if(!display.begin(SSD1306_SWITCHCAPVCC, SSD1306_ADDRESS)) { for(;;); // Don't proceed, loop forever } writedisplaytext("LoRa-APRS","","Init:","Display OK!","","PRESS 3sec for config",1000); Serial.println("LoRa-APRS / Init / Display OK! / PRESS 3sec for config"); #ifndef DONT_USE_FLASH_MEMORY //////////////////////////// Setup CALLSIGN prefs.begin("nvs", false); Tcall = prefs.getString("Tcall", CALLSIGN); wxTcall = prefs.getString("wxTcall", WX_CALLSIGN); LongFixed = prefs.getString("LongFixed", LONGITUDE_PRESET); LatFixed = prefs.getString("LatFixed", LATIDUDE_PRESET); TxSymbol = prefs.getString("TxSymbol", APRS_SYMBOL); prefs.end(); #else Tcall = CALLSIGN; wxTcall = WX_CALLSIGN; LongFixed = LONGITUDE_PRESET; LatFixed = LATIDUDE_PRESET; TxSymbol = APRS_SYMBOL; #endif Serial.println("LoRa-APRS / Call="+Tcall+" / WX-Call="+wxTcall+" / TxSymbol="+TxSymbol); int start_button_pressed = millis(); #ifndef DONT_USE_FLASH_MEMORY while ((digitalRead(BUTTON) == LOW) && (millis() 5000 && gps.charsProcessed() < 10) { writedisplaytext(" "+Tcall,"","Init:","ERROR!","No GPS data!","Please restart TTGO",0); Serial.println("LoRa-APRS / Init / GPS ERROR - no GPS data - please RESTART TTGO"); while (true) {blinker(1);} } writedisplaytext(" "+Tcall,"","Init:","Data from GPS OK!","","",250); Serial.println("LoRa-APRS / Init / Data from GPS OK!"); } else { writedisplaytext(" "+Tcall,"","Init:","GPS switched OFF!","","",250); Serial.println("LoRa-APRS / Init / GPS switched OFF!"); } #ifdef T_BEAM_V1_0 writedisplaytext("LoRa-APRS","","Init:","ADC OK!","BAT: "+String(axp.getBattVoltage()/1000,1),"",250); Serial.print("LoRa-APRS / Init / ADC OK! / BAT: "); Serial.println(String(axp.getBattVoltage()/1000,1)); #else adc1_config_width(ADC_WIDTH_BIT_12); adc1_config_channel_atten(ADC1_CHANNEL_7,ADC_ATTEN_DB_6); writedisplaytext("LoRa-APRS","","Init:","ADC OK!","BAT: "+String(analogRead(35)*7.221/4096,1),"",250); Serial.print("LoRa-APRS / Init / ADC OK! / BAT: "); Serial.println(String(analogRead(35)*7.221/4096,1)); #endif rf95.setFrequency(433.775); rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096); // hard coded because of double definition rf95.setTxPower(5); #ifdef DS18B20 sensors.begin(); #else dht.setup(DHTPIN,dht.AUTO_DETECT); // initialize DHT22 #endif delay(250); #ifdef DS18B20 sensors.requestTemperatures(); // Send the command to get temperature readings temp = sensors.getTempCByIndex(0); // get temp from 1st (!) sensor only #else temp = dht.getTemperature(); hum = dht.getHumidity(); #endif writedisplaytext("LoRa-APRS","","Init:","DHT OK!","TEMP: "+String(temp,1),"HUM: "+String(hum,1),250); Serial.print("LoRa-APRS / Init / DHT OK! Temp="); Serial.print(String(temp)); Serial.print(" Hum="); Serial.println(String(hum)); writedisplaytext("LoRa-APRS","","Init:","FINISHED OK!"," =:-) ","",250); Serial.println("LoRa-APRS / Init / FINISHED OK! / =:-)"); writedisplaytext("","","","","","",0); } // +---------------------------------------------------------------------+// // + MAINLOOP -----------------------------------------------------------+// // +---------------------------------------------------------------------+// void loop() { if (digitalRead(BUTTON)==LOW) { ++button_ctr; if (button_ctr>=5){ switch(tracker_mode) { case TRACKER: tracker_mode = WX_TRACKER; writedisplaytext("LoRa-APRS","","New Mode","WX-TRACKER","","",500); Serial.println("LoRa-APRS / New Mode / WX-TRACKER"); blinker(2); break; case WX_TRACKER: tracker_mode = WX_MOVE; writedisplaytext("LoRa-APRS","","New Mode","WX-MOVING","","",500); Serial.println("LoRa-APRS / New Mode / WX-MOVING"); blinker(3); break; case WX_MOVE: tracker_mode = WX_FIXED; writedisplaytext("LoRa-APRS","","New Mode","WX-FIXED","","",500); Serial.println("LoRa-APRS / New Mode / WX-FIXED"); #ifdef T_BEAM_V1_0 axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // switch OFF GPS at mode WX_FIXED #endif blinker(4); break; case WX_FIXED: default: tracker_mode = TRACKER; writedisplaytext("LoRa-APRS","","New Mode","TRACKER","","",500); Serial.println("LoRa-APRS / New Mode / TRACKER"); #ifdef T_BEAM_V1_0 axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // switch on GPS in all modes except WX_FIXED #endif blinker(1); break; } prefs.begin("nvs", false); prefs.putChar("tracker_mode", (char)tracker_mode); prefs.end(); button_ctr=0; // ESP.restart(); } } else { button_ctr = 0; } if (hum_temp) { hum_temp=false; #ifdef DS18B20 sensors.requestTemperatures(); // Send the command to get temperature readings temp = sensors.getTempCByIndex(0); // get temp from 1st (!) sensor only #else temp = dht.getTemperature(); #endif } else { hum_temp=true; #ifdef DS18B20 hum = 0; #else hum = dht.getHumidity(); #endif } if (tracker_mode != WX_FIXED) { while (ss.available() > 0) { gps.encode(ss.read()); } } if (rf95.waitAvailableTimeout(100)) { if (rf95.recvAPRS(buf, &len)) { } } if (tracker_mode != WX_FIXED) { LatShown = String(gps.location.lat(),5); LongShown = String(gps.location.lng(),5); average_speed[point_avg_speed] = gps.speed.kmph(); // calculate smart beaconing ++point_avg_speed; if (point_avg_speed>4) {point_avg_speed=0;} average_speed_final = (average_speed[0]+average_speed[1]+average_speed[2]+average_speed[3]+average_speed[4])/5; nextTX = (max_time_to_nextTX-min_time_to_nextTX)/(max_speed-min_speed)*(max_speed-average_speed_final)+min_time_to_nextTX; if (nextTX < min_time_to_nextTX) {nextTX=min_time_to_nextTX;} if (nextTX > max_time_to_nextTX) {nextTX=max_time_to_nextTX;} average_course[point_avg_course] = gps.course.deg(); // calculate smart beaconing course ++point_avg_course; if (point_avg_course>2) { point_avg_course=0; // new_course = (average_course[0]+average_course[1]+average_course[2])/3; new_course = atan ((sin(average_course[0])+sin(average_course[1])+sin(average_course[2]))/(cos(average_course[0])+cos(average_course[1])+cos(average_course[2]))); if ((old_course < 30) && (new_course > 330)) { if (abs(new_course-old_course-360)>=30) { nextTX = 0; } } else { if ((old_course > 330) && (new_course < 30)) { if (abs(new_course-old_course+360)>=30) { nextTX = 0; } } else { if (abs(new_course-old_course)>=30) { nextTX = 0; } } } old_course = new_course; } } else { LatShown = LatFixed; LongShown = LongFixed; nextTX = max_time_to_nextTX; } batt_read(); if (button_ctr==2) { nextTX = 0; } if ((millis()-10) {outString += "0"; } tempf = abs(tempf); } else { // positive Werte erstellen if(tempf<100) {outString += "0"; } if(tempf<10) {outString += "0"; } } helper = String(tempf,0); helper.trim(); outString += helper; outString += "r...p...P...h"; if(hum<10) {outString += "0"; } helper = String(hum,0); helper.trim(); outString += helper; outString += "b......DHT22"; break; case WX_TRACKER: if (wx) { #ifdef DS18B20 sensors.requestTemperatures(); // Send the command to get temperature readings tempf = sensors.getTempFByIndex(0); // get temp from 1st (!) sensor only hum = 0; #else hum = dht.getHumidity(); tempf = dht.getTemperature()*9/5+32; #endif for (i=0; i-10) {outString += "0"; } tempf = abs(tempf); } else { // positive Werte erstellen if(tempf<100) {outString += "0"; } if(tempf<10) {outString += "0"; } } helper = String(tempf,0); helper.trim(); outString += helper; outString += "r...p...P...h"; if(hum<10) {outString += "0"; } helper = String(hum,0); helper.trim(); outString += helper; outString += "b......DHT22"; wx = !wx; } else { for (i=0; i-10) {outString += "0"; } tempf = abs(tempf); } else { // positive Werte erstellen if(tempf<100) {outString += "0"; } if(tempf<10) {outString += "0"; } } helper = String(tempf,0); helper.trim(); outString += helper; outString += "r...p...P...h"; if(hum<10) {outString += "0"; } helper = String(hum,0); helper.trim(); outString += helper; outString += "b......DHT22"; break; case TRACKER: default: for (i=0; i","[","b","<"}; String werte_weiter_symbol[5] = {"yes","no"}; int8_t pos_in_string; int8_t pos_ssid; bool key_pressed = false; int waiter, symbol_only; int initial_waiter = 2000; char aktueller_letter; int8_t pos_letter; String pfeile = "^"; int8_t initial_ssid; // set Tx Symbol - gleich zu Beginn, falls man nur das Symbol ändern möchte pos_ssid = 0; while (true) { TxSymbol = werte_TxSymbol_symbol[pos_ssid]; blinker(pos_ssid+1); writedisplaytext(" SETUP", " Symbol",werte_TxSymbol_text[pos_ssid], "", "PRESS KEY to select", "", 0); waiter = millis(); while (millis()<(waiter+1000+initial_waiter)) { if (digitalRead(BUTTON)==LOW) { key_pressed = true; } } initial_waiter = 0; if (key_pressed==true) { key_pressed = false; writedisplaytext(" SETUP", " Symbol",werte_TxSymbol_text[pos_ssid], "", "programmed", "", 2000); break; } ++pos_ssid; if (pos_ssid>=5) {pos_ssid=0;} } // smartDelay(500); // fragen, ob es weiter gehen soll pos_ssid = 0; key_pressed = false; initial_waiter = 2000; while (true) { // TxSymbol = werte_TxSymbol_symbol[pos_ssid]; blinker(2-pos_ssid); writedisplaytext(" SETUP", " stop it?"," "+werte_weiter_symbol[pos_ssid], "", "PRESS KEY to select", "", 0); waiter = millis(); while (millis()<(waiter+1000+initial_waiter)) { if (digitalRead(BUTTON)==LOW) { key_pressed = true; } } initial_waiter = 0; if (key_pressed==true) { key_pressed = false; writedisplaytext(" SETUP", " stop it?"," "+werte_weiter_symbol[pos_ssid], "", "selected", "", 2000); break; } ++pos_ssid; if (pos_ssid>=2) {pos_ssid=0;} } if (pos_ssid != 0) { // set callsign - one for both reports pos_in_string = 0; key_pressed = false; initial_waiter = 2000; while (pos_in_string < 6) { key_pressed = false; aktueller_letter = (char) Tcall.charAt(pos_in_string);// ist Buchstabe holen for (pos_letter=0;pos_letter<37;pos_letter++) { if (aktueller_letter == werte_call[pos_letter]) { break; } } while (true) { Tcall.setCharAt(pos_in_string, aktueller_letter); writedisplaytext(" SETUP", " Call"," "+Tcall," "+pfeile, "PRESS KEY to select", "", 0); waiter = millis(); while (millis()<(waiter+1000+initial_waiter)) { if (digitalRead(BUTTON)==LOW) { key_pressed = true; } } initial_waiter = 0; if (key_pressed==true) { key_pressed = false; break; } // nächster Buchstabe ++pos_letter; if (pos_letter>=37) {pos_letter=0;} aktueller_letter=werte_call[pos_letter]; } initial_waiter = 2000; pfeile = " "+pfeile; ++pos_in_string; } // set normal SSID initial_ssid = (int8_t) (Tcall.substring(7,9)).toInt(); pos_ssid = initial_ssid; pfeile = " ^"; key_pressed = false; initial_waiter = 2000; while (true) { writedisplaytext(" SETUP", " normal SSID"," "+Tcall, pfeile, "PRESS KEY to select", "", 0); waiter = millis(); while (millis()<(waiter+1000+initial_waiter)) { if (digitalRead(BUTTON)==LOW) { key_pressed = true; } } initial_waiter = 0; if (key_pressed==true) { key_pressed = false; break; } ++pos_ssid; if (pos_ssid>=16) {pos_ssid=0;} Tcall = Tcall.substring(0,6)+"-"+werte_SSID[pos_ssid]; } writedisplaytext(" SETUP", " Call"," "+Tcall," ", "programmed", "", 2000); // set WX SSID initial_ssid = (int8_t) (wxTcall.substring(7,9)).toInt(); pos_ssid = initial_ssid; key_pressed = false; initial_waiter = 2000; while (true) { writedisplaytext(" SETUP", " WX SSID"," "+wxTcall, pfeile, "PRESS KEY to select", "", 0); waiter = millis(); while (millis()<(waiter+1000+initial_waiter)) { if (digitalRead(BUTTON)==LOW) { key_pressed = true; } } initial_waiter = 0; if (key_pressed==true) { key_pressed = false; break; } ++pos_ssid; if (pos_ssid>=16) {pos_ssid=0;} wxTcall = wxTcall.substring(0,6)+"-"+werte_SSID[pos_ssid]; } writedisplaytext(" SETUP", " WX-Call"," "+wxTcall," ", "programmed", "", 2000); // set LONGITUDE pfeile = "^"; pos_in_string = 0; key_pressed = false; initial_waiter = 2000; while (pos_in_string < 9) { key_pressed = false; aktueller_letter = (char) LongFixed.charAt(pos_in_string);// ist Buchstabe holen for (pos_letter=0;pos_letter<14;pos_letter++) { if (aktueller_letter == werte_latlon[pos_letter]) { break; } } while (true) { LongFixed.setCharAt(pos_in_string, aktueller_letter); writedisplaytext(" SETUP", " Longitude"," "+LongFixed," "+pfeile, "for fixed POS", "PRESS KEY to select", 0); waiter = millis(); while (millis()<(waiter+1000+initial_waiter)) { if (digitalRead(BUTTON)==LOW) { key_pressed = true; } } initial_waiter = 0; if (key_pressed==true) { key_pressed = false; break; } // nächster Buchstabe ++pos_letter; if (pos_letter>=14) {pos_letter=0;} aktueller_letter=werte_latlon[pos_letter]; } initial_waiter = 2000; pfeile = " "+pfeile; ++pos_in_string; if (pos_in_string == 5) { ++pos_in_string; pfeile = " "+pfeile; } } writedisplaytext(" SETUP", " Longitude"," "+LongFixed,"", "for fixed POS", "programmed", 2000); // set LATITUDE pfeile = "^"; pos_in_string = 0; key_pressed = false; initial_waiter = 2000; while (pos_in_string < 8) { key_pressed = false; aktueller_letter = (char) LatFixed.charAt(pos_in_string);// ist Buchstabe holen for (pos_letter=0;pos_letter<14;pos_letter++) { if (aktueller_letter == werte_latlon[pos_letter]) { break; } } while (true) { LatFixed.setCharAt(pos_in_string, aktueller_letter); writedisplaytext(" SETUP", " Latitude"," "+LatFixed," "+pfeile, "for fixed POS", "PRESS KEY to select", 0); waiter = millis(); while (millis()<(waiter+1000+initial_waiter)) { if (digitalRead(BUTTON)==LOW) { key_pressed = true; } } initial_waiter = 0; if (key_pressed==true) { key_pressed = false; break; } // nächster Buchstabe ++pos_letter; if (pos_letter>=14) {pos_letter=0;} aktueller_letter=werte_latlon[pos_letter]; } initial_waiter = 2000; pfeile = " "+pfeile; ++pos_in_string; if (pos_in_string == 4) { ++pos_in_string; pfeile = " "+pfeile; } } writedisplaytext(" SETUP", " Latitude"," "+LatFixed,"", "for fixed POS", "programmed", 2000); } // write all values to NVRAM prefs.begin("nvs", false); prefs.putString("Tcall", Tcall); prefs.putString("wxTcall", wxTcall); prefs.putString("LatFixed", LatFixed); prefs.putString("LongFixed", LongFixed); prefs.putString("TxSymbol", TxSymbol); prefs.end(); writedisplaytext(" SETUP", "ALL DONE","", "stored in NVS", "", "", 2000); } void blinker(int counter) { for (int i = 0; i < (counter-1); i++) { digitalWrite(TXLED, HIGH); // turn blue LED ON smartDelay(150); digitalWrite(TXLED, LOW); // turn blue LED OFF smartDelay(100); } digitalWrite(TXLED, HIGH); // turn blue LED ON smartDelay(150); digitalWrite(TXLED, LOW); // turn blue LED OFF }