├── .gitignore ├── src ├── LED.ino ├── OTA.ino ├── Config.ino ├── ESP232.ino ├── Simulator.ino ├── WiFi.ino ├── Display.ino ├── MQTT.ino ├── Serial.ino ├── SCPI.ino ├── Debug.ino └── Webserver.ino ├── include ├── SCPI.h └── Config.h ├── platformio.ini └── lib └── README /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /src/LED.ino: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | void led_setup() 5 | { 6 | } 7 | 8 | 9 | void led_set(uint8_t n, uint8_t r, uint8_t g, uint8_t b) 10 | { 11 | digitalWrite(LED_PIN, ((r>0) || (g>0) || (b>0)) ? HIGH : LOW ); 12 | } 13 | 14 | bool led_loop() 15 | { 16 | return false; 17 | } 18 | -------------------------------------------------------------------------------- /include/SCPI.h: -------------------------------------------------------------------------------- 1 | #ifndef __SCPI_H__ 2 | #define __SCPI_H__ 3 | 4 | #define SCPI_STATE_IDLE 0 5 | #define SCPI_STATE_STB 1 6 | #define SCPI_STATE_STB_RESPONSE 2 7 | #define SCPI_STATE_OPC 3 8 | #define SCPI_STATE_OPC_RESPONSE 4 9 | #define SCPI_STATE_COMMAND 5 10 | #define SCPI_STATE_RESPONSE 6 11 | 12 | #define SCPI_MAX_COMMAND 64 13 | 14 | typedef struct 15 | { 16 | char command[SCPI_MAX_COMMAND]; 17 | bool has_response; 18 | bool check_error; 19 | void (*resp)(bool success, const char *line); 20 | } t_scpi_command; 21 | 22 | 23 | void scpi_setup(void); 24 | bool scpi_loop(void); 25 | bool scpi_command(const char *command, bool has_response, bool check_error, void (*cbr)(bool success, const char *response)); 26 | 27 | #endif -------------------------------------------------------------------------------- /include/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H__ 2 | #define __CONFIG_H__ 3 | 4 | #define CONFIG_MAGIC 0xE1AAFF00 5 | 6 | #define CONFIG_SOFTAPNAME "esp232-config" 7 | #define CONFIG_OTANAME "ESP232" 8 | 9 | 10 | typedef struct 11 | { 12 | uint32_t magic; 13 | uint32_t verbose; 14 | uint32_t baudrate; 15 | uint32_t databits; 16 | uint32_t parity; 17 | uint32_t stopbits; 18 | char hostname[32]; 19 | char wifi_ssid[32]; 20 | char wifi_password[32]; 21 | char mqtt_server[32]; 22 | int mqtt_port; 23 | char mqtt_user[32]; 24 | char mqtt_password[32]; 25 | char mqtt_client[32]; 26 | int mqtt_publish; 27 | int mqtt_publish_rate; 28 | char connect_string[128]; 29 | char disconnect_string[128]; 30 | } t_cfg; 31 | 32 | 33 | extern t_cfg current_config; 34 | 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:wemos_d1_mini32] 12 | platform = https://github.com/Jason2866/platform-espressif32.git#new_ULP 13 | board = wemos_d1_mini32 14 | framework = arduino 15 | build_flags = !bash -c "echo -Isrc -DPIO_SRC_REVNUM=$(git rev-list --count HEAD) -DPIO_SRC_REV=$(git rev-parse --short HEAD)" 16 | 17 | lib_deps = 18 | knolleary/PubSubClient@^2.8 19 | suculent/ESP32httpUpdate@^2.1.145 20 | ;upload_protocol = espota 21 | ;upload_port = keithley 22 | monitor_speed = 115200 23 | monitor_filters = time, colorize, esp32_exception_decoder 24 | upload_protocol = espota 25 | upload_port = 192.168.1.37 26 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /src/OTA.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | bool ota_active = false; 5 | bool ota_setup_done = false; 6 | long ota_offtime = 0; 7 | 8 | void ota_setup() 9 | { 10 | if (ota_setup_done) 11 | { 12 | ota_enable(); 13 | return; 14 | } 15 | ArduinoOTA.setHostname(CONFIG_OTANAME); 16 | 17 | ArduinoOTA.onStart([]() { 18 | String type; 19 | ota_active = true; 20 | ota_offtime = millis() + 600000; 21 | }) 22 | .onEnd([]() { 23 | ota_active = false; 24 | }) 25 | .onProgress([](unsigned int progress, unsigned int total) { 26 | }) 27 | .onError([](ota_error_t error) { 28 | //Serial.printf("Error[%u]: ", error); 29 | //if (error == OTA_AUTH_ERROR) //Serial.println("Auth Failed"); 30 | //else if (error == OTA_BEGIN_ERROR) //Serial.println("Begin Failed"); 31 | //else if (error == OTA_CONNECT_ERROR) //Serial.println("Connect Failed"); 32 | //else if (error == OTA_RECEIVE_ERROR) //Serial.println("Receive Failed"); 33 | //else if (error == OTA_END_ERROR) //Serial.println("End Failed"); 34 | }); 35 | 36 | ArduinoOTA.begin(); 37 | 38 | //Serial.printf("[OTA] Setup finished\n"); 39 | ota_setup_done = true; 40 | ota_enable(); 41 | } 42 | 43 | void ota_enable() 44 | { 45 | //Serial.printf("[OTA] Enabled\n"); 46 | ota_offtime = millis() + 600000; 47 | } 48 | 49 | bool ota_enabled() 50 | { 51 | return (ota_offtime > millis() || ota_active); 52 | } 53 | 54 | bool ota_loop() 55 | { 56 | if (ota_enabled()) 57 | { 58 | ArduinoOTA.handle(); 59 | } 60 | 61 | return ota_active; 62 | } 63 | -------------------------------------------------------------------------------- /src/Config.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "Config.h" 6 | 7 | t_cfg current_config; 8 | bool config_valid = false; 9 | 10 | void cfg_save() 11 | { 12 | File file = SPIFFS.open("/config.dat", "w"); 13 | if (!file || file.isDirectory()) 14 | { 15 | return; 16 | } 17 | 18 | if (strlen(current_config.hostname) < 2) 19 | { 20 | strcpy(current_config.hostname, "ESP232"); 21 | } 22 | 23 | file.write((uint8_t *)¤t_config, sizeof(current_config)); 24 | file.close(); 25 | } 26 | 27 | void cfg_reset() 28 | { 29 | memset(¤t_config, 0x00, sizeof(current_config)); 30 | 31 | current_config.magic = CONFIG_MAGIC; 32 | strcpy(current_config.hostname, "ESP232"); 33 | 34 | strcpy(current_config.mqtt_server, ""); 35 | current_config.mqtt_port = 11883; 36 | strcpy(current_config.mqtt_user, ""); 37 | strcpy(current_config.mqtt_password, ""); 38 | strcpy(current_config.mqtt_client, "ESP232"); 39 | current_config.mqtt_publish = 2; 40 | current_config.mqtt_publish_rate = 250; 41 | 42 | current_config.baudrate = 115200; 43 | current_config.databits = 8; 44 | current_config.parity = 0; 45 | current_config.stopbits = 1; 46 | current_config.verbose = 0; 47 | strcpy(current_config.connect_string, ""); 48 | strcpy(current_config.disconnect_string, ""); 49 | 50 | strcpy(current_config.wifi_ssid, "(not set)"); 51 | strcpy(current_config.wifi_password, "(not set)"); 52 | } 53 | 54 | void cfg_read() 55 | { 56 | File file = SPIFFS.open("/config.dat", "r"); 57 | 58 | config_valid = false; 59 | 60 | if (!file || file.isDirectory()) 61 | { 62 | cfg_reset(); 63 | } 64 | else 65 | { 66 | file.read((uint8_t *)¤t_config, sizeof(current_config)); 67 | file.close(); 68 | 69 | if (current_config.magic != CONFIG_MAGIC) 70 | { 71 | cfg_reset(); 72 | return; 73 | } 74 | config_valid = true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ESP232.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include 3 | #include 4 | #define LED_PIN 0 5 | const char *sync_str = "ESP8266-232-UPD"; 6 | #elif defined(ESP32) 7 | #include 8 | #include 9 | #define LED_PIN 13 10 | const char *sync_str = "ESP232-UPD\n"; 11 | #else 12 | #error "This ain't a ESP8266 or ESP32, dumbo!" 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | bool config_mode = false; 19 | 20 | void setup() 21 | { 22 | // Serial.begin(115200); 23 | // Serial.printf("\n\n\n"); 24 | 25 | // Serial.printf("[i] SDK: '%s'\n", ESP.getSdkVersion()); 26 | // Serial.printf("[i] CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()); 27 | // Serial.printf("[i] Chip Id: %06llX\n", ESP.getEfuseMac()); 28 | // Serial.printf("[i] Flash Mode: %08X\n", ESP.getFlashChipMode()); 29 | // Serial.printf("[i] Flash Size: %08X\n", ESP.getFlashChipSize()); 30 | // Serial.printf("[i] Flash Speed: %d MHz\n", ESP.getFlashChipSpeed() / 1000000); 31 | // Serial.printf("[i] Heap %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize()); 32 | // Serial.printf("[i] SPIRam %d/%d\n", ESP.getFreePsram(), ESP.getPsramSize()); 33 | // Serial.printf("\n"); 34 | // Serial.printf("[i] Starting\n"); 35 | // Serial.printf("[i] Setup SPIFFS\n"); 36 | if (!SPIFFS.begin(true)) 37 | { 38 | // Serial.println("[E] SPIFFS Mount Failed"); 39 | } 40 | 41 | cfg_read(); 42 | scpi_setup(); 43 | wifi_setup(); 44 | www_setup(); 45 | serial_setup(); 46 | dbg_setup(); 47 | 48 | if (has_loopback()) 49 | { 50 | ota_enable(); 51 | config_mode = true; 52 | } 53 | } 54 | 55 | bool has_loopback() 56 | { 57 | return false; 58 | 59 | bool ret = false; 60 | char receive_buf[32]; 61 | 62 | // Serial.write((uint8_t *)sync_str, strlen(sync_str)); 63 | // Serial.setTimeout(5); 64 | 65 | // Serial.readBytes(receive_buf, strlen(sync_str)); 66 | 67 | ret = !strncmp(sync_str, receive_buf, strlen(sync_str)); 68 | 69 | return ret; 70 | } 71 | 72 | void loop() 73 | { 74 | bool hasWork = false; 75 | 76 | if (config_mode) 77 | { 78 | pinMode(LED_PIN, OUTPUT); 79 | digitalWrite(LED_PIN, (millis() % 500) > 250); 80 | wifi_loop(); 81 | www_loop(); 82 | ota_loop(); 83 | return; 84 | } 85 | 86 | // if (!ota_active) 87 | { 88 | hasWork |= dbg_loop(); 89 | hasWork |= scpi_loop(); 90 | hasWork |= wifi_loop(); 91 | hasWork |= www_loop(); 92 | hasWork |= serial_loop_rx(); 93 | hasWork |= serial_loop_tx(); 94 | hasWork |= mqtt_loop(); 95 | hasWork |= disp_loop(); 96 | } 97 | hasWork |= ota_loop(); 98 | 99 | if (!hasWork) 100 | { 101 | delay(10); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Simulator.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define TEST_ESP_OK(x,ret) do { if((x) != ESP_OK) return ret; } while(0) 5 | #define TEST_ASSERT_NOT_EQUAL(x,y) 6 | #define TEST_ASSERT_EQUAL(x,y) 7 | #define TEST_ASSERT_NOT_NULL(x) 8 | 9 | const uart_port_t uart_echo = UART_NUM_1; 10 | char sim_rxbuf[1024]; 11 | int sim_rxpos = 0; 12 | 13 | int32_t sim_setup() 14 | { 15 | uart_config_t uart_config = 16 | { 17 | .baud_rate = (int)current_config.baudrate, 18 | .data_bits = UART_DATA_8_BITS, 19 | .parity = UART_PARITY_EVEN, 20 | .stop_bits = UART_STOP_BITS_2, 21 | .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, 22 | .source_clk = UART_SCLK_APB, 23 | }; 24 | 25 | const int uart0_tx_signal = U0TXD_OUT_IDX; 26 | const int uart1_tx_signal = U1TXD_OUT_IDX; 27 | const int uart0_tx = 1; 28 | const int uart0_rx = 13; 29 | const int uart1_tx = 14; 30 | const int uart1_rx = 1; 31 | const int buf_size = 256; 32 | const int intr_alloc_flags = 0; 33 | 34 | 35 | TEST_ESP_OK(uart_driver_install(uart_echo, buf_size * 2, 0, 0, NULL, intr_alloc_flags), -1); 36 | TEST_ESP_OK(uart_param_config(uart_echo, &uart_config), -2); 37 | TEST_ESP_OK(uart_set_pin(uart_echo, uart1_tx, uart1_rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), -3); 38 | 39 | /* reconfigure UART 0 */ 40 | uart_set_pin(UART_NUM_0, 1, 13, -1, -1); 41 | esp_rom_gpio_connect_out_signal(uart0_rx, uart1_tx_signal, false, false); 42 | esp_rom_gpio_connect_out_signal(uart1_rx, uart0_tx_signal, false, false); 43 | 44 | 45 | return 0; 46 | } 47 | 48 | void sim_parse(char *line) 49 | { 50 | const char *response = ""; 51 | 52 | if(line[0] == '>') 53 | { 54 | return; 55 | } 56 | 57 | if(!strcmp(line, "*STB?")) 58 | { 59 | response = "0\n"; 60 | } 61 | else if(!strcmp(line, "*OPC?")) 62 | { 63 | response = "1\n"; 64 | } 65 | else if(!strcmp(line, "*CLS")) 66 | { 67 | response = ""; 68 | } 69 | else if(!strcmp(line, "DISP:WIND1:DATA?")) 70 | { 71 | response = "\" -0.0000 A\"\n"; 72 | } 73 | 74 | if(strlen(response)) 75 | { 76 | uart_write_bytes(uart_echo, response, strlen(response)); 77 | serial_println(response); 78 | } 79 | } 80 | 81 | 82 | bool sim_loop() 83 | { 84 | const uart_port_t uart_echo = UART_NUM_1; 85 | const int buf_size = 256; 86 | 87 | while(true) 88 | { 89 | char rcvd; 90 | int len = uart_read_bytes(uart_echo, &rcvd, 1, 1); 91 | 92 | if(len <= 0) 93 | { 94 | break; 95 | } 96 | 97 | char msg[2]; 98 | 99 | msg[0] = rcvd; 100 | msg[1] = 0; 101 | 102 | if(rcvd == '\n') 103 | { 104 | sim_rxbuf[sim_rxpos] = '\000'; 105 | sim_parse(sim_rxbuf); 106 | sim_rxpos = 0; 107 | } 108 | else if(rcvd == '\r') 109 | { 110 | } 111 | else 112 | { 113 | sim_rxbuf[sim_rxpos++] = rcvd; 114 | sim_rxpos %= sizeof(sim_rxbuf); 115 | } 116 | } 117 | 118 | return true; 119 | } 120 | -------------------------------------------------------------------------------- /src/WiFi.ino: -------------------------------------------------------------------------------- 1 | #include 2 | DNSServer dnsServer; 3 | 4 | bool connecting = false; 5 | bool wifi_captive = false; 6 | int wifi_rssi = 0; 7 | char wifi_error[64]; 8 | 9 | void wifi_setup() 10 | { 11 | //Serial.printf("[WiFi] Connecting to '%s', password '%s'...\n", current_config.wifi_ssid, current_config.wifi_password); 12 | sprintf(wifi_error, ""); 13 | WiFi.begin(current_config.wifi_ssid, current_config.wifi_password); 14 | connecting = true; 15 | 16 | pinMode(LED_PIN, OUTPUT); 17 | digitalWrite(LED_PIN, LOW); 18 | } 19 | 20 | void wifi_off() 21 | { 22 | connecting = false; 23 | WiFi.disconnect(); 24 | WiFi.mode(WIFI_OFF); 25 | } 26 | 27 | void wifi_enter_captive() 28 | { 29 | wifi_off(); 30 | WiFi.softAP(CONFIG_SOFTAPNAME); 31 | dnsServer.start(53, "*", WiFi.softAPIP()); 32 | //Serial.printf("[WiFi] Local IP: %s\n", WiFi.softAPIP().toString().c_str()); 33 | 34 | wifi_captive = true; 35 | 36 | /* reset captive idle timer */ 37 | www_activity(); 38 | } 39 | 40 | bool wifi_loop(void) 41 | { 42 | int status = WiFi.status(); 43 | int curTime = millis(); 44 | static int nextTime = 0; 45 | static int stateCounter = 0; 46 | 47 | if (wifi_captive) 48 | { 49 | dnsServer.processNextRequest(); 50 | digitalWrite(LED_PIN, ((millis() % 250) > 125) ? LOW : HIGH); 51 | 52 | /* captive mode, but noone cares */ 53 | if (!www_is_captive_active()) 54 | { 55 | Serial.printf("[WiFi] Timeout in captive, trying known networks again\n"); 56 | sprintf(wifi_error, "Timeout in captive, trying known networks again"); 57 | dnsServer.stop(); 58 | wifi_off(); 59 | wifi_captive = false; 60 | stateCounter = 0; 61 | sprintf(wifi_error, ""); 62 | } 63 | return true; 64 | } 65 | 66 | if (nextTime > curTime) 67 | { 68 | return false; 69 | } 70 | 71 | /* standard refresh time */ 72 | nextTime = curTime + 500; 73 | 74 | /* when stuck at a state, disconnect */ 75 | if (++stateCounter > 20) 76 | { 77 | // Serial.printf("[WiFi] Timeout connecting\n"); 78 | sprintf(wifi_error, "Timeout - incorrect password?"); 79 | wifi_off(); 80 | } 81 | 82 | if (strcmp(wifi_error, "")) 83 | { 84 | // Serial.printf("[WiFi] Entering captive mode. Reason: '%s'\n", wifi_error); 85 | 86 | wifi_enter_captive(); 87 | 88 | stateCounter = 0; 89 | return false; 90 | } 91 | 92 | switch (status) 93 | { 94 | case WL_CONNECTED: 95 | if (connecting) 96 | { 97 | connecting = false; 98 | serial_println("[WiFi] Connected, IP address: "); 99 | serial_println(WiFi.localIP().toString().c_str()); 100 | stateCounter = 0; 101 | sprintf(wifi_error, ""); 102 | digitalWrite(LED_PIN, HIGH); 103 | } 104 | else 105 | { 106 | wifi_rssi = WiFi.RSSI(); 107 | 108 | /* happy with this state, reset counter */ 109 | stateCounter = 0; 110 | } 111 | break; 112 | 113 | case WL_CONNECTION_LOST: 114 | //Serial.printf("[WiFi] Connection lost\n"); 115 | sprintf(wifi_error, "Network found, but connection lost"); 116 | wifi_off(); 117 | break; 118 | 119 | case WL_CONNECT_FAILED: 120 | //Serial.printf("[WiFi] Connection failed\n"); 121 | sprintf(wifi_error, "Network found, but connection failed"); 122 | wifi_off(); 123 | break; 124 | 125 | case WL_NO_SSID_AVAIL: 126 | //Serial.printf("[WiFi] No SSID with that name\n"); 127 | sprintf(wifi_error, "Network not found"); 128 | wifi_off(); 129 | break; 130 | 131 | case WL_SCAN_COMPLETED: 132 | //Serial.printf("[WiFi] Scan completed\n"); 133 | wifi_off(); 134 | break; 135 | 136 | case WL_DISCONNECTED: 137 | if(!connecting) 138 | { 139 | //Serial.printf("[WiFi] Disconnected\n"); 140 | wifi_off(); 141 | } 142 | break; 143 | 144 | case WL_IDLE_STATUS: 145 | if (!connecting) 146 | { 147 | connecting = true; 148 | //Serial.printf("[WiFi] Idle, connect to '%s'\n", current_config.wifi_ssid); 149 | WiFi.mode(WIFI_STA); 150 | WiFi.begin(current_config.wifi_ssid, current_config.wifi_password); 151 | } 152 | else 153 | { 154 | //Serial.printf("[WiFi] Idle, connecting...\n"); 155 | } 156 | break; 157 | 158 | case WL_NO_SHIELD: 159 | if (!connecting) 160 | { 161 | connecting = true; 162 | //Serial.printf("[WiFi] Disabled (%d), connecting to '%s'\n", status, current_config.wifi_ssid); 163 | WiFi.mode(WIFI_STA); 164 | WiFi.begin(current_config.wifi_ssid, current_config.wifi_password); 165 | } 166 | break; 167 | 168 | default: 169 | //Serial.printf("[WiFi] unknown (%d), disable\n", status); 170 | wifi_off(); 171 | break; 172 | } 173 | 174 | return false; 175 | } 176 | -------------------------------------------------------------------------------- /src/Display.ino: -------------------------------------------------------------------------------- 1 | 2 | #include "scpi.h" 3 | 4 | static int line_pos = 0; 5 | static uint8_t line_buffer[64]; 6 | static bool line_queued = false; 7 | 8 | 9 | void disp_init() 10 | { 11 | 12 | } 13 | 14 | bool disp_loop() 15 | { 16 | uint32_t time = millis(); 17 | static uint32_t nextTime = 0; 18 | 19 | if(current_config.mqtt_publish) 20 | { 21 | if(time < nextTime) 22 | { 23 | return false; 24 | } 25 | if(line_queued && time > nextTime + 500) 26 | { 27 | line_queued = false; 28 | } 29 | if(line_queued) 30 | { 31 | return false; 32 | } 33 | nextTime = time + current_config.mqtt_publish_rate; 34 | line_queued = true; 35 | 36 | if((current_config.mqtt_publish & 8)) 37 | { 38 | scpi_command(":MEAS?", true, true, &disp_parse_meas); 39 | } 40 | else 41 | { 42 | if((current_config.mqtt_publish & 4) == 0) 43 | { 44 | scpi_command(":INIT", false, true, NULL); 45 | } 46 | scpi_command("DISP:WIND1:DATA?", true, true, &disp_parse); 47 | } 48 | } 49 | return line_queued; 50 | } 51 | 52 | double disp_parse_float(const char *line, char *unit) 53 | { 54 | int pos = 0; 55 | double sign = 1; 56 | double value = 0; 57 | int decimal = 10; 58 | 59 | while(line[pos] == ' ') 60 | { 61 | pos++; 62 | } 63 | 64 | if(line[pos] == '-') 65 | { 66 | sign = -1; 67 | pos++; 68 | } 69 | else if(line[pos] == '+') 70 | { 71 | pos++; 72 | } 73 | 74 | while(isdigit(line[pos])) 75 | { 76 | value *= 10.0f; 77 | value += (double)(line[pos] - '0'); 78 | pos++; 79 | } 80 | 81 | if(line[pos] == '.') 82 | { 83 | pos++; 84 | while(isdigit(line[pos])) 85 | { 86 | value += (double)(line[pos] - '0') / (double)decimal; 87 | pos++; 88 | decimal *= 10; 89 | } 90 | } 91 | 92 | switch(line[pos]) 93 | { 94 | case 'n': 95 | value /= 1000000000.0f; 96 | break; 97 | case 'u': 98 | value /= 1000000.0f; 99 | break; 100 | case 'm': 101 | value /= 1000.0f; 102 | break; 103 | case 'k': 104 | value *= 1000.0f; 105 | break; 106 | case 'M': 107 | value *= 1000000.0f; 108 | break; 109 | case 'G': 110 | value *= 1000000000.0f; 111 | break; 112 | } 113 | 114 | value *= sign; 115 | 116 | pos++; 117 | 118 | *unit = line[pos]; 119 | 120 | return value; 121 | } 122 | 123 | void disp_parse(bool success, const char *src_line) 124 | { 125 | int start = 0; 126 | char line[64]; 127 | 128 | mqtt_publish_string("debug/string/%s/src_line", src_line); 129 | mqtt_publish_string("debug/string/%s/success", success ? "true" : "false"); 130 | 131 | if(!success) 132 | { 133 | return; 134 | } 135 | 136 | strncpy(line, src_line, sizeof(line)); 137 | 138 | if(line[0] != '"') 139 | { 140 | return; 141 | } 142 | 143 | /* quit RS232 mode again */ 144 | if(current_config.mqtt_publish & 4) 145 | { 146 | scpi_command(":SYST:LOC", false, false, NULL); 147 | } 148 | 149 | line_queued = false; 150 | 151 | 152 | /* skip first quotation mark and spaces */ 153 | while((line[start] == ' ') || (line[start] == '"')) 154 | { 155 | start++; 156 | } 157 | 158 | /* translate symbols and skip trailing quot */ 159 | int pos = start; 160 | 161 | while(line[pos]) 162 | { 163 | if(line[pos] == '\x10') 164 | { 165 | line[pos] = 'u'; 166 | } 167 | else if(line[pos] == '\x11') 168 | { 169 | line[pos] = '+'; 170 | } 171 | else if(line[pos] == '\x12') 172 | { 173 | line[pos] = 'O'; 174 | } 175 | if(line[pos] == '"') 176 | { 177 | line[pos] = '\000'; 178 | } 179 | 180 | pos++; 181 | } 182 | 183 | if(current_config.mqtt_publish & 1) 184 | { 185 | mqtt_publish_string("feeds/string/%s/display", &line[start]); 186 | } 187 | 188 | if(current_config.mqtt_publish & 2) 189 | { 190 | char path[32]; 191 | char valstr[32]; 192 | char unit; 193 | double value = disp_parse_float(&line[start], &unit); 194 | 195 | strcpy(path, "feeds/float/%s/value_X"); 196 | path[21] = unit; 197 | 198 | sprintf(valstr, "%g", value); 199 | 200 | mqtt_publish_string(path, valstr); 201 | } 202 | } 203 | 204 | void disp_parse_meas(bool success, const char *src_line) 205 | { 206 | if(!success) 207 | { 208 | return; 209 | } 210 | 211 | /* +4.016779E+00,+1.001000E+00,+9.910000E+37,+6.041777E+02,+3.584400E+04 */ 212 | float meas_u, meas_i, meas_r, meas_t, status; 213 | /* 214 | Bit 0 (OFLO) — Set to 1 if measurement was made while in over-range. 215 | Bit 1 (Filter) — Set to 1 if measurement was made with the filter enabled. 216 | Bit 2 (Front/Rear) — Set to 1 if FRONT terminals are selected. 217 | Bit 3 (Compliance) — Set to 1 if in real compliance. 218 | Bit 4 (OVP) — Set to 1 if the over voltage protection limit was reached. 219 | Bit 5 (Math) — Set to 1 if math expression (calc1) is enabled. 220 | Bit 6 (Null) — Set to 1 if Null is enabled. 221 | Bit 7 (Limits) — Set to 1 if a limit test (calc2) is enabled. 222 | Bits 8 and 9 (Limit Results) — Provides limit test results (see grading and sorting modes below). 223 | Bit 10 (Auto-ohms) — Set to 1 if auto-ohms enabled. 224 | Bit 11 (V-Meas) — Set to 1 if V-Measure is enabled. 225 | Bit 12 (I-Meas) — Set to 1 if I-Measure is enabled. 226 | Bit 13 (Ω-Meas) — Set to 1 if Ω-Measure is enabled. 227 | Bit 14 (V-Sour) — Set to 1 if V-Source used. 228 | Bit 15 (I-Sour) — Set to 1 if I-Source used. 229 | Bit 16 (Range Compliance) — Set to 1 if in range compliance. 230 | Bit 17 (Offset Compensation) — Set to 1 if Offset Compensated Ohms is enabled. 231 | Bit 18 — Contact check failure (see Appendix F). 232 | Bits 19, 20 and 21 (Limit Results) — Provides limit test results 233 | (see grading and sorting modes below). 234 | Bit 22 (Remote Sense) — Set to 1 if 4-wire remote sense selected. 235 | Bit 23 (Pulse Mode) — Set to 1 if in the Pulse Mode. 236 | */ 237 | 238 | int items_parsed = sscanf(src_line, "%f,%f,%f,%f,%f", 239 | &meas_u, &meas_i, &meas_r, &meas_t, &status); 240 | 241 | if(items_parsed == 5) 242 | { 243 | mqtt_publish_float("feeds/float/%s/meas_U", meas_u); 244 | mqtt_publish_float("feeds/float/%s/meas_I", meas_i); 245 | mqtt_publish_float("feeds/float/%s/meas_R", meas_r); 246 | mqtt_publish_float("feeds/float/%s/meas_t", meas_t); 247 | mqtt_publish_float("feeds/float/%s/meas_status", status); 248 | } 249 | 250 | } 251 | -------------------------------------------------------------------------------- /src/MQTT.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | WiFiClient mqtt_client; 8 | PubSubClient mqtt(mqtt_client); 9 | 10 | 11 | int mqtt_last_publish_time = 0; 12 | int mqtt_lastConnect = 0; 13 | int mqtt_retries = 0; 14 | bool mqtt_fail = false; 15 | 16 | char command_topic[64]; 17 | char response_topic[64]; 18 | 19 | 20 | void callback(char *topic, byte *payload, unsigned int length) 21 | { 22 | // Serial.print("Message arrived ["); 23 | // Serial.print(topic); 24 | // Serial.print("] "); 25 | // Serial.print("'"); 26 | for (int i = 0; i < length; i++) 27 | { 28 | // Serial.print((char)payload[i]); 29 | } 30 | // Serial.print("'"); 31 | // Serial.println(); 32 | 33 | payload[length] = 0; 34 | 35 | if (!strcmp(topic, command_topic)) 36 | { 37 | char *command = (char *)payload; 38 | char buf[1024]; 39 | 40 | if (!strncmp(command, "http", 4)) 41 | { 42 | snprintf(buf, sizeof(buf)-1, "updating from: '%s'", command); 43 | // Serial.printf("%s\n", buf); 44 | 45 | mqtt.publish(response_topic, buf); 46 | ESPhttpUpdate.rebootOnUpdate(false); 47 | t_httpUpdate_return ret = ESPhttpUpdate.update(command); 48 | 49 | switch (ret) 50 | { 51 | case HTTP_UPDATE_FAILED: 52 | snprintf(buf, sizeof(buf)-1, "HTTP_UPDATE_FAILED Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); 53 | mqtt.publish(response_topic, buf); 54 | // Serial.printf("%s\n", buf); 55 | break; 56 | 57 | case HTTP_UPDATE_NO_UPDATES: 58 | snprintf(buf, sizeof(buf)-1, "HTTP_UPDATE_NO_UPDATES"); 59 | mqtt.publish(response_topic, buf); 60 | // Serial.printf("%s\n", buf); 61 | break; 62 | 63 | case HTTP_UPDATE_OK: 64 | snprintf(buf, sizeof(buf)-1, "HTTP_UPDATE_OK"); 65 | mqtt.publish(response_topic, buf); 66 | // Serial.printf("%s\n", buf); 67 | delay(500); 68 | ESP.restart(); 69 | break; 70 | 71 | default: 72 | snprintf(buf, sizeof(buf)-1, "update failed"); 73 | mqtt.publish(response_topic, buf); 74 | // Serial.printf("%s\n", buf); 75 | break; 76 | } 77 | } 78 | else 79 | { 80 | snprintf(buf, sizeof(buf)-1, "unknown command: '%s'", command); 81 | mqtt.publish(response_topic, buf); 82 | // Serial.printf("%s\n", buf); 83 | } 84 | } 85 | } 86 | 87 | void mqtt_setup() 88 | { 89 | mqtt.setCallback(callback); 90 | } 91 | 92 | void mqtt_publish_string(const char *name, const char *value) 93 | { 94 | char path_buffer[128]; 95 | 96 | sprintf(path_buffer, name, current_config.mqtt_client); 97 | 98 | if (!mqtt.publish(path_buffer, value)) 99 | { 100 | mqtt_fail = true; 101 | } 102 | // Serial.printf("Published %s : %s\n", path_buffer, value); 103 | } 104 | 105 | void mqtt_publish_float(const char *name, float value) 106 | { 107 | char path_buffer[128]; 108 | char buffer[32]; 109 | 110 | sprintf(path_buffer, name, current_config.mqtt_client); 111 | sprintf(buffer, "%f", value); 112 | 113 | if (!mqtt.publish(path_buffer, buffer)) 114 | { 115 | mqtt_fail = true; 116 | } 117 | // Serial.printf("Published %s : %s\n", path_buffer, buffer); 118 | } 119 | 120 | void mqtt_publish_int(const char *name, uint32_t value) 121 | { 122 | char path_buffer[128]; 123 | char buffer[32]; 124 | 125 | if (value == 0x7FFFFFFF) 126 | { 127 | return; 128 | } 129 | sprintf(path_buffer, name, current_config.mqtt_client); 130 | sprintf(buffer, "%d", value); 131 | 132 | if (!mqtt.publish(path_buffer, buffer)) 133 | { 134 | mqtt_fail = true; 135 | } 136 | // Serial.printf("Published %s : %s\n", path_buffer, buffer); 137 | } 138 | 139 | bool mqtt_loop() 140 | { 141 | uint32_t time = millis(); 142 | static int nextTime = 0; 143 | 144 | #ifdef TESTMODE 145 | return false; 146 | #endif 147 | if (mqtt_fail) 148 | { 149 | mqtt_fail = false; 150 | mqtt.disconnect(); 151 | } 152 | 153 | MQTT_connect(); 154 | 155 | if (!mqtt.connected()) 156 | { 157 | return false; 158 | } 159 | 160 | mqtt.loop(); 161 | 162 | if (time >= nextTime) 163 | { 164 | bool do_publish = false; 165 | 166 | if ((time - mqtt_last_publish_time) > 60000) 167 | { 168 | do_publish = true; 169 | } 170 | 171 | if (do_publish) 172 | { 173 | mqtt_last_publish_time = time; 174 | 175 | if (current_config.mqtt_publish & 1) 176 | { 177 | } 178 | if (current_config.mqtt_publish & 2) 179 | { 180 | } 181 | if (current_config.mqtt_publish & 4) 182 | { 183 | } 184 | if (current_config.mqtt_publish & 8) 185 | { 186 | } 187 | } 188 | 189 | if(current_config.mqtt_publish & 1) 190 | { 191 | nextTime = time + 1000; 192 | } 193 | if(current_config.mqtt_publish & 2) 194 | { 195 | nextTime = time + 250; 196 | } 197 | } 198 | 199 | return false; 200 | } 201 | 202 | void MQTT_connect() 203 | { 204 | int curTime = millis(); 205 | int8_t ret; 206 | 207 | if (strlen(current_config.mqtt_server) == 0) 208 | { 209 | return; 210 | } 211 | 212 | mqtt.setServer(current_config.mqtt_server, current_config.mqtt_port); 213 | 214 | if (WiFi.status() != WL_CONNECTED) 215 | { 216 | return; 217 | } 218 | 219 | if (mqtt.connected()) 220 | { 221 | return; 222 | } 223 | 224 | if ((mqtt_lastConnect != 0) && (curTime - mqtt_lastConnect < (1000 << mqtt_retries))) 225 | { 226 | return; 227 | } 228 | 229 | mqtt_lastConnect = curTime; 230 | 231 | // Serial.println("MQTT: Connecting to MQTT... "); 232 | 233 | sprintf(command_topic, "tele/%s/command", current_config.mqtt_client); 234 | sprintf(response_topic, "tele/%s/response", current_config.mqtt_client); 235 | 236 | ret = mqtt.connect(current_config.mqtt_client, current_config.mqtt_user, current_config.mqtt_password); 237 | 238 | if (ret == 0) 239 | { 240 | mqtt_retries++; 241 | if (mqtt_retries > 8) 242 | { 243 | mqtt_retries = 8; 244 | } 245 | // Serial.printf("MQTT: (%d) ", mqtt.state()); 246 | // Serial.println("MQTT: Retrying MQTT connection"); 247 | mqtt.disconnect(); 248 | } 249 | else 250 | { 251 | /* discard counts till then */ 252 | // Serial.println("MQTT Connected!"); 253 | mqtt.subscribe(command_topic); 254 | mqtt_publish_string((char *)"feeds/string/%s/error", ""); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/Serial.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | WiFiServer telnet(23); 5 | WiFiClient client; 6 | uint8_t serial_buf[1024]; 7 | bool client_connected = false; 8 | 9 | const char *udpAddress = "192.168.1.255"; 10 | const int udpPort = 2323; 11 | WiFiUDP udp_out; 12 | 13 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 14 | 15 | uint8_t terminator = 0x0A; 16 | bool serial_started = false; 17 | unsigned long last_activity = 0; 18 | uint32_t delay_us_per_byte = 0; 19 | 20 | #define IDF 21 | const uart_port_t uart_num = UART_NUM_0; 22 | 23 | #ifdef IDF 24 | const int uart_buffer_size = (1024 * 2); 25 | QueueHandle_t uart_queue; 26 | #endif 27 | 28 | void serial_setup() 29 | { 30 | if (!serial_started) 31 | { 32 | #ifdef IDF 33 | uart_config_t uart_config = 34 | { 35 | .baud_rate = (int)current_config.baudrate, 36 | .data_bits = (uart_word_length_t)(current_config.databits - 5), 37 | .parity = (uart_parity_t)(current_config.parity ? current_config.parity + 1 : 0), 38 | .stop_bits = (uart_stop_bits_t)(current_config.stopbits + 1), 39 | .flow_ctrl = UART_HW_FLOWCTRL_DISABLE 40 | }; 41 | 42 | pinMode(3, INPUT); 43 | 44 | uart_param_config(uart_num, &uart_config); 45 | uart_set_pin(uart_num, 1, 3, -1, -1); 46 | uart_driver_install(uart_num, uart_buffer_size, 0, 100, &uart_queue, 0); 47 | uart_set_sw_flow_ctrl(uart_num, false, 0, 0); 48 | uart_set_hw_flow_ctrl(uart_num, UART_HW_FLOWCTRL_DISABLE, 0); 49 | 50 | #else 51 | Serial.begin(current_config.baudrate); 52 | #endif 53 | telnet.begin(); 54 | telnet.setNoDelay(true); 55 | } 56 | 57 | uart_set_baudrate(uart_num, current_config.baudrate); 58 | 59 | if (current_config.baudrate > 0) 60 | { 61 | delay_us_per_byte = (10 * 1000000 / current_config.baudrate); 62 | } 63 | 64 | #if defined(ESP8266) 65 | Serial.swap(); 66 | #endif 67 | serial_started = true; 68 | 69 | pinMode(LED_PIN, OUTPUT); 70 | digitalWrite(LED_PIN, HIGH); 71 | } 72 | 73 | void serial_print(const char *str, int length) 74 | { 75 | char buf[128]; 76 | 77 | #ifdef IDF 78 | uart_write_bytes(uart_num, str, length); 79 | #else 80 | Serial.write(str, length); 81 | #endif 82 | if(WiFi.status() == WL_CONNECTED) 83 | { 84 | udp_out.beginPacket(udpAddress, udpPort); 85 | udp_out.write((const uint8_t *)"> ", 2); 86 | udp_out.write((const uint8_t *)str, length); 87 | udp_out.endPacket(); 88 | 89 | if (client.connected()) 90 | { 91 | client.write("> ", 2); 92 | client.write(str, length); 93 | } 94 | } 95 | } 96 | 97 | void serial_println(const char *str) 98 | { 99 | char buf[128]; 100 | 101 | snprintf(buf, sizeof(buf), "%s\n", str); 102 | serial_print(buf, strlen(buf)); 103 | } 104 | 105 | bool serial_loop_rx() 106 | { 107 | /* network part */ 108 | if (telnet.hasClient()) 109 | { 110 | client_connected = true; 111 | 112 | if (client != NULL) 113 | { 114 | client.stop(); 115 | } 116 | 117 | client = telnet.available(); 118 | char remote[32]; 119 | 120 | sprintf(remote, "%s:%d", client.remoteIP().toString().c_str(), client.remotePort()); 121 | 122 | #ifndef IDF 123 | if (current_config.verbose) 124 | { 125 | Serial.printf("[Serial] New client connected: %s\n", remote); 126 | } 127 | 128 | if (strstr(current_config.connect_string, "%s")) 129 | { 130 | Serial.printf(current_config.connect_string, remote); 131 | } 132 | else 133 | { 134 | Serial.printf(current_config.connect_string); 135 | } 136 | #endif 137 | } 138 | 139 | if (!client_connected) 140 | { 141 | return false; 142 | } 143 | 144 | if (!client.connected()) 145 | { 146 | client_connected = false; 147 | #ifndef IDF 148 | Serial.printf(current_config.disconnect_string); 149 | #endif 150 | 151 | client.stop(); 152 | return false; 153 | } 154 | 155 | int avail = client.available(); 156 | if (avail <= 0) 157 | { 158 | return false; 159 | } 160 | 161 | int net_avail = MIN(sizeof(serial_buf), avail); 162 | unsigned long current_millis = millis(); 163 | 164 | if (avail > 0) 165 | { 166 | digitalWrite(LED_PIN, HIGH); 167 | int net_read = client.read(serial_buf, net_avail); 168 | // Serial.printf("r: %d/%d", net_avail, net_read); 169 | #ifdef IDF 170 | uart_write_bytes(uart_num, (const char *)serial_buf, net_read); 171 | #else 172 | Serial.write(serial_buf, net_read); 173 | #endif 174 | 175 | /* no delay needed anymore, deactivated Tx buffer. enabling it causes Rx loss on IDF. sigh. */ 176 | //uint32_t delay_ms = net_read * delay_us_per_byte / 1000; 177 | //delay(delay_ms); 178 | digitalWrite(LED_PIN, LOW); 179 | 180 | last_activity = current_millis; 181 | } 182 | 183 | bool activity = (current_millis - last_activity) < 1000; 184 | 185 | return activity; 186 | } 187 | 188 | bool serial_loop_tx() 189 | { 190 | /* UART part */ 191 | int rcv_pos = 0; 192 | uint32_t rcv_timeout = micros(); 193 | uint32_t current_millis = micros(); 194 | 195 | while (rcv_timeout > 0) 196 | { 197 | static uint8_t buffer[1024]; 198 | int pos = 0; 199 | #ifdef IDF 200 | int32_t length = uart_read_bytes(uart_num, buffer, sizeof(buffer), 1); 201 | #else 202 | int32_t length = Serial.available() ? 1 : 0; 203 | #endif 204 | 205 | if (current_config.verbose & 2) 206 | { 207 | if (length < 0) 208 | { 209 | udp_out.beginPacket(udpAddress, udpPort); 210 | udp_out.write((const uint8_t *)"ERROR:", 6); 211 | udp_out.write((const uint8_t *)length, 4); 212 | udp_out.endPacket(); 213 | } 214 | } 215 | 216 | if (length > 0) 217 | { 218 | if (current_config.verbose & 2) 219 | { 220 | udp_out.beginPacket(udpAddress, udpPort); 221 | } 222 | while (pos < length) 223 | { 224 | uint8_t ch = 0; 225 | digitalWrite(LED_PIN, HIGH); 226 | 227 | #ifdef IDF 228 | ch = buffer[pos++]; 229 | #else 230 | ch = Serial.read(); 231 | #endif 232 | if (current_config.verbose & 2) 233 | { 234 | udp_out.write((const uint8_t *)&ch, 1); 235 | } 236 | 237 | digitalWrite(LED_PIN, LOW); 238 | 239 | serial_buf[rcv_pos++] = ch; 240 | scpi_cb(ch); 241 | 242 | /* received a line terminator? if not wait for it or until timeout happened */ 243 | if ((terminator != 0) && (ch == terminator)) 244 | { 245 | rcv_timeout = 0; 246 | } 247 | else 248 | { 249 | rcv_timeout = micros() + 2 * delay_us_per_byte; 250 | } 251 | #ifndef IDF 252 | length += Serial.available() ? 1 : 0; 253 | #endif 254 | } 255 | if (current_config.verbose & 2) 256 | { 257 | udp_out.endPacket(); 258 | } 259 | } 260 | 261 | if (micros() >= rcv_timeout || rcv_pos >= sizeof(serial_buf)) 262 | { 263 | rcv_timeout = 0; 264 | } 265 | } 266 | 267 | if (rcv_pos > 0 && client.connected()) 268 | { 269 | last_activity = current_millis; 270 | client.write(serial_buf, rcv_pos); 271 | } 272 | 273 | bool activity = (current_millis - last_activity) < 1000; 274 | 275 | return activity; 276 | } 277 | -------------------------------------------------------------------------------- /src/SCPI.ino: -------------------------------------------------------------------------------- 1 | 2 | #include "scpi.h" 3 | 4 | static int scpi_line_pos = 0; 5 | static uint8_t scpi_line_buffer[128]; 6 | static int scpi_state = SCPI_STATE_IDLE; 7 | static uint32_t scpi_request_time = 0; 8 | static uint32_t scpi_opc_timeout = 100; 9 | static uint32_t scpi_request_timeout = 500; 10 | static uint32_t scpi_retries = 0; 11 | static uint32_t scpi_opc_retries_max = 100; 12 | static bool scpi_state_changed = false; 13 | 14 | static t_scpi_command scpi_current; 15 | 16 | QueueHandle_t scpi_commands; 17 | 18 | 19 | void scpi_debug(const char *msg) 20 | { 21 | if(0) 22 | { 23 | serial_print("> ", 3); 24 | serial_println(msg); 25 | } 26 | } 27 | 28 | void scpi_setup() 29 | { 30 | scpi_commands = xQueueCreate(10, sizeof(t_scpi_command)); 31 | scpi_state = SCPI_STATE_IDLE; 32 | } 33 | 34 | bool scpi_loop() 35 | { 36 | uint32_t time = millis(); 37 | static int nextTime = 0; 38 | 39 | t_scpi_command dummy; 40 | 41 | bool item_waiting = (xQueuePeek(scpi_commands, &dummy, 0) == pdTRUE) && (scpi_state == SCPI_STATE_IDLE); 42 | 43 | if(item_waiting || scpi_state_changed) 44 | { 45 | nextTime = time; 46 | } 47 | 48 | while(time >= nextTime) 49 | { 50 | nextTime = time + 1000; 51 | 52 | switch(scpi_state) 53 | { 54 | case SCPI_STATE_IDLE: 55 | if(xQueuePeek(scpi_commands, &dummy, 0) == pdFALSE) 56 | { 57 | return false; 58 | } 59 | xQueueReceiveFromISR(scpi_commands, &scpi_current, NULL); 60 | 61 | /* immediately restart loop */ 62 | scpi_state = SCPI_STATE_OPC; 63 | scpi_retries = 0; 64 | nextTime = time; 65 | scpi_debug("Fetched new command"); 66 | continue; 67 | 68 | case SCPI_STATE_OPC: 69 | /* too many failures already? */ 70 | if(scpi_retries++ > scpi_opc_retries_max) 71 | { 72 | scpi_debug("SCPI_STATE_OPC (scpi_retries++ > scpi_opc_retries_max) -> SCPI_STATE_IDLE"); 73 | scpi_current.resp(false, NULL); 74 | 75 | /* immediately restart loop */ 76 | scpi_state = SCPI_STATE_IDLE; 77 | nextTime = time; 78 | } 79 | else 80 | { 81 | serial_println("*OPC?"); 82 | scpi_retries++; 83 | scpi_request_time = millis(); 84 | nextTime = scpi_request_time + scpi_opc_timeout; 85 | 86 | scpi_state = SCPI_STATE_OPC_RESPONSE; 87 | } 88 | break; 89 | 90 | case SCPI_STATE_OPC_RESPONSE: 91 | scpi_debug("> OPC timed out"); 92 | //while(scpi_retries > 2); 93 | 94 | /* immediately restart loop, sending OPC again */ 95 | scpi_state = SCPI_STATE_COMMAND; 96 | scpi_retries = 0; 97 | nextTime = time; 98 | break; 99 | 100 | case SCPI_STATE_COMMAND: 101 | scpi_debug("SCPI_STATE_COMMAND"); 102 | serial_println("*CLS"); 103 | delay(5); 104 | serial_println(scpi_current.command); 105 | 106 | if(scpi_current.has_response) 107 | { 108 | scpi_debug("SCPI_STATE_COMMAND -> SCPI_STATE_RESPONSE"); 109 | 110 | scpi_state = SCPI_STATE_RESPONSE; 111 | scpi_request_time = millis(); 112 | nextTime = scpi_request_time + scpi_request_timeout; 113 | } 114 | else 115 | { 116 | scpi_debug("SCPI_STATE_COMMAND -> SCPI_STATE_IDLE"); 117 | scpi_current.resp(true, NULL); 118 | 119 | /* immediately restart loop */ 120 | nextTime = time; 121 | if(scpi_current.check_error) 122 | { 123 | delay(5); 124 | scpi_state = SCPI_STATE_STB; 125 | scpi_retries = 0; 126 | } 127 | else 128 | { 129 | scpi_state = SCPI_STATE_IDLE; 130 | } 131 | } 132 | break; 133 | 134 | case SCPI_STATE_RESPONSE: 135 | scpi_debug("> RESPONSE timed out"); 136 | //while(scpi_retries > 2); 137 | 138 | scpi_debug("SCPI_STATE_RESPONSE (timeout)"); 139 | /* query timed out */ 140 | scpi_current.resp(false, NULL); 141 | 142 | /* immediately restart loop */ 143 | scpi_state = SCPI_STATE_IDLE; 144 | nextTime = time; 145 | break; 146 | 147 | case SCPI_STATE_STB: 148 | /* too many failures already? */ 149 | if(scpi_retries++ > scpi_opc_retries_max) 150 | { 151 | scpi_debug("SCPI_STATE_STB (scpi_retries++ > scpi_opc_retries_max) -> SCPI_STATE_IDLE"); 152 | scpi_current.resp(false, NULL); 153 | 154 | /* immediately restart loop */ 155 | scpi_state = SCPI_STATE_IDLE; 156 | nextTime = time; 157 | } 158 | else 159 | { 160 | serial_println("*STB?"); 161 | scpi_retries++; 162 | scpi_request_time = millis(); 163 | nextTime = scpi_request_time + scpi_opc_timeout; 164 | 165 | scpi_state = SCPI_STATE_STB_RESPONSE; 166 | } 167 | break; 168 | 169 | case SCPI_STATE_STB_RESPONSE: 170 | scpi_debug("> STB timed out"); 171 | //while(scpi_retries > 2); 172 | 173 | /* immediately restart loop, sending STB again */ 174 | scpi_state = SCPI_STATE_STB; 175 | nextTime = time; 176 | break; 177 | 178 | } 179 | } 180 | 181 | scpi_state_changed = false; 182 | 183 | return scpi_state != SCPI_STATE_IDLE; 184 | } 185 | 186 | void dummy_cbr(bool success, const char *response) 187 | { 188 | } 189 | 190 | bool scpi_command(const char *command, bool has_response, bool check_error, void (*cbr)(bool success, const char *response)) 191 | { 192 | t_scpi_command entry; 193 | 194 | strncpy(entry.command, command, sizeof(entry.command)); 195 | entry.has_response = has_response; 196 | entry.check_error = check_error; 197 | entry.resp = cbr ? cbr : &dummy_cbr; 198 | 199 | return xQueueSend(scpi_commands, &entry, 0) == pdTRUE; 200 | } 201 | 202 | void scpi_parse(const char *line) 203 | { 204 | switch(scpi_state) 205 | { 206 | case SCPI_STATE_OPC_RESPONSE: 207 | if(line[0] == '1') 208 | { 209 | scpi_state = SCPI_STATE_COMMAND; 210 | scpi_retries = 0; 211 | scpi_state_changed = true; 212 | break; 213 | } 214 | break; 215 | 216 | case SCPI_STATE_RESPONSE: 217 | scpi_current.resp(true, line); 218 | if(scpi_current.check_error) 219 | { 220 | scpi_state = SCPI_STATE_STB; 221 | } 222 | else 223 | { 224 | scpi_state = SCPI_STATE_IDLE; 225 | } 226 | scpi_retries = 0; 227 | scpi_state_changed = true; 228 | break; 229 | 230 | case SCPI_STATE_STB_RESPONSE: 231 | int status = atoi(line); 232 | 233 | if(status & 4) 234 | { 235 | scpi_state = SCPI_STATE_COMMAND; 236 | scpi_state_changed = true; 237 | break; 238 | } 239 | scpi_state = SCPI_STATE_IDLE; 240 | scpi_state_changed = true; 241 | break; 242 | } 243 | } 244 | 245 | void scpi_cb(uint8_t ch) 246 | { 247 | if(ch == '\r') 248 | { 249 | } 250 | else if(ch == '\n') 251 | { 252 | scpi_line_buffer[scpi_line_pos] = 0; 253 | scpi_line_pos = 0; 254 | scpi_parse((const char *)scpi_line_buffer); 255 | } 256 | else if((scpi_line_pos + 1) < sizeof(scpi_line_buffer)) 257 | { 258 | scpi_line_buffer[scpi_line_pos++] = ch; 259 | } 260 | } -------------------------------------------------------------------------------- /src/Debug.ino: -------------------------------------------------------------------------------- 1 | 2 | WiFiServer debug_server(2323); 3 | WiFiClient debug_client; 4 | char debug_buf[1024]; 5 | char dbg_status[1024]; 6 | 7 | bool debug_started = false; 8 | bool debug_connected = false; 9 | int dbg_last_activity = 0; 10 | 11 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 12 | #define COUNT(x) (sizeof(x)/sizeof(x[0])) 13 | 14 | typedef struct 15 | { 16 | const char *name; 17 | const char *desc; 18 | uint32_t address; 19 | const char *access; 20 | } t_dbg_per; 21 | 22 | 23 | t_dbg_per dbg_registers[] = { 24 | // regex (UART_[A-Z0-9_]*) ([a-zA-Z0-9_\- ]+) (0x[0-9A-F]{8}) (0x[0-9A-F]{8}) (0x[0-9A-F]{8}) ([RWO/]+) 25 | // Configuration registers 26 | { "UART_CONF0_REG", "Configuration register 0", 0x3FF40020, "R/W" }, 27 | { "UART_CONF1_REG", "Configuration register 1", 0x3FF40024, "R/W" }, 28 | { "UART_CLKDIV_REG", "Clock divider configuration", 0x3FF40014, "R/W" }, 29 | { "UART_FLOW_CONF_REG", "Software flow-control configuration", 0x3FF40034, "R/W" }, 30 | { "UART_SWFC_CONF_REG", "Software flow-control character configuration", 0x3FF4003C, "R/W" }, 31 | { "UART_SLEEP_CONF_REG", "Sleep-mode configuration", 0x3FF40038, "R/W" }, 32 | { "UART_IDLE_CONF_REG", "Frame-end idle configuration", 0x3FF40040, "R/W" }, 33 | { "UART_RS485_CONF_REG", "RS485 mode configuration", 0x3FF40044, "R/W" }, 34 | // Status registers 35 | { "UART_STATUS_REG", "UART status register", 0x3FF4001C, "RO" }, 36 | // Autobaud registers 37 | { "UART_AUTOBAUD_REG", "Autobaud configuration register", 0x3FF40018, "R/W" }, 38 | { "UART_LOWPULSE_REG", "Autobaud minimum low pulse duration register", 0x3FF40028, "RO" }, 39 | { "UART_HIGHPULSE_REG", "Autobaud minimum high pulse duration register", 0x3FF4002C, "RO" }, 40 | { "UART_POSPULSE_REG", "Autobaud high pulse register", 0x3FF40068, "RO" }, 41 | { "UART_NEGPULSE_REG", "Autobaud low pulse register", 0x3FF4006C, "RO" }, 42 | { "UART_RXD_CNT_REG", "Autobaud edge change count register", 0x3FF40030, "RO" }, 43 | // AT escape seqence detection configuration 44 | { "UART_AT_CMD_PRECNT_REG", "Pre-sequence timing configuration", 0x3FF40048, "R/W" }, 45 | { "UART_AT_CMD_POSTCNT_REG", "Post-sequence timing configuration", 0x3FF4004C, "R/W" }, 46 | { "UART_AT_CMD_GAPTOUT_REG", "Timeout configuration", 0x3FF40050, "R/W" }, 47 | { "UART_AT_CMD_CHAR_REG", "AT escape sequence detection configuration", 0x3FF40054, "R/W" }, 48 | //FIFO configuration 49 | { "UART_FIFO_REG", "FIFO data register", 0x3FF40000, "R/W" }, 50 | { "UART_MEM_CONF_REG", "UART threshold and allocation configuration", 0x3FF40058, "R/W" }, 51 | { "UART_MEM_CNT_STATUS_REG", "Receive and transmit memory configuration", 0x3FF40064, "RO" }, 52 | //Interrupt registers 53 | { "UART_INT_RAW_REG", "Raw interrupt status", 0x3FF40004, "RO" }, 54 | { "UART_INT_ST_REG", "Masked interrupt status", 0x3FF40008, "RO" }, 55 | { "UART_INT_ENA_REG", "Interrupt enable bits", 0x3FF4000C, "R/W" }, 56 | { "UART_INT_CLR_REG", "Interrupt clear bits", 0x3FF40010, "WO" } 57 | }; 58 | 59 | uint32_t *dbg_last_values = NULL; 60 | uint32_t *dbg_last_update = NULL; 61 | 62 | void dbg_setup() 63 | { 64 | if (!debug_started) 65 | { 66 | debug_server.begin(); 67 | debug_server.setNoDelay(true); 68 | } 69 | dbg_last_values = (uint32_t *)malloc(sizeof(uint32_t) * COUNT(dbg_registers)); 70 | dbg_last_update = (uint32_t *)malloc(sizeof(uint32_t) * COUNT(dbg_registers)); 71 | strcpy(dbg_status, ""); 72 | debug_started = true; 73 | } 74 | 75 | uint32_t dbg_read(uint32_t address) 76 | { 77 | volatile uint32_t *ptr = (volatile uint32_t *)address; 78 | 79 | return *ptr; 80 | } 81 | 82 | void dbg_write(uint32_t address, uint32_t value) 83 | { 84 | volatile uint32_t *ptr = (volatile uint32_t *)address; 85 | 86 | *ptr = value; 87 | } 88 | 89 | 90 | bool dbg_loop() 91 | { 92 | uint32_t time = millis(); 93 | 94 | /* network part */ 95 | if (debug_server.hasClient()) 96 | { 97 | debug_connected = true; 98 | 99 | if (debug_client != NULL) 100 | { 101 | debug_client.stop(); 102 | } 103 | 104 | debug_client = debug_server.available(); 105 | char remote[32]; 106 | 107 | sprintf(remote, "%s:%d", debug_client.remoteIP().toString().c_str(), debug_client.remotePort()); 108 | } 109 | 110 | if (!debug_connected) 111 | { 112 | return false; 113 | } 114 | 115 | if (!debug_client.connected()) 116 | { 117 | debug_connected = false; 118 | debug_client.stop(); 119 | return false; 120 | } 121 | 122 | int avail = debug_client.available(); 123 | if (avail > 0) 124 | { 125 | dbg_last_activity = time; 126 | 127 | int net_avail = MIN(sizeof(debug_buf), avail); 128 | int net_read = debug_client.read((uint8_t *)debug_buf, net_avail); 129 | 130 | if(net_read > 0) 131 | { 132 | dbg_parse(debug_buf); 133 | } 134 | } 135 | 136 | static int nextTime = 0; 137 | static int nextTimeRefresh = 0; 138 | 139 | if(time >= nextTime) 140 | { 141 | bool update = false; 142 | bool refresh = (time >= nextTimeRefresh); 143 | 144 | for(int pos = 0; pos < COUNT(dbg_registers); pos++) 145 | { 146 | uint32_t value = dbg_read(dbg_registers[pos].address); 147 | if(value != dbg_last_values[pos]) 148 | { 149 | dbg_last_values[pos] = value; 150 | dbg_last_update[pos] = time; 151 | update = true; 152 | } 153 | } 154 | 155 | if(refresh) 156 | { 157 | nextTimeRefresh = time + 10000; 158 | } 159 | 160 | if(update) 161 | { 162 | debug_client.printf("\x1b[0;0H\n Debug interface\n"); 163 | debug_client.printf("-----------------\n"); 164 | for(int pos = 0; pos < COUNT(dbg_registers); pos++) 165 | { 166 | const char *high_str = " "; 167 | const char *norm_str = " "; 168 | bool draw = refresh; 169 | 170 | if(time - dbg_last_update[pos] < 1000) 171 | { 172 | high_str = "\x1b[36m>"; 173 | norm_str = "\x1b[0m"; 174 | draw = true; 175 | } 176 | 177 | if(draw) 178 | { 179 | debug_client.printf("%s %32s 0x%08X '%s' %s\n", high_str, dbg_registers[pos].name, dbg_last_values[pos], dbg_registers[pos].desc, norm_str); 180 | } 181 | else 182 | { 183 | debug_client.printf("\x1b[1B"); 184 | } 185 | } 186 | debug_client.printf("\n Status> %s\n", dbg_status); 187 | } 188 | nextTime = time + 250; 189 | } 190 | 191 | bool activity = (time - dbg_last_activity) < 1000; 192 | 193 | return activity; 194 | } 195 | 196 | void dbg_parse(char *buf) 197 | { 198 | int start = 0; 199 | 200 | while(buf[start] && buf[start] == ' ') 201 | { 202 | start++; 203 | } 204 | 205 | switch(buf[start]) 206 | { 207 | case 'w': 208 | start++; 209 | start++; 210 | int len = 0; 211 | 212 | while(buf[start + len] && (buf[start + len] != ' ')) 213 | { 214 | len++; 215 | } 216 | 217 | int reg = -1; 218 | for(int pos = 0; pos < COUNT(dbg_registers); pos++) 219 | { 220 | if(!strncmp(&buf[start], dbg_registers[pos].name, len)) 221 | { 222 | reg = pos; 223 | } 224 | } 225 | 226 | if(reg == -1) 227 | { 228 | sprintf(dbg_status, "Unknown register in command '%s'", buf); 229 | return; 230 | } 231 | 232 | int value_start = start + len; 233 | 234 | while(buf[value_start] && (buf[value_start] != ' ')) 235 | { 236 | value_start++; 237 | } 238 | value_start++; 239 | 240 | //w UART_INT_CLR_REG 0xFFFF 241 | //w UART_INT_ENA_REG 0x195 242 | //w UART_FLOW_CONF_REG 0x01 243 | //w UART_FLOW_CONF_REG 0x00 244 | 245 | 246 | uint32_t value = (uint32_t)strtol(&buf[value_start], NULL, 0); 247 | 248 | sprintf(dbg_status, "Writing 0x%08X to %s", value, dbg_registers[reg].name); 249 | dbg_write(dbg_registers[reg].address, value); 250 | 251 | break; 252 | } 253 | } -------------------------------------------------------------------------------- /src/Webserver.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Config.h" 4 | 5 | #define xstr(s) str(s) 6 | #define str(s) #s 7 | 8 | WebServer webserver(80); 9 | extern char wifi_error[]; 10 | extern bool wifi_captive; 11 | int www_wifi_scanned = -1; 12 | int www_last_captive = 0; 13 | 14 | #define min(a, b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; }) 15 | #define max(a, b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; }) 16 | 17 | void www_setup() 18 | { 19 | webserver.on("/", handle_root); 20 | webserver.on("/index.html", handle_index); 21 | webserver.on("/set_parm", handle_set_parm); 22 | webserver.on("/ota", handle_ota); 23 | webserver.on("/dbg", handle_dbg); 24 | webserver.on("/reset", handle_reset); 25 | webserver.on("/test", handle_test); 26 | webserver.onNotFound(handle_404); 27 | 28 | webserver.begin(); 29 | // Serial.println("HTTP server started"); 30 | 31 | if (!MDNS.begin(current_config.hostname)) 32 | { 33 | Serial.println("Error setting up MDNS responder!"); 34 | while (1) 35 | { 36 | delay(1000); 37 | } 38 | } 39 | MDNS.addService("http", "tcp", 80); 40 | MDNS.addService("telnet", "tcp", 23); 41 | } 42 | 43 | unsigned char h2int(char c) 44 | { 45 | if (c >= '0' && c <= '9') 46 | { 47 | return ((unsigned char)c - '0'); 48 | } 49 | if (c >= 'a' && c <= 'f') 50 | { 51 | return ((unsigned char)c - 'a' + 10); 52 | } 53 | if (c >= 'A' && c <= 'F') 54 | { 55 | return ((unsigned char)c - 'A' + 10); 56 | } 57 | return (0); 58 | } 59 | 60 | String urldecode(String str) 61 | { 62 | String encodedString = ""; 63 | char c; 64 | char code0; 65 | char code1; 66 | for (int i = 0; i < str.length(); i++) 67 | { 68 | c = str.charAt(i); 69 | if (c == '+') 70 | { 71 | encodedString += ' '; 72 | } 73 | else if (c == '%') 74 | { 75 | i++; 76 | code0 = str.charAt(i); 77 | i++; 78 | code1 = str.charAt(i); 79 | c = (h2int(code0) << 4) | h2int(code1); 80 | encodedString += c; 81 | } 82 | else 83 | { 84 | encodedString += c; 85 | } 86 | 87 | yield(); 88 | } 89 | 90 | return encodedString; 91 | } 92 | 93 | void www_activity() 94 | { 95 | if (wifi_captive) 96 | { 97 | www_last_captive = millis(); 98 | } 99 | } 100 | 101 | int www_is_captive_active() 102 | { 103 | if (wifi_captive && millis() - www_last_captive < 30000) 104 | { 105 | return 1; 106 | } 107 | return 0; 108 | } 109 | 110 | void handle_404() 111 | { 112 | www_activity(); 113 | 114 | if (wifi_captive) 115 | { 116 | char buf[128]; 117 | sprintf(buf, "HTTP/1.1 302 Found\r\nContent-Type: text/html\r\nContent-length: 0\r\nLocation: http://%s/\r\n\r\n", WiFi.softAPIP().toString().c_str()); 118 | webserver.sendContent(buf); 119 | // Serial.printf("[WWW] 302 - http://%s%s/ -> http://%s/\n", webserver.hostHeader().c_str(), webserver.uri().c_str(), WiFi.softAPIP().toString().c_str()); 120 | } 121 | else 122 | { 123 | webserver.send(404, "text/plain", "So empty here"); 124 | // Serial.printf("[WWW] 404 - http://%s%s/\n", webserver.hostHeader().c_str(), webserver.uri().c_str()); 125 | } 126 | } 127 | 128 | void handle_index() 129 | { 130 | webserver.send(200, "text/html", SendHTML()); 131 | } 132 | 133 | bool www_loop() 134 | { 135 | webserver.handleClient(); 136 | return false; 137 | } 138 | 139 | void handle_root() 140 | { 141 | webserver.send(200, "text/html", SendHTML()); 142 | } 143 | 144 | void handle_ota() 145 | { 146 | ota_setup(); 147 | webserver.send(200, "text/html", SendHTML()); 148 | } 149 | 150 | void handle_dbg() 151 | { 152 | uart_flush(uart_num); 153 | webserver.send(200, "text/html", "OK"); 154 | } 155 | 156 | void handle_reset() 157 | { 158 | webserver.send(200, "text/html", SendHTML()); 159 | ESP.restart(); 160 | } 161 | 162 | void handle_test() 163 | { 164 | // Serial.printf("Test\n"); 165 | webserver.send(200, "text/html", SendHTML()); 166 | } 167 | 168 | void handle_set_parm() 169 | { 170 | if (webserver.arg("http_download") != "" && webserver.arg("http_name") != "") 171 | { 172 | String url = webserver.arg("http_download"); 173 | String filename = webserver.arg("http_name"); 174 | HTTPClient http; 175 | 176 | http.begin(url); 177 | 178 | int httpCode = http.GET(); 179 | 180 | // Serial.printf("[HTTP] GET... code: %d\n", httpCode); 181 | 182 | switch (httpCode) 183 | { 184 | case HTTP_CODE_OK: 185 | { 186 | int len = http.getSize(); 187 | const int blocksize = 1024; 188 | uint8_t *buffer = (uint8_t *)malloc(blocksize); 189 | 190 | if (!buffer) 191 | { 192 | // Serial.printf("[HTTP] Failed to alloc %d byte\n", blocksize); 193 | return; 194 | } 195 | 196 | WiFiClient *stream = http.getStreamPtr(); 197 | File file = SPIFFS.open("/" + filename, "w"); 198 | 199 | if (!file) 200 | { 201 | // Serial.printf("[HTTP] Failed to open file\n", blocksize); 202 | return; 203 | } 204 | 205 | int written = 0; 206 | 207 | while (http.connected() && (written < len)) 208 | { 209 | size_t size = stream->available(); 210 | 211 | if (size) 212 | { 213 | int c = stream->readBytes(buffer, ((size > blocksize) ? blocksize : size)); 214 | 215 | if (c > 0) 216 | { 217 | file.write(buffer, c); 218 | written += c; 219 | } 220 | else 221 | { 222 | break; 223 | } 224 | } 225 | } 226 | 227 | free(buffer); 228 | file.close(); 229 | 230 | // Serial.printf("[HTTP] Finished. Wrote %d byte to %s\n", written, filename.c_str()); 231 | webserver.send(200, "text/plain", "Downloaded " + url + " and wrote " + written + " byte to " + filename); 232 | break; 233 | } 234 | 235 | default: 236 | { 237 | // Serial.print("[HTTP] unexpected response\n"); 238 | webserver.send(200, "text/plain", "Unexpected HTTP status code " + httpCode); 239 | break; 240 | } 241 | } 242 | 243 | return; 244 | } 245 | 246 | if (webserver.arg("http_update") != "") 247 | { 248 | String url = webserver.arg("http_update"); 249 | 250 | Serial.printf("Update from %s\n", url.c_str()); 251 | 252 | ESPhttpUpdate.rebootOnUpdate(false); 253 | t_httpUpdate_return ret = ESPhttpUpdate.update(url); 254 | 255 | switch (ret) 256 | { 257 | case HTTP_UPDATE_FAILED: 258 | webserver.send(200, "text/plain", "HTTP_UPDATE_FAILED while updating from " + url + " " + ESPhttpUpdate.getLastErrorString()); 259 | // Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); 260 | break; 261 | 262 | case HTTP_UPDATE_NO_UPDATES: 263 | webserver.send(200, "text/plain", "HTTP_UPDATE_NO_UPDATES: Updating from " + url); 264 | // Serial.println("Update failed: HTTP_UPDATE_NO_UPDATES"); 265 | break; 266 | 267 | case HTTP_UPDATE_OK: 268 | webserver.send(200, "text/html", "

