Open bbgt0814 opened 7 months ago
Did it compile and flash without errors; what was the logging output?
C:\Arduino Ockam ersatz\nmea-bridge-main\nmea_bridge\nmea_bridge.ino: In function 'void handle_udp_event()':
C:\Arduino Ockam ersatz\nmea-bridge-main\nmea_bridge\nmea_bridge.ino:513:54: warning: ISO C++ forbids converting a string constant to 'char' [-Wwrite-strings]
513 | handle_incoming_sentence(buffer, length, "udp");
| ^~~~~
C:\Arduino Ockam ersatz\nmea-bridge-main\nmea_bridge\nmea_bridge.ino: In function 'void handle_websocket_event(uint8_t, WStype_t, uint8_t, size_t)':
C:\Arduino Ockam ersatz\nmea-bridge-main\nmea_bridge\nmea_bridge.ino:533:63: warning: ISO C++ forbids converting a string constant to 'char' [-Wwrite-strings]
533 | handle_incoming_sentence((char )payload, length, "ws");
| ^~~~
C:\Arduino Ockam ersatz\nmea-bridge-main\nmea_bridge\nmea_bridge.ino: In function 'void transmit_outgoing_over_websocket(char, size_t)':
C:\Arduino Ockam ersatz\nmea-bridge-main\nmea_bridge\nmea_bridge.ino:589:50: warning: ISO C++ forbids converting a string constant to 'char' [-Wwrite-strings]
589 | send_websocket_message(sentence, length, "serial");
| ^~~~
C:\Arduino Ockam ersatz\nmea-bridge-main\nmea_bridge\nmea_bridge.ino: In function 'void send_websocket_message(char, size_t, char)':
C:\Arduino Ockam ersatz\nmea-bridge-main\nmea_bridge\nmea_bridge.ino:603:54: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
603 | write_format_to_http_response_buffer("\u%04x", int(c));
| ^~~~~
. Variables and constants in RAM (global, static), used 42204 / 80192 bytes (52%)
║ SEGMENT BYTES DESCRIPTION
╠══ DATA 1632 initialized variables
╠══ RODATA 8668 constants
╚══ BSS 31904 zeroed variables
. Instruction RAM (IRAM_ATTR, ICACHE_RAM_ATTR), used 60863 / 65536 bytes (92%)
║ SEGMENT BYTES DESCRIPTION
╠══ ICACHE 32768 reserved space for flash instruction cache
╚══ IRAM 28095 code in IRAM
. Code in flash (default, ICACHE_FLASH_ATTR), used 316280 / 1048576 bytes (30%)
║ SEGMENT BYTES DESCRIPTION
╚══ IROM 316280 code in flash
esptool.py v3.0
Serial port COM9
Connecting....
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 84:0d:8e:a7:73:22
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 358832 bytes to 256751...
Writing at 0x00000000... (6 %)
Writing at 0x00004000... (12 %)
Writing at 0x00008000... (18 %)
Writing at 0x0000c000... (25 %)
Writing at 0x00010000... (31 %)
Writing at 0x00014000... (37 %)
Writing at 0x00018000... (43 %)
Writing at 0x0001c000... (50 %)
Writing at 0x00020000... (56 %)
Writing at 0x00024000... (62 %)
Writing at 0x00028000... (68 %)
Writing at 0x0002c000... (75 %)
Writing at 0x00030000... (81 %)
Writing at 0x00034000... (87 %)
Writing at 0x00038000... (93 %)
Writing at 0x0003c000... (100 %)
Wrote 358832 bytes (256751 compressed) at 0x00000000 in 6.2 seconds (effective 461.7 kbit/s)...
Hash of data verified.
Leaving... Hard resetting via RTS pin...
Gesendet: Mittwoch, 06. Dezember 2023 um 17:02 Uhr Von: "Alexander van Ratingen" @.> An: "alvra/nmea-bridge" @.> Cc: "bbgt0814" @.>, "Author" @.> Betreff: Re: [alvra/nmea-bridge] can't find the ssid on my network NMEA Bridge (Issue #5)
Did it compile and flash without errors; what was the logging output?
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>
I tried it with both Arduino IDE Versions 1.8X and 2.X, the error remains the same. Greetings
Hi, questions about the problem described. on which board was the sketch originally developed? which lib and which IDE compiler version does the sketch work? Is there currently a board on which the sketch can run?
Hello,
Having a similar issue with HiLetgo NodeMCU LUA ESP8266
Looking through the code, I am getting a double blink which indicates I am in access point mode but there is no SSID showing up on any of my devices.
Any thoughts on where to go from here?
Some information that may be helpful to you: Original device for which this was developed: NodeMCU v3 Arduino IDE version: 1.8.19 (2021) Board config in IDE: NodeMCU 1.0 (ESP-12E Module)
If the device was properly flashed and otherwise seems to function but you're not seeing a WiFi access point, it might be that the device is accidentally configured into station mode. Try to reset it as explained in the readme, and see if the AP NMEA Bridge
appears.
I've been through everything. I assume that the eeprom is addressed incorrectly. I gave up the project because all work with the sketch was not productive. The new IDE no longer works as expected. Process too complex.
Gesendet: Montag, 08. Januar 2024 um 22:42 Uhr Von: "Alexander van Ratingen" @.> An: "alvra/nmea-bridge" @.> Cc: "bbgt0814" @.>, "Author" @.> Betreff: Re: [alvra/nmea-bridge] can't find the ssid on my network NMEA Bridge (Issue #5)
Some information that may be helpful to you: Original device for which this was developed: NodeMCU v3 Arduino IDE version: 1.8.19 (2021) Board config in IDE: NodeMCU 1.0 (ESP-12E Module)
If the device was properly flashed and otherwise seems to function but you're not seeing a WiFi access point, it might be that the device is accidentally configured into station mode. Try to reset it as explained in the readme, and see if the AP NMEA Bridge appears.
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>
Had the same problem. The ESP8266 came up with an access point SSID of FARYLINK-***, password unknown. To cut a long story short, I was not able to reset the module on boot. So I added a button to an unused GPIO input and changed the code just to check if that button is pressed. Then bingo, I got the NEMA-Bridge SSID successfully.
I assume there is a timing problem when booting. The solution with the initial query does not show a 100% solution to the problem
Gesendet: Freitag, 09. Februar 2024 um 11:20 Uhr Von: "Fire-Power" @.> An: "alvra/nmea-bridge" @.> Cc: "bbgt0814" @.>, "Author" @.> Betreff: Re: [alvra/nmea-bridge] can't find the ssid on my network NMEA Bridge (Issue #5)
Had the same problem. The ESP8266 came up with an access point SSID of FARYLINK-***, password unknown. To cut a long story short, I was not able to reset the module on boot. So I added a button to an unused GPIO input and changed the code just to check if that button is pressed. Then bingo, I got the NEMA-Bridge SSID successfully.
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>
@alvra I'm having the exact same problem. I get the network ESP-5D3CAE and I cannot connect to it. The reset button on the board doesn't work as well :(
@Fire-Power can you please share the example of your code? I'd spent 5+ hours trying different solutions that came to my mind - nothing works.
The version with a reset config button added. `// moded to enable config reset using a button press. @firepower
extern "C" {
uint16 readvdd33(void);
}
// global macros utils
// #define DEBUG_PRINT(x) DEBUG_SERIAL.print(x) // #define DEBUG_PRINTLN(x) DEBUG_SERIAL.println(x)
// hardware layout
//#define LAMP1 4
// configuration
enum WifiMode: uint8_t { station = 1, access_point = 2, };
enum TransmitMode: uint8_t { broadcast = 1, multicast = 2, unicast = 3, };
uint32_t baudrate_options[BAUDRATE_OPTION_COUNT + 1] = { 0, // not an option, used to distinguish from undefined 4800, 9600, 38400, 115200 };
/ Serial on the ESP8266 The ESP has two hardware serial ports. Serial: uart0 TX: gpio1 gpio15 RX: gpio3 gpio13 By default, the first set of pins is used. Calling Serial.swap() changes these to the other set. Serial1: uart1 TX: gpio2 This one can only transmit, not receive. Source: https://github.com/esp8266/Arduino/blob/master/doc/reference.rst#serial /
// The serial device on which this device receives NMEA sentences.
// The serial device on which this device transmits NMEA sentences.
// Set to 1 if the RX and TX serial ports are the same one. //#define SERIAL_SAME 0 // TODO automatic
struct Configuration { WifiMode wifi_mode; char wifi_ssid[MAX_WIFI_SSID_SIZE]; char wifi_password[MAX_WIFI_PASSWORD_SIZE]; IPAddress static_ip_address; char mdns_hostname[MAX_MDNS_HOSTNAME_SIZE]; TransmitMode tx_mode; IPAddress tx_address; uint16_t tx_port; uint16_t rx_port; uint8_t tx_baudrate; uint8_t rx_baudrate; } config;
const char* display_wifi_mode(WifiMode mode) { if (mode == WifiMode::station) { return "Station"; } else { return "Access Point"; } }
const char* display_tx_mode(TransmitMode mode) { if (mode == TransmitMode::broadcast) { return "Broadcast"; } else if (mode == TransmitMode::multicast) { return "Multicast"; } else { return "Unicast"; } }
struct Configuration default_config = { // wifi_mode WifiMode::access_point, // wifi_ssid "NMEA Bridge", // wifi_password "nmeabridgeesp8266", // static_ip_address INADDR_ANY, // mdns_hostname "nmea_bridge", // tx_mode TransmitMode::broadcast, // tx_address INADDR_ANY, // tx_port 10110, // rx_port 10110, // tx_baudrate (4800) 1, // rx_baudrate (4800) 1, };
// global values
bool inConfigMode = false; uint8_t indicator_led_state; WifiMode effective_wifi_mode; char effective_wifi_ssid[MAX_WIFI_SSID_SIZE]; IPAddress effective_tx_address; uint16_t nmea_sentences_received = 0; uint16_t nmea_sentences_sent = 0; bool is_network_config_changed = false;
bool sentence_wanted = false; //String check_sentence; bool filter_on = false;
char http_response_buffer[HTTP_RESPONSE_BUFFER_SIZE + 1]; uint16_t http_response_buffer_filled = 0;
(HTTP_RESPONSE_BUFFER_SIZE - http_response_buffer_filled)
/* The buffer should be long enough for about two sentences * so we can handle the case where a (single) newline is lost. /
char nmea_sentence_buffer[NMEA_SENTENCE_BUFFER_SIZE + 1]; uint8_t nmea_sentence_buffer_filled = 0; // number of chars in buffer
ESP8266WebServer http_server(HTTP_PORT); WiFiUDP udp_server; DNSServer dns_server;
WebSocketsServer websocket_server(WEBSOCKET_PORT);
// cache static files "forever" (about a year)
/ Create a request handler for static files. /
{ \
if (captive_portal()) {return;} \
http_server.sendHeader( \
"Cache-Control", \
String("max-age=") + STATIC_FILE_CACHE_TIME, \
true); \
http_server.send(200, content_type, content); \
}
// forward declarations
void send_info_page_response(); void send_config_form_response(); void nmea_log_page_response(); void send_css_style_response(); void send_404_not_found_response();
void update_mdns_hostname(); void update_tx_config(); void update_rx_config(); void update_tx_baudrate(); void update_rx_baudrate();
void set_indicator_led_on(); void set_indicator_led_off(); void force_indicator_led_on(); void force_indicator_led_off(); void toggle_indicator_led(); void long_indicator_blink(); void update_indicator_slow_blink(); void update_indicator_double_blink(); bool blink_while_pressing_mode_button(); void blink_while_connecting_wifi();
String get_uptime_display(); IPAddress get_device_ip_address(); IPAddress gateway_address(IPAddress addr); IPAddress subnet_address(IPAddress addr);
void load_config_from_eeprom(Configuration &new_config); void store_config_to_eeprom(Configuration new_config); void fix_config();
extern const String html_start; extern const String html_end; extern const String html_log_content; extern const String css_style; extern const String js_log_script;
/** MAIN EVENT LOOP **/
void setup() { EEPROM.begin(512); load_config_from_eeprom(config); fix_config();
nmea_sentence_buffer_filled = 0;
SERIAL_RX.begin(baudrate_options[config.rx_baudrate]);
SERIAL_TX.begin(baudrate_options[config.tx_baudrate]);
Serial.swap(); // swap serial to GPIO 13 GPIO 15
#ifdef DEBUG
DEBUG_SERIAL.setDebugOutput(true);
#endif
// wait for serial to connect
delay(10);
// give the user a chance to press the boot-mode button after reset
delay(1000);
pinMode(INDICATOR_LED_PIN, OUTPUT);
force_indicator_led_off();
//pinMode(LAMP1, OUTPUT);
pinMode(LAMP2, OUTPUT);
pinMode(LAMP3, OUTPUT);
pinMode(BUTTON1, INPUT_PULLUP);
pinMode(BUTTON2, INPUT_PULLUP);
delay(1000);
if (digitalRead(BUTTON2) == LOW) {
filter_on = true;
}
else {
filter_on = false;
}
effective_wifi_mode = config.wifi_mode;
// pinMode(BOOT_MODE_BUTTON_PIN, INPUT_PULLUP); if (digitalRead(BUTTON1) == LOW) {
// bool exceeded_threshold = blink_while_pressing_mode_button();
// if (exceeded_threshold) {
DEBUG_PRINTLN("Resetting configuration to default");
long_indicator_blink();
config = default_config;
store_config_to_eeprom(config);
// change state to match new config
update_tx_baudrate();
update_rx_baudrate();
effective_wifi_mode = config.wifi_mode;
} else {
DEBUG_PRINTLN("Forcing access point mode");
effective_wifi_mode = WifiMode::access_point;
inConfigMode = true;
}
//}
// WiFi.hostname(config.mdns_hostname);
#ifdef WIFI_SIGNAL_POWER
WiFi.setOutputPower(WIFI_SIGNAL_POWER);
#endif
strlcpy(effective_wifi_ssid, config.wifi_ssid, MAX_WIFI_SSID_SIZE);
WiFi.setSleepMode(WIFI_NONE_SLEEP);
if (effective_wifi_mode == WifiMode::access_point) {
DEBUG_PRINT("starting new wifi network: ");
DEBUG_PRINTLN(effective_wifi_ssid);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(
(config.static_ip_address.isSet() ?
config.static_ip_address : WIFI_ACCESS_POINT_DEVICE_IP),
WIFI_ACCESS_POINT_GATEWAY_IP,
WIFI_ACCESS_POINT_SUBNET_IP);
WiFi.softAP(effective_wifi_ssid, config.wifi_password, WIFI_ACCESS_POINT_CHANNEL, WIFI_ACCESS_POINT_MAX_CONNECTIONS);
// wait for the ip address to be available
delay(500);
// setup dns for captive portal
dns_server.setErrorReplyCode(DNSReplyCode::NoError);
dns_server.start(DNS_PORT, "*", WiFi.softAPIP());
} else {
DEBUG_PRINT("connecting to existing wifi network: ");
DEBUG_PRINTLN(effective_wifi_ssid);
WiFi.mode(WIFI_STA);
if (config.static_ip_address.isSet()) {
WiFi.config(config.static_ip_address, gateway_address(config.static_ip_address), subnet_address(config.static_ip_address));
} else {
// use DHCP, which is the default
}
WiFi.begin(effective_wifi_ssid, config.wifi_password);
blink_while_connecting_wifi();
}
DEBUG_PRINTLN("done setting up wifi");
// start udp server
udp_server.begin(config.rx_port);
// start mDNS server
if (config.mdns_hostname[0] != '\0') {
// TODO mDNS doesnt work in station mode
DEBUG_PRINT("starting MDNS: http://");
DEBUG_PRINT(config.mdns_hostname);
DEBUG_PRINTLN(".local/");
MDNS.begin(config.mdns_hostname);
}
// start web server
DEBUG_PRINTLN("starting http server");
http_response_buffer[HTTP_RESPONSE_BUFFER_SIZE] = '\0';
http_server.on("/", send_info_page_response);
http_server.on("/config", send_config_form_response);
#ifdef ENABLE_WEBSOCKET_LOG
http_server.on("/log", nmea_log_page_response);
http_server.on("/log_script.js", send_js_log_script);
#endif
http_server.on("/style.css", send_css_style_response);
http_server.onNotFound(send_404_not_found_response);
http_server.begin();
#ifdef ENABLE_WEBSOCKET_LOG
DEBUG_PRINTLN("starting websocket server");
websocket_server.begin();
websocket_server.onEvent(handle_websocket_event);
#endif
// determine transmit address
update_tx_config();
DEBUG_PRINT("transmit: udp://");
DEBUG_PRINT(effective_tx_address.toString());
DEBUG_PRINT(":");
DEBUG_PRINT(config.tx_port);
DEBUG_PRINT("/ (");
DEBUG_PRINT(display_tx_mode(config.tx_mode));
DEBUG_PRINTLN(")");
DEBUG_PRINT("receive: udp://");
DEBUG_PRINT(get_device_ip_address().toString());
DEBUG_PRINT(":");
DEBUG_PRINT(config.rx_port);
DEBUG_PRINTLN("/");
}
void loop() { handle_serial_event(); yield();
handle_udp_event();
yield();
http_server.handleClient();
yield();
#ifdef ENABLE_WEBSOCKET_LOG
websocket_server.loop();
yield();
#endif
MDNS.update();
yield();
dns_server.processNextRequest();
yield();
// if (digitalRead(BUTTON1)) { // digitalWrite(LAMP1,HIGH); // } else { // digitalWrite(LAMP1,LOW); // }
// use indicator led to show connection status
if (effective_wifi_mode == WifiMode::access_point) {
update_indicator_double_blink();
} else {
if (!WiFi.isConnected()) {
update_indicator_slow_blink();
} else {
set_indicator_led_on();
}
}
return;
}
/ EVENT HANDLERS /
void handle_serial_event() { // on incoming data over the hardware serial port int serial_character_int; char serial_character; while (1) { serial_character_int = SERIAL_RX.read();
if (serial_character_int < 0) {
// no more data available
return;
}
serial_character = (char)serial_character_int;
// TODO sometimes this gives a large number of zero bytes
// TODO then we also don't get the extra newline
nmea_sentence_buffer[nmea_sentence_buffer_filled] = serial_character;
nmea_sentence_buffer_filled ++;
if (serial_character == '\n') {
} else if (nmea_sentence_buffer_filled >= NMEA_SENTENCE_BUFFER_SIZE) {
DEBUG_PRINTLN("nmea sentence buffer overflow");
// digitalWrite(LAMP2,HIGH);
//
//
//
// A newline must have been lost since at least two sentences
// should fit in the buffer, so send all buffered data immedately
// to let the client decide how to handle this.
nmea_sentence_buffer[NMEA_SENTENCE_BUFFER_SIZE] = '\n';
nmea_sentence_buffer_filled = NMEA_SENTENCE_BUFFER_SIZE + 1;
} else {
// digitalWrite(LAMP2,LOW);
continue;
}
handle_outgoing_sentence(nmea_sentence_buffer, nmea_sentence_buffer_filled);
nmea_sentence_buffer_filled = 0;
}
}
void handle_udp_event() { // on incoming data over the network udp connection int packet_size = udp_server.parsePacket(); char buffer[UDP_SENTENCE_BUFFER_SIZE + 2]; if (packet_size) { while (1) { size_t length = udp_server.read(buffer, UDP_SENTENCE_BUFFER_SIZE); if (length <= 0) { return; } if (buffer[length - 1] != '\n') { // Add newline terminator if not not yet present. buffer[length] = '\n'; length ++; }
//DEBUG_SERIAL.print(buffer);
//DEBUG_SERIAL.println();
handle_incoming_sentence(buffer, length, "udp");
}
}
}
void handle_websocket_event(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
// on incoming data over the network websocket connection
// TODO handle text fragments, although nmea sentences are very short
// https://github.com/Links2004/arduinoWebSockets/blob/master/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino
if (type == WStype_TEXT) {
if ((char)payload[length - 1] != '\n') {
// Add newline terminator if not not yet present.
String sentence = (char *)payload; // TODO not pretty
sentence += '\n';
payload = (uint8_t*)sentence.c_str();
length ++;
}
handle_incoming_sentence((char *)payload, length, "ws");
}
}
void handle_incoming_sentence(char sentence, size_t length, char source) { /* data is sent into the nmea device * sentence: always includes the newline and must be newline terminted / // DEBUG_SERIAL.print(sentence); // DEBUG_SERIAL.println(); if (filter_on == true) { filter_unwanted_sentence(sentence, length);
if (sentence_wanted == true) {
transmit_incoming_over_serial(sentence, length);
}}
else {
transmit_incoming_over_serial(sentence, length);
}
#ifdef ENABLE_WEBSOCKET_LOG
transmit_incoming_over_websocket(sentence, length, source);
#endif
nmea_sentences_sent ++;
}
void filter_unwanted_sentence(char *sentence, size_t length) {
String check = String(sentence);
String check_sentence = check.substring(3,6);
// DEBUG_SERIAL.print(sentence);
// DEBUG_SERIAL.println();
// DEBUG_SERIAL.print(check_sentence);
// DEBUG_SERIAL.println();
if (check_sentence == "VWR" or check_sentence == "VHW" or check_sentence == "APB" or check_sentence == "RMB" or check_sentence == "XTE" or check_sentence == "VTG" or check_sentence == "MWV" ) {
sentence_wanted = true;
//DEBUG_SERIAL.print(sentence); //DEBUG_SERIAL.println();
}
else {
sentence_wanted = false;
}
}
void handle_outgoing_sentence(char sentence, size_t length) { / data is received from the nmea device * sentence: always includes the newline and must be newline terminted / //digitalWrite(LAMP2,HIGH);
transmit_outgoing_over_udp(sentence, length);
//digitalWrite(LAMP2,LOW);
// transmit_incoming_over_serial(sentence, length); // Added to loop back on NEMA
#ifdef ENABLE_WEBSOCKET_LOG
transmit_outgoing_over_websocket(sentence, length);
#endif
nmea_sentences_received ++;
}
void transmit_incoming_over_serial(char *sentence, size_t length) {
// int freetxbuf = SERIAL_TX.availableForWrite(); // DEBUG_SERIAL.print(freetxbuf); // DEBUG_SERIAL.println(); // if (freetxbuf < 2) { // digitalWrite(LAMP3,HIGH); //} //else { // digitalWrite(LAMP3,LOW); //} SERIAL_TX.write(sentence, length); }
void buffer_outgoing_serial(char *sentence, size_t length) {
}
void transmit_outgoing_over_udp(char *sentence, size_t length) { if (config.tx_mode == TransmitMode::multicast) { int result = udp_server.beginPacketMulticast( effective_tx_address, config.tx_port, get_device_ip_address()); } else { int result = udp_server.beginPacket( effective_tx_address, config.tx_port); }
udp_server.write(sentence, length);
udp_server.endPacket();
}
void transmit_incoming_over_websocket(char *sentence, size_t length, char *source) {
send_websocket_message(sentence, length, source);
}
void transmit_outgoing_over_websocket(char *sentence, size_t length) {
send_websocket_message(sentence, length, "serial");
}
bool JSONEscapeRequired(const char c) {
return (c == '"' || c == '\\' || ('\x00' <= c && c <= '\x1f'));
}
void send_websocket_message(char *data, size_t length, char *source) {
write_to_http_response_buffer("{\"source\":\"");
write_to_http_response_buffer(source);
write_to_http_response_buffer("\",\"sentence\":\"");
// escape data
for (size_t n = 0; n < length; n ++) {
char c = data[n];
if (JSONEscapeRequired(c)) {
write_format_to_http_response_buffer("\\u%04x", int(c));
} else {
write_to_http_response_buffer(c);
}
}
write_to_http_response_buffer("\"}");
websocket_server.broadcastTXT(
http_response_buffer, http_response_buffer_filled);
clear_http_response_buffer();
}
/** HTTP RESPONSE CALLBACKS **/
bool isThisHost(String host) { String ip = get_device_ip_address().toString(); String name = String(config.mdns_hostname) + ".local"; return ((host == ip) || (host == name) || (host == ip + String(":") + String(HTTP_PORT)) || (host == name + String(":") + String(HTTP_PORT))); }
bool captive_portal() { if (!isThisHost(http_server.hostHeader())) { http_server.sendHeader( "Location", String("http://") + get_device_ip_address().toString(), true); http_server.send(302, "text/plain", ""); return true; } return false; }
void clear_http_response_buffer() { http_response_buffer_filled = 0; } void write_to_http_response_buffer(const char data, uint16_t length) { char buffer_start = &http_response_buffer[http_response_buffer_filled]; uint16_t remaining = HTTP_RESPONSE_BUFFER_REMAINING; if (length > remaining) { DEBUG_PRINTLN("response buffer overflow\n"); length = remaining; } memcpy(buffer_start, data, length); http_response_buffer_filled += length; } void write_to_http_response_buffer(const char data) { char buffer_start = &http_response_buffer[http_response_buffer_filled]; uint16_t remaining = HTTP_RESPONSE_BUFFER_REMAINING; // Add one for the zero byte at the end. remaining += 1; size_t result = strlcpy(buffer_start, data, remaining); http_response_buffer_filled += min(result, remaining - 1);; if (result >= remaining) { DEBUG_PRINTLN("response buffer overflow"); } } void write_to_http_response_buffer(char c) { if (HTTP_RESPONSE_BUFFER_REMAINING >= 1) { http_response_buffer[http_response_buffer_filled] = c; http_response_buffer_filled ++; } } void write_to_http_response_buffer(String str) { write_to_http_response_buffer(str.c_str(), str.length()); } void write_format_to_http_response_buffer(char format, ...) { char buffer_start = &http_response_buffer[http_response_buffer_filled]; uint16_t remaining = HTTP_RESPONSE_BUFFER_REMAINING; // Add one for the zero byte at the end. remaining += 1;
va_list args;
va_start(args, format);
size_t result = vsnprintf(buffer_start, remaining, format, args);
va_end(args);
http_response_buffer_filled += min(result, remaining - 1);;
if (result >= HTTP_RESPONSE_BUFFER_REMAINING + 1) {
DEBUG_PRINTLN("response buffer overflow\n");
}
}
void send_http_response_buffer() { http_response_buffer[http_response_buffer_filled] = '\0'; String html = http_response_buffer; // TODO avoid the copy? http_server.send(200, "text/html", html); clear_http_response_buffer(); }
const String html_link(String url, String text) { return String("<a href=\"") + url + String("\">") + text + String(""); }
void write_info_html_section_start(const char title) {
write_to_http_response_buffer("");
write_to_http_response_buffer(title);
write_to_http_response_buffer("
");
}
void write_info_html_field(const char name, String value) {
write_to_http_response_buffer("
");
} ");
}
void write_info_html_section_end() {
write_to_http_response_buffer("");
write_to_http_response_buffer(name);
write_to_http_response_buffer(" ");
write_to_http_response_buffer(value);
write_to_http_response_buffer("
void send_info_page_response() { / Request handler for the info page. /
if (captive_portal()) {return;}
String device_ip = get_device_ip_address().toString();
write_to_http_response_buffer(html_start);
write_info_html_section_start("Network");
if (is_network_config_changed) {
write_to_http_response_buffer(
"<p class=\"note\">"
"The network configuration has been updated "
"since the device started. "
"Changes will be applied after restart."
"</p>");
}
write_info_html_field(
"Wifi mode",
display_wifi_mode(effective_wifi_mode));
write_info_html_field(
"Wifi SSID",
effective_wifi_ssid);
write_info_html_field(
"IP Address",
device_ip);
write_info_html_field(
"Hostname",
config.mdns_hostname[0] == '\0' ?
"undefined" :
html_link(
String("http://") + config.mdns_hostname + String(".local/"),
config.mdns_hostname));
write_info_html_field(
"MAC Address",
WiFi.macAddress());
if (effective_wifi_mode == WifiMode::access_point) {
write_info_html_field(
"Connected Stations",
String(WiFi.softAPgetStationNum()));
}
write_info_html_section_end();
write_info_html_section_start("Connection");
write_info_html_field(
"Transmit Mode",
display_tx_mode(config.tx_mode));
String tx_addr = (
String("udp://") + effective_tx_address.toString()
+ String(":") + String(config.tx_port));
write_info_html_field(
"Transmit",
html_link(tx_addr, tx_addr));
String rx_addr = (
String("udp://") + device_ip + String(":") + String(config.rx_port));
write_info_html_field(
"Receive",
html_link(rx_addr, rx_addr));
write_info_html_section_end();
write_info_html_section_start("Device");
#if SERIAL_SAME
write_info_html_field(
"Baudrate",
String(baudrate_options[config.rx_baudrate]));
#else
write_info_html_field(
"RX Baudrate",
String(baudrate_options[config.rx_baudrate]));
write_info_html_field(
"TX Baudrate",
String(baudrate_options[config.tx_baudrate]));
#endif
write_info_html_field(
"RX Counter",
String(nmea_sentences_received));
write_info_html_field(
"TX Counter",
String(nmea_sentences_sent));
write_info_html_field(
"Voltage",
String(readvdd33() / 1000.0, 2) + String("V"));
write_info_html_field(
"Uptime",
get_uptime_display());
write_info_html_field(
"Device ID",
String(system_get_chip_id()));
write_info_html_section_end();
write_to_http_response_buffer(html_end);
send_http_response_buffer();
}
void write_form_html_field_start(const char title, const char tag) { write_to_http_response_buffer("<"); write_to_http_response_buffer(tag); write_to_http_response_buffer(" class=\"field\">
void write_form_html_heading(String title) { write_to_http_response_buffer("
void write_form_html_field(const char type, const char name, String value, const char title, const char help, const char *attrs) { write_form_html_field_start(title, "label"); write_form_html_help(help); write_to_http_response_buffer("<input type=\""); write_to_http_response_buffer(type); write_to_http_response_buffer("\" name=\""); write_to_http_response_buffer(name); write_to_http_response_buffer("\" value=\""); write_to_http_response_buffer(value); write_to_http_response_buffer("\""); write_to_http_response_buffer(attrs); write_to_http_response_buffer("/>"); }
void write_form_html_options_start(const char title, const char help) { write_form_html_field_start(title, "div"); write_form_html_help(help); write_to_http_response_buffer("<div class=\"optgroup\">"); } void write_form_html_options_item(const char name, const char value, const char title, const char attrs, bool checked) { write_to_http_response_buffer("<label class=\"option\">"); write_to_http_response_buffer("<input name=\""); write_to_http_response_buffer(name); write_to_http_response_buffer("\" value=\""); write_to_http_response_buffer(value); write_to_http_response_buffer("\" type=\"radio\""); if (checked) { write_to_http_response_buffer(" checked"); } write_to_http_response_buffer(attrs); write_to_http_response_buffer("/>
"); write_to_http_response_buffer(title); write_to_http_response_buffer("
"); } void write_form_html_options_end() { write_to_http_response_buffer("void write_form_html_select_start(const char name, const char title, const char help, const char attrs) { write_form_html_field_start(title, "label"); write_form_html_help(help); write_to_http_response_buffer("<select name=\""); write_to_http_response_buffer(name); write_to_http_response_buffer("\""); write_to_http_response_buffer(attrs); write_to_http_response_buffer(">"); } void write_form_html_select_item(String value, String title, bool selected) { write_to_http_response_buffer("<option value=\""); write_to_http_response_buffer(value); write_to_http_response_buffer("\""); if (selected) { write_to_http_response_buffer(" selected"); } write_to_http_response_buffer(">"); write_to_http_response_buffer(title); write_to_http_response_buffer(""); } void write_form_html_select_end() { write_to_http_response_buffer(""); } String ip_address_form_value(IPAddress addr) { if (addr.isSet()) { return addr.toString(); } else { return ""; } }
void write_form_html_baudrate_items(uint8_t selected_baudrate) { for (int n = 1; n <= BAUDRATE_OPTION_COUNT; n ++) { write_form_html_select_item( String(n), String(baudrate_options[n]), selected_baudrate == n); } }
void send_config_form_response() { / Request handler for the config form page. /
if (captive_portal()) {return;}
if (http_server.method() == HTTP_POST) {
send_config_form_post_response();
return;
}
write_to_http_response_buffer(html_start);
write_to_http_response_buffer(
"<form method=\"post\"><h1>Configure</h1>");
write_form_html_heading("Network");
write_form_html_options_start("Wifi Mode", "");
write_form_html_options_item(
"1", "1", "Connect to existing access point", " required",
config.wifi_mode == WifiMode::station);
write_form_html_options_item(
"1", "2", "Create a new wifi access point", " required",
config.wifi_mode == WifiMode::access_point);
write_form_html_options_end();
write_form_html_field("text", "2", config.wifi_ssid, "Wifi SSID", "", " required");
write_form_html_field("text", "3", config.wifi_password, "Wifi password", "", " required");
write_form_html_field("text", "4", ip_address_form_value(config.static_ip_address), "IP Address", "Leave empty to get an address by DHCP.", "");
write_form_html_field("text", "5", config.mdns_hostname, "mDNS Hostname", "Leave empty to skip setting up mDNS.", "");
write_form_html_heading("Connection");
write_form_html_options_start("Transmit Mode", "");
write_form_html_options_item(
"6", "1", "Unicast", " required",
config.tx_mode == TransmitMode::unicast);
write_form_html_options_item(
"6", "2", "Multicast", " required",
config.tx_mode == TransmitMode::multicast);
write_form_html_options_item(
"6", "3", "Broadcast", " required",
config.tx_mode == TransmitMode::broadcast);
write_form_html_options_end();
write_form_html_field("text", "7", ip_address_form_value(config.tx_address), "UDP Transmit Address", "In broadcast mode, only the global broadcast address is used.<br>In unicast mode, this must be a valid unicast address.", "");
write_form_html_field("number", "8", String(config.tx_port), "UDP Transmit Port", "", " min=\"1\" max=\"65535\" required");
write_form_html_field("number", "9", String(config.rx_port), "UDP Receive Port", "", " min=\"1\" max=\"65535\" required");
write_form_html_heading("Device");
#if SERIAL_SAME
write_form_html_select_start("a", "Baudrate", "", " required");
write_form_html_baudrate_items(config.rx_baudrate);
write_form_html_select_end();
#else
write_form_html_select_start("a", "Receive Baudrate", "", " required");
write_form_html_baudrate_items(config.rx_baudrate);
write_form_html_select_end();
write_form_html_select_start("b", "Transmit Baudrate", "", " required");
write_form_html_baudrate_items(config.tx_baudrate);
write_form_html_select_end();
#endif
write_to_http_response_buffer(
"<button type=\"submit\">Save</button></form>");
write_to_http_response_buffer(html_end);
send_http_response_buffer();
}
void send_config_form_post_response() { / Request handler for the config form POST. /
Configuration new_config = config; // copy config
IPAddress addr;
uint16_t port;
WifiMode wifi_mode;
TransmitMode tx_mode;
uint8_t baudrate;
bool tx_config_changed = false;
bool rx_config_changed = false;
bool mdns_hostname = false;
bool tx_baudrate_changed = false;
bool rx_baudrate_changed = false;
for (uint8_t i = 0; i < http_server.args(); i++) {
String argname = http_server.argName(i);
const char *argname_str = argname.c_str();
if (argname_str[0] == '\0' || argname_str[1] != '\0') {
// Skip all param names that aren't a single character.
continue;
}
String argvalue = http_server.arg(i);
const char *argvalue_str = argvalue.c_str();
switch (argname_str[0]) {
case '1':
if (argvalue == "1") {
wifi_mode = WifiMode::station;
} else if (argvalue == "2") {
wifi_mode = WifiMode::access_point;
} else {
continue;
}
if (wifi_mode != new_config.wifi_mode) {
new_config.wifi_mode = wifi_mode;
is_network_config_changed = true;
}
break;
case '2':
strlcpy(new_config.wifi_ssid, argvalue_str, MAX_WIFI_SSID_SIZE);
if (strcmp(config.wifi_ssid, new_config.wifi_ssid) != 0) {
is_network_config_changed = true;
}
break;
case '3':
strlcpy(new_config.wifi_password, argvalue_str, MAX_WIFI_PASSWORD_SIZE);
if (strcmp(config.wifi_password, new_config.wifi_password) != 0) {
is_network_config_changed = true;
}
break;
case '4':
if (addr.fromString(argvalue)) {
if (addr != new_config.static_ip_address) {
new_config.static_ip_address = addr;
is_network_config_changed = true;
}
}
break;
case '5':
strlcpy(new_config.mdns_hostname, argvalue_str, MAX_MDNS_HOSTNAME_SIZE);
if (strcmp(config.mdns_hostname, new_config.mdns_hostname) != 0) {
mdns_hostname = true;
}
break;
case '6':
if (argvalue == "1") {
tx_mode = TransmitMode::unicast;
} else if (argvalue == "2") {
tx_mode = TransmitMode::multicast;
} else if (argvalue == "3") {
tx_mode = TransmitMode::broadcast;
} else {
continue;
}
if (tx_mode != new_config.tx_mode) {
new_config.tx_mode = tx_mode;
tx_config_changed = true;
}
break;
case '7':
if (addr.fromString(argvalue)) {
if (addr != new_config.tx_address) {
new_config.tx_address = addr;
tx_config_changed = true;
}
}
break;
case '8':
port = atoi(argvalue_str);
if (port == new_config.tx_port) {
continue;
}
if ((0 < port) && (port <= 65535)) {
new_config.tx_port = port;
tx_config_changed = true;
}
break;
case '9':
port = atoi(argvalue_str);
if (port == new_config.rx_port) {
continue;
}
if ((0 < port) && (port <= 65535)) {
new_config.rx_port = port;
rx_config_changed = true;
}
break;
case 'a':
baudrate = argvalue_str[0] - '0';
if ((1 <= baudrate) && (baudrate <= BAUDRATE_OPTION_COUNT)) {
if (baudrate != new_config.rx_baudrate) {
new_config.rx_baudrate = baudrate;
#if SERIAL_SAME
new_config.tx_baudrate = baudrate;
#endif
rx_baudrate_changed = true;
}
}
break;
#if !SERIAL_SAME
case 'b':
baudrate = argvalue_str[0] - '0';
if ((1 <= baudrate) && (baudrate <= BAUDRATE_OPTION_COUNT)) {
if (baudrate != new_config.tx_baudrate) {
new_config.tx_baudrate = baudrate;
tx_baudrate_changed = true;
}
}
break;
#endif
}
}
config = new_config;
store_config_to_eeprom(new_config);
if (tx_config_changed) {
update_tx_config();
}
if (rx_config_changed) {
update_rx_config();
}
if (mdns_hostname) {
update_mdns_hostname();
}
#if SERIAL_SAME
if (rx_baudrate_changed) {
update_rx_baudrate();
}
#else
if (tx_baudrate_changed) {
update_tx_baudrate();
}
if (rx_baudrate_changed) {
update_rx_baudrate();
}
#endif
http_server.sendHeader(
"Location",
http_server.uri(),
true);
http_server.send(302, "text/plain", "");
}
void nmea_log_page_response() { / Request handler for the NMEA log page. / String body = html_start; write_to_http_response_buffer(html_start); write_to_http_response_buffer(html_log_content); write_to_http_response_buffer(html_end); send_http_response_buffer(); }
void send_js_log_script() STATIC_FILE_REQUEST_HANDLER(JS_CONTENT_TYPE, js_log_script);
void send_css_style_response() STATIC_FILE_REQUEST_HANDLER(CSS_CONTENT_TYPE, css_style);
void send_404_not_found_response() { / Request handler for undefined urls. /
if (captive_portal()) {return;}
String body = html_start;
body += "<h1>Page not found</h1>";
body += "<p>Happy sailing!</p>";
body += html_end;
http_server.send(404, "text/html", body);
}
/** EEPROM SETTINGS STORAGE **/
void load_config_from_eeprom(Configuration &new_config) { /* Load config from persistent memory. * The config is written to the passed instance. / EEPROM.get(CONFIG_EEPROM_ADDRESS, new_config); }
void store_config_to_eeprom(Configuration new_config) { / Store the given config to persistent memory. / EEPROM.put(CONFIG_EEPROM_ADDRESS, new_config); EEPROM.commit(); }
void fix_config() { / Update invalid config values to valid defaults. / bool config_changed = false;
if (config.wifi_mode == 0 || config.wifi_mode > 2) {
config.wifi_mode = default_config.wifi_mode;
config_changed = true;
}
if (config.wifi_ssid[0] == '\0') {
strlcpy(config.wifi_ssid, default_config.wifi_ssid, MAX_WIFI_SSID_SIZE);
config_changed = true;
}
config.wifi_ssid[MAX_WIFI_SSID_SIZE - 1] = '\0';
if (config.wifi_password[0] == '\0') {
strlcpy(config.wifi_password, default_config.wifi_password, MAX_WIFI_PASSWORD_SIZE);
config_changed = true;
}
config.wifi_password[MAX_WIFI_PASSWORD_SIZE - 1] = '\0';
if (!config.static_ip_address.isSet()) {
config.static_ip_address = default_config.static_ip_address;
config_changed = true;
}
if (config.mdns_hostname[0] == '\0') {
strlcpy(config.mdns_hostname, default_config.mdns_hostname, MAX_MDNS_HOSTNAME_SIZE);
config_changed = true;
}
config.mdns_hostname[MAX_MDNS_HOSTNAME_SIZE - 1] = '\0';
if (config.tx_mode == 0 || config.tx_mode > 3) {
config.tx_mode = default_config.tx_mode;
config_changed = true;
}
if (!config.tx_address.isSet()) {
config.tx_address = default_config.tx_address;
config_changed = true;
}
if (!((0 < config.tx_port) && (config.tx_port <= 65535))) {
config.tx_port = default_config.tx_port;
config_changed = true;
}
if (!((0 < config.rx_port) && (config.rx_port <= 65535))) {
config.rx_port = default_config.rx_port;
config_changed = true;
}
if (config.tx_baudrate == 0 || config.tx_baudrate > BAUDRATE_OPTION_COUNT) {
config.tx_baudrate = default_config.tx_baudrate;
config_changed = true;
}
if (config.rx_baudrate == 0 || config.rx_baudrate > BAUDRATE_OPTION_COUNT) {
config.rx_baudrate = default_config.rx_baudrate;
config_changed = true;
}
if (config_changed) {
store_config_to_eeprom(config);
}
}
/**** UTILITY FUNCTIONS ****/
void update_mdns_hostname() { /* Update the mDNS hostname, * used then the config has changed. / MDNS.setInstanceName(config.mdns_hostname);
// TODO this doen't work, how to change the mDNS hostname while running?
// https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266mDNS/src/ESP8266mDNS_Legacy.cpp
// MDNS.begin(config.mdns_hostname); // MDNS.notifyAPChange(); } void update_tx_config() { /* Update the network transmit state, used then the config has changed. / if (config.tx_mode == TransmitMode::broadcast) { if (config.tx_address == INADDR_NONE) { // Special case; if the transmit address is set // to 255.255.255.255, use this as broadcast address. effective_tx_address = INADDR_NONE; } else { IPAddress gateway; IPAddress subnet_mask; if (effective_wifi_mode == WifiMode::access_point) { gateway = WIFI_ACCESS_POINT_GATEWAY_IP; subnet_mask = WIFI_ACCESS_POINT_SUBNET_IP; } else { gateway = WiFi.subnetMask(); subnet_mask = WiFi.gatewayIP(); } effective_tx_address = IPAddress( (~ uint32_t(subnet_mask)) | uint32_t(gateway)); } } else { effective_tx_address = config.tx_address; } } void update_rx_config() { / Update the network receive state, used then the config has changed. / udp_server.stop(); udp_server.begin(config.rx_port); } void update_tx_baudrate() { / Update the transmit serial baudrate, used then the config has changed. / SERIAL_TX.flush(); SERIAL_TX.begin(baudrate_options[config.tx_baudrate]); } void update_rx_baudrate() { / Update the receive serial baudrate, used then the config has changed. */ SERIAL_RX.flush(); SERIAL_RX.begin(baudrate_options[config.rx_baudrate]); }
IPAddress get_device_ip_address() { / Get the ip address of this device. / if (effective_wifi_mode == WifiMode::access_point) { return WiFi.softAPIP(); } else { return WiFi.localIP(); } }
IPAddress gateway_address(IPAddress addr) { /* Get the gateway address based on an ip-address * when connecting to an existing wifi network. / return IPAddress(addr[0], addr[1], addr[2], 1); }
IPAddress subnet_address(IPAddress addr) { /* Get the subnet address-mask based on an ip-address * when connecting to an existing wifi network. / return IPAddress(255, 255, 255, 0); }
unsigned long get_uptime_in_seconds() { / Get the current uptime in seconds. / return millis() / 1000; }
String get_uptime_display() { / Get the current uptime as a human-readable string. / int seconds = get_uptime_in_seconds(); int minutes = seconds / 60; int hours = minutes / 60; int days = hours / 24;
seconds %= 60;
minutes %= 60;
hours %= 24;
char uptime_buffer[9];
snprintf(
uptime_buffer,
sizeof(uptime_buffer),
"%02d:%02d:%02d",
hours, minutes, seconds);
if (days == 0) {
return uptime_buffer;
} else if (days == 1) {
return "1 day, " + String(uptime_buffer);
} else {
return String(days) + " days, " + String(uptime_buffer);
}
}
void set_indicator_led_on() { /* Set the indicator led on, if it isn't already so. / if (indicator_led_state == INDICATOR_LED_OFF) { force_indicator_led_on(); } } void set_indicator_led_off() { / Set the indicator led off, if it isn't already so. / if (indicator_led_state == INDICATOR_LED_ON) { force_indicator_led_off(); } } void force_indicator_led_on() { / Set the indicator led on, without checking the current state. / digitalWrite(INDICATOR_LED_PIN, INDICATOR_LED_ON); indicator_led_state = INDICATOR_LED_ON; } void force_indicator_led_off() { / Set the indicator led off, without checking the current state. / digitalWrite(INDICATOR_LED_PIN, INDICATOR_LED_OFF); indicator_led_state = INDICATOR_LED_OFF; } void toggle_indicator_led() { / Invert the indicator led state. */ if (indicator_led_state == INDICATOR_LED_ON) { force_indicator_led_off(); } else { force_indicator_led_on(); } }
void long_indicator_blink() { / Perform a long-blink. / force_indicator_led_on(); delay(1000); force_indicator_led_off(); }
void update_indicator_slow_blink() { / Update the led state for slow-blinking. / unsigned long time = millis() % 1000; if (time < 500) { set_indicator_led_on(); return; } set_indicator_led_off(); }
void update_indicator_double_blink() { / Update the led state for double-blinking. / unsigned long time = millis() % 1000; if (time < 100) { set_indicator_led_on(); return; } else if (time < 200) { set_indicator_led_off(); return; } else if (time < 300) { set_indicator_led_on(); return; } set_indicator_led_off(); }
bool blink_while_pressing_mode_button() { / Return True if pressed longer than the threshold. / unsigned long start_time = millis(); while (digitalRead(BOOT_MODE_BUTTON_PIN) == LOW) { toggle_indicator_led(); delay(50); if ((millis() - start_time) > RESET_CONFIG_THRESHOLD) { set_indicator_led_off(); return true; } } set_indicator_led_off(); return false; }
void blink_while_connecting_wifi() { / Return when wifi is connected. / while (WiFi.status() != WL_CONNECTED) { toggle_indicator_led(); delay(500); DEBUG_PRINT("."); } set_indicator_led_on(); DEBUG_PRINTLN(""); }`
please post a working sketch (for nodemcu esp8266). I really need to connect nmea 0183 to wifi
Ok, I've solved my problem. On my board, there are actually two buttons: reset and flash. Pressing the reset button resets the board - nothing else. What actually worked was pressing the flash button!
I hope I'll save some poor soul hours of debugging.
The ssid says ESP-XXXXX and the password is unknown. I used a new ESP8266 chip, but everything remains as it was at the beginning. Changing the network name and password is not possible. Use Arduino IDE2.1 It's a shame I would have liked to try this thing out. greeting