#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ONE_WIRE_BUS D4 File myFile; String nssid = "WIFIrouterSSID"; String npwrd = "WIFIpassword"; String uname = "ftpusr"; String pword = "ftppwd"; String APRSisLogin = "NOCALL"; String APRSisPass = "00000"; ESP8266WebServer server(80); FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial // NTP Servers: IPAddress timeServer(188, 213, 165, 209); // pool.ntp.org // IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov // IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov // IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov int timeZone = 0; //const int timeZone = 1; // Central European Time //const int timeZone = -5; // Eastern Standard Time (USA) //const int timeZone = -4; // Eastern Daylight Time (USA) //const int timeZone = -8; // PacYUific Standard Time (USA) //const int timeZone = -7; // Pacific Daylight Time (USA) WiFiUDP Udp; unsigned int localPort = 8888; // local port to listen for UDP packets String AX25Header = "NOCALL-15>APRS,WIDE1-1,WIDE2-2:"; String KissHeader = " ddddddSssssssSrrrrrrSrrrrrrS "; String Source_Address = "@@@@@@"; char Source_SSID = '0'; String Destination_Address = "@@@@@@"; char Destination_SSID = '0'; String Via1_Address = "@@@@@@"; char Via1_SSID = '0'; String Via2_Address = "@@@@@@"; char Via2_SSID = '0'; String APRSisHost = "rotate.aprs2.net"; unsigned int APRSPort = 14580; String PositionData = "=1234.56N/01234.56E_000/000g000t"; String BeaconTail = " ESP WX "; String timestamp = "01/01/1970;12:34:56 "; String ConvCMD = "CONVERSE"; String KissInit1 = "HB 1200"; String KissInit2 = "KISS ON"; String KissInit3 = "RESTART"; static const char KissParms[16] = {0xC0, 0x01, 0x32, 0xC0, 0xC0, 0x04, 0x04, 0xC0, 0xC0, 0x02, 0x80, 0xC0, 0xC0, 0x03, 0x0F, 0xC0}; //Set KISS timing params to: \ TXdelay 50 / \ TXtail 4 / \ Persist 128 / \ SlotTime 15 / bool KissMode = false; bool bootstrap = true; bool log_flag = false; bool TNC_beacon = false; bool DS18b20_flag = true; bool BME280_flag = true; bool APRSis = false; bool new_day = true; unsigned int mtbl = 600; unsigned int tnc_beacon_interval = 600; unsigned int prev_day, prev_min; unsigned int rs232speed = 115200; float tm = 0.0; float acc_t = 0.0; unsigned int num_t = 0; float gg = 0.0; float ggr = 0.0; float acc_gg = 0.0; float acc_ggr = 0.0; time_t nextLog = 0; time_t nextBeacon = 0; String DS_read = "-12.3"; OneWire oneWire(ONE_WIRE_BUS); DallasTemperature DS18B20(&oneWire); Adafruit_BME280 bme; // I2C float h, t, tF, p, pin, dp; char temperatureFString[6]; char dpString[6]; char humidityString[6]; char pressureString[7]; char pressureInchString[6]; char tmp_buffer[20]; const int chipSelect = 15; //************************************************* // local function //************************************************* void getWeather() { if (BME280_flag) { h = bme.readHumidity(); t = bme.readTemperature(); tF = t * 1.8 + 32.0; // to Fahrenheit dp = t - 0.36 * (100.0 - h); p = (bme.readPressure() / 100.0F + 26.1) * 10; pin = 0.02953 * p; dtostrf(tF, 5, 1, temperatureFString); dtostrf(h, 5, 1, humidityString); dtostrf(p, 6, 1, pressureString); dtostrf(pin, 5, 2, pressureInchString); dtostrf(dp, 5, 1, dpString); } delay(100); } void getTemperature() { if (DS18b20_flag) { float temp; char tString[6]; do { DS18B20.requestTemperatures(); temp = DS18B20.getTempCByIndex(0); delay(100); } while (temp == 85.0 || temp == (-127.0)); dtostrf(temp, 3, 1, tString); DS_read = String(tString); } } //****************************************************************************** void setup() { Serial.begin(rs232speed); Serial.setTimeout(2000); // Wait for serial to initialize. while (!Serial) { } for (int n = 0; n < 10; n++) delay(500); //5 sec. delay Serial.println(F("Device Started")); if (BME280_flag) { bool status; status = bme.begin(0x76); if (!status) { Serial.println(F("Could not find a valid BME280 sensor, check wiring!")); while (1); } } // Set WiFi to station mode and disconnect from an AP if it was Previously connected WiFi.mode(WIFI_STA); WiFi.disconnect(); delay(100); if (!SD.begin(chipSelect)) { Serial.println(F("SD initialization failed!")); Serial.println(F("boot using default settings..")); } else { read_setup(); } if (!BME280_flag) PositionData = ">"; if (!TNC_beacon) Serial.print(F("Connecting Wifi: ")); WiFi.begin(nssid.c_str(), npwrd.c_str()); while (WiFi.status() != WL_CONNECTED) { delay(500); } if (!TNC_beacon) { Serial.print(F("WiFi connected, IP address: ")); Serial.println(WiFi.localIP()); } else { Serial.begin(rs232speed); //re-init to TNC serial speed Serial.setTimeout(2000); // Wait for serial to initialize. while (!Serial) { } } ftpSrv.begin(uname, pword); server.on("/", handleRoot); server.onNotFound(handleNotFound); server.begin(); Udp.begin(localPort); setSyncInterval(86400); //86400=24h Serial.println("\r\n"); delay(500); Serial.println("\r\n"); delay(500); //for a "clean" TNC prompt if (TNC_beacon && KissMode) { delay(500); Serial.println(KissInit1); //pass init string 1 to TNC delay(500); Serial.println(KissInit2); //pass init string 3 to TNC delay(500); Serial.println(KissInit3); //pass init string 3 to TNC for (int n = 0; n < 6; n++) delay(500); for (int i = 0; i < 16; i++) Serial.print(KissParms[i]); //set KISS timing params delay(500); } if (!TNC_beacon) Serial.println(F("Init done.")); } //******************************************************************************* void loop() { unsigned long timeout = millis(); if (bootstrap || year() == 1970) { //bootstrap one-time routine or no NTP time if (!TNC_beacon) Serial.println (F("NTP resync...")); setSyncProvider(getNtpTime); nextLog = now() - ((now() % 3600) % mtbl) + mtbl; //align log to minute 00 nextBeacon = now() - ((now() % 3600) % tnc_beacon_interval) + tnc_beacon_interval; //align beacon prev_day = day(); bootstrap = false; } if (day() != prev_day) { prev_day = day(); tm = acc_t / num_t; if (tm < 20.0) { gg = (20.0 - tm); ggr = 0.0; acc_gg += gg; } else { ggr = (tm - 20.0); gg = 0; acc_ggr += ggr; } num_t = 0; acc_t = 0; new_day = true; } if (minute() != prev_min) { prev_min = minute(); create_timestamp(); if (!TNC_beacon) { Serial.print(timestamp); Serial.print(F("free heap ")); Serial.println(ESP.getFreeHeap()); } getWeather(); delay(100); getTemperature(); acc_t += t; num_t++; } if (now() >= nextLog) { //is it time to log ? nextLog += mtbl; Log_Wx_data(); } if (now() >= nextBeacon) { //is it time to beacon ? nextBeacon += tnc_beacon_interval; APRS_WxReport(); } while ((millis() - timeout) < 5000) { ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! server.handleClient(); } } //****************************************************************************** /*-------- NTP code ----------*/ const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets time_t getNtpTime() { while (Udp.parsePacket() > 0) ; // discard any previously received packets if (!TNC_beacon) Serial.println(F("Transmit NTP Request")); sendNTPpacket(timeServer); uint32_t beginWait = millis(); while (millis() - beginWait < 1500) { int size = Udp.parsePacket(); if (size >= NTP_PACKET_SIZE) { if (!TNC_beacon) Serial.println(F("Receive NTP Response")); Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer unsigned long secsSince1900; // convert four bytes starting at location 40 to a long integer secsSince1900 = (unsigned long)packetBuffer[40] << 24; secsSince1900 |= (unsigned long)packetBuffer[41] << 16; secsSince1900 |= (unsigned long)packetBuffer[42] << 8; secsSince1900 |= (unsigned long)packetBuffer[43]; return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; } } if (!TNC_beacon) Serial.println(F("No NTP Response :-(")); return 0; // return 0 if unable to get the time } // send an NTP request to the time server at the given address void sendNTPpacket(IPAddress &address) { // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: Udp.beginPacket(address, 123); //NTP requests are to port 123 Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); } void read_setup() { myFile = SD.open("SETUP.INI"); delay(100); if (myFile) { char c; AX25Header = ""; KissHeader = ""; // ---------------------Source field------------------------------------ while (myFile.read() != '<'); //skip until '<' int i = 0; do { c = myFile.read(); //read Source address char if (c != '>') { AX25Header += c; //compose text header Source_Address.setCharAt(i, c << 1);//compose kiss field i++; } } while (c != '>'); //keep until '>' while (myFile.read() != '<'); //skip until '<' c = myFile.read(); if (c > 64 && c < 71) c -= 55; // 10 < SSID < 15 else if (c > 47 && c < 58) c -= 48; // 0 < SSID < 9 else c = 0; if (c != 0) { AX25Header += "-"; //compose text header AX25Header += String((int)c); //compose text header } Source_SSID = char(96 + (c << 1)); //compose kiss field AX25Header += ">"; //needed after callsign // ---------------------Destination field------------------------------------ while (myFile.read() != '<'); //skip until '<' i = 0; do { c = myFile.read(); //read Destination address char if (c != '>') { AX25Header += c; //compose text header Destination_Address.setCharAt(i, c << 1); //compose kiss field i++; } } while (c != '>'); //keep until '>' while (myFile.read() != '<'); //skip until '<' c = myFile.read(); if (c > 64 && c < 71) c -= 55; // 10 < SSID < 15 else if (c > 47 && c < 58) c -= 48; // 0 < SSID < 9 else c = 0; if (c != 0) { AX25Header += "-"; //compose text header AX25Header += String((int)c); //compose text header } Destination_SSID = char(96 + (c << 1)); //compose kiss field // ---------------------Via1 fields------------------------------------------ while (myFile.read() != '<'); //skip until '<' c = myFile.read (); if (c != '>') { //Repeater1 has been set AX25Header += ","; //comma between fields AX25Header += c; //compose text header Via1_Address.setCharAt(0, c << 1); //compose kiss field i = 1; do { c = myFile.read(); //read Repeater1 address if (c != '>') { AX25Header += c; //compose text header Via1_Address.setCharAt(i, c << 1); //compose kiss field i++; } } while (c != '>'); //keep until '>' while (myFile.read() != '<'); //skip until '<' c = myFile.read(); if (c > 64 && c < 71) c -= 55; // 10 < SSID < 15 else if (c > 47 && c < 58) c -= 48; // 0 < SSID < 9 else c = 0; if (c != 0) { AX25Header += "-"; //compose text header AX25Header += String((int)c); //compose text header } Via1_SSID = char(96 + (c << 1)); //compose kiss field } else { Source_SSID++; while (myFile.read() != '<'); //skip until '<' } // ---------------------Via2 fields------------------------------------------ while (myFile.read() != '<'); //skip until '<' c = myFile.read (); if (c != '>' && Via1_Address != "@@@@@@") { //Via2 has been set AX25Header += ","; //comma between fields AX25Header += c; //compose text header Via2_Address.setCharAt(0, c << 1); //compose kiss field i = 1; do { c = myFile.read(); //read Repeater2 address if (c != '>') { AX25Header += c; //compose text header Via2_Address.setCharAt(i, c << 1); //compose kiss field i++; } } while (c != '>'); //keep until '>' while (myFile.read() != '<'); //skip until '<' c = myFile.read(); if (c > 64 && c < 71) c -= 55; // 10 < SSID < 15 else if (c > 47 && c < 58) c -= 48; // 0 < SSID < 9 else c = 0; if (c != 0) { AX25Header += "-"; //compose text header AX25Header += String((int)c); //compose text header } Via2_SSID = char(97 + (c << 1)); //compose kiss field } else { Via1_SSID++; while (myFile.read() != '<'); //skip until '<' } AX25Header += ":"; // --------------------Assemble KISS frame header---------------------------- KissHeader += (char) 0xC0; //Begin a kiss frame KissHeader += (char) 0x00; //is a data frame KissHeader += Destination_Address; KissHeader += Destination_SSID; KissHeader += Source_Address; KissHeader += Source_SSID; if (Via1_Address != "@@@@@@") { //if Via1 has been set KissHeader += Via1_Address; KissHeader += Via1_SSID; } if (Via2_Address != "@@@@@@") { //if Via2 has been set KissHeader += Via2_Address; KissHeader += Via2_SSID; } KissHeader += (char) 0x03; //UI type frame KissHeader += (char) 0xF0; //Ready for payload //--------------------------------------------------------------------- APRSisHost = string_from_setup(); APRSPort = int_from_setup(); APRSisLogin = string_from_setup(); APRSisPass = string_from_setup(); PositionData = string_from_setup(); BeaconTail = string_from_setup(); timeZone = int_from_setup(); nssid = string_from_setup(); npwrd = string_from_setup(); uname = string_from_setup(); pword = string_from_setup(); BME280_flag = flag_from_setup(); DS18b20_flag = flag_from_setup(); log_flag = flag_from_setup(); mtbl = int_from_setup(); TNC_beacon = flag_from_setup(); rs232speed = int_from_setup(); KissMode = flag_from_setup(); KissInit1 = string_from_setup(); KissInit2 = string_from_setup(); KissInit3 = string_from_setup(); ConvCMD = string_from_setup(); APRSis = flag_from_setup(); tnc_beacon_interval = int_from_setup(); myFile.close(); delay(100); } else { // if the file didn't open, print an error: Serial.println(F("error opening setup file")); } } String string_from_setup() { String temp_string = ""; char c; while (myFile.read() != '<'); //skip until '<' do { c = myFile.read(); if (c != '>') temp_string += c; //read field from file } while (c != '>'); //keep until '>' return temp_string; } int int_from_setup() { String tmp = string_from_setup(); int r_value = tmp.toInt(); return r_value; } bool flag_from_setup() { while (myFile.read() != '<'); char c = myFile.read(); bool flag; if (c == 'Y') flag = true; else flag = false; return flag; } void create_timestamp() { timestamp = inslead0(day()); timestamp += "/"; timestamp += inslead0(month()); timestamp += "/"; timestamp += String(year()); timestamp += ";"; timestamp += inslead0(hour()); timestamp += ":"; timestamp += inslead0(minute()); timestamp += ":"; timestamp += inslead0(second()); timestamp += ";"; } void Log_Wx_data() { if (log_flag) { String file_name = monthShortStr(month()) + String(year()) + ".CSV"; String Data_wx = ""; myFile = SD.open(file_name, FILE_WRITE); delay(10); if (myFile) { create_timestamp(); myFile.print(timestamp); if (BME280_flag) { dtostrf(t, 3, 1, temperatureFString); Data_wx = String(temperatureFString); Data_wx += ";"; Data_wx += String(humidityString); Data_wx += ";"; dtostrf(p / 10.0, 5, 1, pressureString); Data_wx += String(pressureString); } if (DS18b20_flag) { if (BME280_flag) Data_wx += ";"; Data_wx += String(DS_read); } if (new_day && BME280_flag) { //is a new day, so also log: Data_wx += ";"; dtostrf(tm, 3, 1, temperatureFString); //mean temp Data_wx += String(temperatureFString); Data_wx += ";"; dtostrf(gg, 5, 1, tmp_buffer); //gg Data_wx += String(tmp_buffer); Data_wx += ";"; dtostrf(ggr, 5, 1, tmp_buffer); //ggr Data_wx += String(tmp_buffer); if ((day() == 15 && month() == 10) || (day() == 16 && month() == 4)) { // begin/end of season dtostrf(acc_gg, 5, 1, tmp_buffer); //acc_gg Data_wx += String(tmp_buffer); Data_wx += ";"; dtostrf(acc_ggr, 5, 1, tmp_buffer); //acc_ggr Data_wx += String(tmp_buffer); acc_gg = 0.0; //reset gg/ggr counters acc_ggr = 0.0; if (!TNC_beacon) { Serial.println(Data_wx); Serial.println(F("\n GG/GGR data logged to SD \n")); } } new_day = false; } Data_wx.replace(".", ","); //better .csv compatibility myFile.println(Data_wx); myFile.close(); if (!TNC_beacon) { Serial.println(Data_wx); Serial.println(F("\n WX data logged to SD \n")); } } else { // if the file didn't open, print an error: if (!TNC_beacon) { Serial.print(F("error opening file ")); Serial.println(file_name); } } } } void handleRoot() { server.send ( 200, "text/html", "ESP-8266 WX" ); } String inslead0(int dgt) { // insert leading zero when dgt < 10 String chunk = ""; if (dgt < 10) chunk += "0"; chunk += String(dgt); return chunk; } void handleNotFound() { server.send(404, "text/plain", "File Not Found\n\n"); } //************************************************* void APRS_WxReport() { String DataPkt = PositionData; //Compose payload... if (BME280_flag) { snprintf(tmp_buffer, 4, "%03d", (int)(tF)); DataPkt += tmp_buffer; DataPkt += "h"; snprintf(tmp_buffer, 4, "%02d", (int)(h)); DataPkt += tmp_buffer; DataPkt += "b"; snprintf(tmp_buffer, 6, "%05d", (int)(p)); DataPkt += tmp_buffer; } DataPkt += BeaconTail; if (DS18b20_flag) { DataPkt += " Ts:"; DataPkt += DS_read; } if (TNC_beacon) { if (KissMode) { Serial.print(KissHeader); Serial.print(DataPkt); Serial.print((char) 0x0D); Serial.print((char) 0xC0); //End of KISS frame } else { Serial.println(ConvCMD); //put TNC in converse mode delay(500); Serial.println(DataPkt); delay(500); //Serial.print((char) 3); //ctrl-c, quit converse mode Serial.println((char) 3); //ctrl-c, quit converse mode } } if (APRSis) { DataPkt = AX25Header + DataPkt; if (!TNC_beacon) { Serial.print(F("Sending Data: ")); Serial.println(DataPkt); //while (true) ESP.wdtFeed(); Serial.print("connecting to "); Serial.println(APRSisHost); } // Use WiFiClient class to create TCP connections WiFiClient client; if (!client.connect(APRSisHost.c_str(), APRSPort)) { if (!TNC_beacon) Serial.println(F("connection failed")); return; } String LoginPkt = "user " + APRSisLogin + " pass " + APRSisPass + BeaconTail; if (!TNC_beacon) { Serial.println(F("Sending Login: ")); Serial.println(LoginPkt); } client.print(LoginPkt + "\r\n"); unsigned long timeout = millis(); while (client.available() == 0) { if (millis() - timeout > 5000) { if (!TNC_beacon) Serial.println(F(">>> Client Timeout !")); client.stop(); } } // Read all the lines of the reply from server and print them to Serial int Verified = 0; while (client.available()) { String line = client.readStringUntil('\r'); if (line.indexOf("verified") > 0) { Verified = 1; } if (!TNC_beacon) Serial.print(line); } if (Verified == 1) if (!TNC_beacon) { Serial.println(F("Server verified OK")); Serial.println(F("Sending Data: ")); Serial.println(DataPkt); } client.print(DataPkt + "\r\n"); if (client.connected()) { if (!TNC_beacon) { Serial.println(); Serial.println(F("disconnecting.")); } client.stop(); //client.flush(); } } }