Firmware updated. Rebooting...

(will refresh page in 5 seconds)"); 269 | webserver.close(); 270 | // Serial.println("Update successful"); 271 | delay(500); 272 | ESP.restart(); 273 | return; 274 | } 275 | 276 | return; 277 | } 278 | 279 | current_config.baudrate = max(1200, min(1000000, webserver.arg("baud").toInt())); 280 | current_config.databits = max(5, min(8, webserver.arg("databits").toInt())); 281 | current_config.parity = max(0, min(2, webserver.arg("parity").toInt())); 282 | current_config.stopbits = max(0, min(2, webserver.arg("stopbits").toInt())); 283 | 284 | current_config.verbose = 0; 285 | current_config.verbose |= (webserver.arg("verbose_c0") != "") ? 1 : 0; 286 | current_config.verbose |= (webserver.arg("verbose_c1") != "") ? 2 : 0; 287 | current_config.verbose |= (webserver.arg("verbose_c2") != "") ? 4 : 0; 288 | current_config.verbose |= (webserver.arg("verbose_c3") != "") ? 8 : 0; 289 | current_config.verbose |= (webserver.arg("verbose_c4") != "") ? 16 : 0; 290 | current_config.mqtt_publish = 0; 291 | current_config.mqtt_publish |= (webserver.arg("mqtt_publish_c0") != "") ? 1 : 0; 292 | current_config.mqtt_publish |= (webserver.arg("mqtt_publish_c1") != "") ? 2 : 0; 293 | current_config.mqtt_publish |= (webserver.arg("mqtt_publish_c2") != "") ? 4 : 0; 294 | current_config.mqtt_publish |= (webserver.arg("mqtt_publish_c3") != "") ? 8 : 0; 295 | current_config.mqtt_publish_rate = max(50, webserver.arg("mqtt_publish_rate").toInt()); 296 | 297 | strncpy(current_config.connect_string, webserver.arg("conn_string").c_str(), 127); 298 | strncpy(current_config.disconnect_string, webserver.arg("disconn_string").c_str(), 127); 299 | strncpy(current_config.hostname, webserver.arg("hostname").c_str(), sizeof(current_config.hostname)); 300 | strncpy(current_config.wifi_ssid, webserver.arg("wifi_ssid").c_str(), sizeof(current_config.wifi_ssid)); 301 | strncpy(current_config.wifi_password, webserver.arg("wifi_password").c_str(), sizeof(current_config.wifi_password)); 302 | 303 | strncpy(current_config.mqtt_server, webserver.arg("mqtt_server").c_str(), sizeof(current_config.mqtt_server)); 304 | current_config.mqtt_port = max(1, min(65535, webserver.arg("mqtt_port").toInt())); 305 | strncpy(current_config.mqtt_user, webserver.arg("mqtt_user").c_str(), sizeof(current_config.mqtt_user)); 306 | strncpy(current_config.mqtt_password, webserver.arg("mqtt_password").c_str(), sizeof(current_config.mqtt_password)); 307 | strncpy(current_config.mqtt_client, webserver.arg("mqtt_client").c_str(), sizeof(current_config.mqtt_client)); 308 | 309 | cfg_save(); 310 | serial_setup(); 311 | 312 | if (current_config.verbose) 313 | { 314 | // Serial.printf("[Webserver] '%s' %d %d '%s' '%s'\n", current_config.hostname, current_config.baudrate, current_config.verbose, current_config.connect_string, current_config.disconnect_string); 315 | } 316 | 317 | 318 | 319 | if (webserver.arg("reboot") == "true") 320 | { 321 | webserver.send(200, "text/html", "

Saved. Rebooting...

(will refresh page in 5 seconds)"); 322 | delay(500); 323 | ESP.restart(); 324 | return; 325 | } 326 | 327 | if (webserver.arg("scan") == "true") 328 | { 329 | www_wifi_scanned = WiFi.scanNetworks(); 330 | } 331 | webserver.send(200, "text/html", SendHTML()); 332 | www_wifi_scanned = -1; 333 | } 334 | 335 | void handle_NotFound() 336 | { 337 | webserver.send(404, "text/plain", "Not found"); 338 | } 339 | 340 | String SendHTML() 341 | { 342 | char buf[1024]; 343 | 344 | www_activity(); 345 | 346 | String ptr = " \n"; 347 | ptr += "\n"; 348 | 349 | sprintf(buf, "ESP232 '%s' Control\n", current_config.hostname); 350 | 351 | ptr += buf; 352 | ptr += "\n"; 376 | /* https://github.com/mdbassit/Coloris */ 377 | ptr += "\n"; 378 | ptr += "\n"; 379 | ptr += "\n"; 380 | ptr += "\n"; 381 | 382 | sprintf(buf, "

ESP232 - %s

\n", current_config.hostname); 383 | ptr += buf; 384 | 385 | sprintf(buf, "

v1." xstr(PIO_SRC_REVNUM) " - " xstr(PIO_SRC_REV) "

\n"); 386 | ptr += buf; 387 | 388 | if (strlen(wifi_error) != 0) 389 | { 390 | sprintf(buf, "

WiFi Error: %s

\n", wifi_error); 391 | ptr += buf; 392 | } 393 | 394 | if (!ota_enabled()) 395 | { 396 | ptr += "[Enable OTA] "; 397 | } 398 | sprintf(buf, "
\n"); 399 | ptr += buf; 400 | ptr += "

\n"; 401 | 402 | ptr += "
\n"; 403 | ptr += ""; 404 | 405 | #define ADD_CONFIG(name, value, fmt, desc) \ 406 | do \ 407 | { \ 408 | ptr += ""; \ 409 | sprintf(buf, "\n", value); \ 410 | ptr += buf; \ 411 | } while (0) 412 | 413 | #define ADD_CONFIG_CHECK4(name, value, fmt, desc, text0, text1, text2, text3) \ 414 | do \ 415 | { \ 416 | ptr += "\n"); \ 434 | ptr += buf; \ 435 | } while (0) 436 | 437 | #define ADD_CONFIG_CHECK5(name, value, fmt, desc, text0, text1, text2, text3, text4) \ 438 | do \ 439 | { \ 440 | ptr += "\n"); \ 462 | ptr += buf; \ 463 | } while (0) 464 | 465 | #define ADD_CONFIG_COLOR(name, value, fmt, desc) \ 466 | do \ 467 | { \ 468 | ptr += ""; \ 469 | sprintf(buf, "\n", value); \ 470 | ptr += buf; \ 471 | } while (0) 472 | 473 | ADD_CONFIG("hostname", current_config.hostname, "%s", "Hostname"); 474 | ADD_CONFIG("wifi_ssid", current_config.wifi_ssid, "%s", "WiFi SSID"); 475 | ADD_CONFIG("wifi_password", current_config.wifi_password, "%s", "WiFi Password"); 476 | 477 | ptr += ""; 508 | 509 | ADD_CONFIG("mqtt_server", current_config.mqtt_server, "%s", "MQTT Server"); 510 | ADD_CONFIG("mqtt_port", current_config.mqtt_port, "%d", "MQTT Port"); 511 | ADD_CONFIG("mqtt_user", current_config.mqtt_user, "%s", "MQTT Username"); 512 | ADD_CONFIG("mqtt_password", current_config.mqtt_password, "%s", "MQTT Password"); 513 | ADD_CONFIG("mqtt_client", current_config.mqtt_client, "%s", "MQTT Client Identification"); 514 | 515 | ADD_CONFIG("baud", current_config.baudrate, "%d", "Baudrate"); 516 | ADD_CONFIG("databits", current_config.databits, "%d", "Data bits (5-8)"); 517 | ADD_CONFIG("parity", current_config.parity, "%d", "Parity (0=none, 1=even, 2=odd)"); 518 | ADD_CONFIG("stopbits", current_config.stopbits, "%d", "Stop bits (0=1, 1=1.5, 2=2)"); 519 | ADD_CONFIG("connect_string", current_config.connect_string, "%s", "Connect Message"); 520 | ADD_CONFIG("disconnect_string", current_config.disconnect_string, "%s", "Disconnect Message"); 521 | ADD_CONFIG_CHECK4("verbose", current_config.verbose, "%d", "Verbosity", "Serial", "UDP", "_", "_"); 522 | ADD_CONFIG("mqtt_publish_rate", current_config.mqtt_publish_rate, "%d", "Publish rate [ms]"); 523 | ADD_CONFIG_CHECK4("mqtt_publish", current_config.mqtt_publish, "%d", "Publish data via MQTT", "Publish string", "Publish parsed", "Exit REM", "Publish MEAS"); 524 | 525 | 526 | ADD_CONFIG("http_update", "", "%s", "Update URL (Release)"); 527 | 528 | ptr += "
" desc ":
"; \ 417 | sprintf(buf, "\n", (value & 1) ? "checked" : ""); \ 418 | ptr += buf; \ 419 | sprintf(buf, "\n"); \ 420 | ptr += buf; \ 421 | sprintf(buf, "\n", (value & 2) ? "checked" : ""); \ 422 | ptr += buf; \ 423 | sprintf(buf, "\n"); \ 424 | ptr += buf; \ 425 | sprintf(buf, "\n", (value & 4) ? "checked" : ""); \ 426 | ptr += buf; \ 427 | sprintf(buf, "\n"); \ 428 | ptr += buf; \ 429 | sprintf(buf, "\n", (value & 8) ? "checked" : ""); \ 430 | ptr += buf; \ 431 | sprintf(buf, "\n"); \ 432 | ptr += buf; \ 433 | sprintf(buf, "
" desc ":
"; \ 441 | sprintf(buf, "\n", (value & 1) ? "checked" : ""); \ 442 | ptr += buf; \ 443 | sprintf(buf, "\n"); \ 444 | ptr += buf; \ 445 | sprintf(buf, "\n", (value & 2) ? "checked" : ""); \ 446 | ptr += buf; \ 447 | sprintf(buf, "\n"); \ 448 | ptr += buf; \ 449 | sprintf(buf, "\n", (value & 4) ? "checked" : ""); \ 450 | ptr += buf; \ 451 | sprintf(buf, "\n"); \ 452 | ptr += buf; \ 453 | sprintf(buf, "\n", (value & 8) ? "checked" : ""); \ 454 | ptr += buf; \ 455 | sprintf(buf, "\n"); \ 456 | ptr += buf; \ 457 | sprintf(buf, "\n", (value & 16) ? "checked" : "");\ 458 | ptr += buf; \ 459 | sprintf(buf, "\n"); \ 460 | ptr += buf; \ 461 | sprintf(buf, "
WiFi networks:"; 478 | 479 | if (www_wifi_scanned == -1) 480 | { 481 | ptr += ""; 482 | } 483 | else if (www_wifi_scanned == 0) 484 | { 485 | ptr += "No networks found, "; 486 | } 487 | else 488 | { 489 | ptr += ""; 490 | ptr += ""; 491 | for (int i = 0; i < www_wifi_scanned; ++i) 492 | { 493 | if (WiFi.SSID(i) != "") 494 | { 495 | ptr += ""; 502 | } 503 | } 504 | ptr += "
"; 498 | ptr += WiFi.SSID(i); 499 | ptr += ""; 500 | ptr += WiFi.RSSI(i); 501 | ptr += " dBm
"; 505 | } 506 | 507 | ptr += "
\n"; 529 | ptr += "\n"; 530 | ptr += "\n"; 531 | 532 | return ptr; 533 | } 534 | --------------------------------------------------------------------------------