// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt /* Modified by Christophe Duparquet: extended implementation of RFC 2217 * * Verified on ESP-WROOM-02 with the following configuration: * Reset: gpio5 * ISP/Flash: disabled * Conn LED: gpio4 * Serial LED: disabled * UART pins: swapped * RX pull-up: yes * * ESP connections: * GPIO2 (#7) / 3V3 (#1) -> 1kR * GPIO0 (#8) / 3V3 (#1) -> 1kR * GPIO0 (#8) = USB serial RTS * GPIO13 (#5) / GPIO15 (#6) -> 1kR * GPIO15 (#6) / GND (#9) -> 10kR * EN (#2) / 3V3 (#1) -> 10 kR * #18 = #13 = #1 = GND * RST (#15) -> USB serial RTS * * ATtiny85 (3V3) connections: * RESET (#1) -> ESP pin GPIO5 (#14) * RXTX (#2) -> ESP pin GPIO13 (#5) * * Commands (using pyserial-3.0): * python -m serial.tools.miniterm rfc2217://192.168.1.78:23 * diabolo -t rfc2217://192.168.1.78:23 --keep-txd-low 0.1 --read-flash */ #include "esp8266.h" #include "uart.h" #include "crc16.h" #include "serbridge.h" #include "serled.h" #include "config.h" #include "console.h" #include "slip.h" #include "cmd.h" #define SKIP_AT_RESET /* ESP8266 hardware registers */ #define REG_BRR (*(volatile uint32_t*)0x60000014) #define REG_CONF0 (*(volatile uint32_t*)0x60000020) #define REG_CONF1 (*(volatile uint32_t*)0x60000024) #ifdef SERBR_DBG # define DBG(...) do { os_printf(__VA_ARGS__); }while(0) #else # define DBG(...) do{}while(0) #endif static struct espconn serbridgeConn1; // plain bridging port static struct espconn serbridgeConn2; // programming port static esp_tcp serbridgeTcp1, serbridgeTcp2; static int8_t mcu_reset_pin, mcu_isp_pin; extern uint8_t slip_disabled; // disable slip to allow flashing of attached MCU void (*programmingCB)(char *buffer, short length) = NULL; // Connection pool serbridgeConnData connData[MAX_CONN]; static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) ; static sint8 ICACHE_FLASH_ATTR espbuffsend_tn(serbridgeConnData *conn, const char *data, uint16 len) ; // Telnet protocol (RFC854) characters // #define IAC 0xFF // escape #define DONT 0xFE // negociation (RFC855) #define DO 0xFD // negociation #define WONT 0xFC // negociation #define WILL 0xFB // negociation #define SB 0xFA // subnegotiation begin #define SE 0xF0 // subnegotiation end #define BINARY 0x00 // RFC856 #define ECHO 0x01 // RFC857 #define SUPPRESS_GO_AHEAD 0x03 // RFC858 #define COM_PORT_OPTION 0x2C // RFC2217 #define SET_BAUDRATE 1 // suboption "BAUDRATE" #define SET_DATASIZE 2 // suboption "DATASIZE" #define SET_PARITY 3 // suboption "PARITY" #define SET_STOPSIZE 4 // suboption "STOPSIZE" #define SET_CONTROL 5 // suboption "CONTROL" #define PURGE_DATA 12 // suboption "PURGE" /* Telnet state machine states */ enum { ST_NORMAL, ST_IAC, ST_NEGO, ST_SB, ST_COMPORT, ST_BAUDRATE, ST_DATASIZE, ST_PARITY, ST_STOPSIZE, ST_CONTROL, ST_PURGE, ST_WAIT_IAC }; /* Telnet state machine: process one byte comming from a telnet connection */ static void ICACHE_FLASH_ATTR telnet_process_char ( serbridgeConnData *conn, char c ) { #define state conn->tn_state #define opt conn->tn_opt #define vlen conn->tn_vlen #define value conn->tn_value if ( state == ST_NORMAL ) { if ( c == IAC ) state = ST_IAC ; else uart0_write_char(c) ; return ; } if ( state == ST_IAC ) { if ( c == IAC ) { /* * Dual IAC means char \xFF */ uart0_write_char(0xFF); state = ST_NORMAL ; return ; } else if ( c == DONT || c == DO || c == WONT || c == WILL ) { /* * Beginning of negociation */ opt = c ; state = ST_NEGO ; return ; } else if ( c == SB ) { /* * Beginning of sub option */ opt = c ; state = ST_SB ; return ; } else if ( c == SE ) { /* * End of sub option */ opt = c ; state = ST_NORMAL ; return ; } return ; } if ( state == ST_NEGO ) { DBG("TELNET: IAC NEGO (%02X)", c); if ( c == BINARY || c == ECHO || c == SUPPRESS_GO_AHEAD || c == COM_PORT_OPTION ) { /* * Acknowledge positive requests for known options */ DBG(": OK\n"); if ( opt == DO ) opt = WILL ; else if ( opt == WILL ) opt = DO ; } else { /* * Deny positive requests for unknown options */ DBG(": REJECTED\n"); if ( opt == DO ) opt = WONT ; else if ( opt == WILL ) opt = DONT ; } /* * Send reply */ espbuffsend( conn, (char[]){IAC,opt,c}, 3 ); state = ST_NORMAL ; return ; } if ( state == ST_SB ) { if ( c == COM_PORT_OPTION ) state = ST_COMPORT ; else state = ST_WAIT_IAC ; return ; } if ( state == ST_WAIT_IAC ) { if ( c == IAC ) state = ST_IAC ; return ; } if ( state == ST_COMPORT ) { if ( c == SET_BAUDRATE ) { DBG(" BAUDRATE:"); vlen = 0 ; state = ST_BAUDRATE ; } else if ( c == SET_DATASIZE ) { DBG(" DATASIZE:"); state = ST_DATASIZE ; } else if ( c == SET_PARITY ) { DBG(" PARITY:"); state = ST_PARITY ; } else if ( c == SET_STOPSIZE ) { DBG(" STOPSIZE:"); state = ST_STOPSIZE ; } else if ( c == SET_CONTROL ) { DBG(" CONTROL:"); state = ST_CONTROL ; } else if ( c == PURGE_DATA ) { DBG(" PURGE:"); state = ST_PURGE ; } else { DBG("UNKNOWN: %02X\n", c); state = ST_WAIT_IAC ; } return ; } if ( state == ST_BAUDRATE ) { /* * Get 4 bytes of baudrate (MSB first) */ DBG(" %02X", c); value <<= 8 ; value += c ; vlen++ ; if ( vlen == 4 ) { /* * 4 bytes received, process the value * * Note: must acknowledge with the value that was set otherwise the * pyserial client considers that the set value is rejected. */ if ( value == 0 ) /* * Get baudrate */ //value = (int)(0.5 + 80e6/REG_BRR); value = flashConfig.baud_rate ; else /* * Set baudrate */ //REG_BRR = (int)(0.5 + 80e6/value); //UART_SetBaudrate(0,value); /* Not supported? */ flashConfig.baud_rate = value ; uart0_baud( value ); /* Acknowledge */ DBG(" = %ld\n", value); char v0 = value ; char v1 = value >> 8 ; char v2 = value >> 16 ; char v3 = value >> 24 ; espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_BAUDRATE}, 4 ); espbuffsend_tn( conn, (char[]){v3,v2,v1,v0}, 4 ); espbuffsend( conn, (char[]){IAC,SE}, 2 ); state = ST_WAIT_IAC ; return ; } return ; } if ( state == ST_DATASIZE ) { if ( c >= 5 && c <= 8 ) { /* * Set data size: CONF0 bits 3..2 */ REG_CONF0 = (REG_CONF0 & ~0xC) | ((c-5)<<2) ; } else { /* * Get data size */ c = 5 + ((REG_CONF0>>2) & 0x03) ; } /* Acknowledge datasize */ DBG(" %d\n", c ); espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_DATASIZE,c,IAC,SE},7 ); state = ST_WAIT_IAC ; return ; } if ( state == ST_PARITY ) { if ( c == 0 ) { /* * Get actual parity: CONF0 bits 1..0 */ c = (REG_CONF0>>2) & 0x03 ; if ( c==2 ) c = 1 ; else if ( c==3 ) c = 2 ; else c = 3 ; } else if ( c==1 || c==2 || c==3 ) { /* * Set parity * 1 NONE * 2 ODD * 3 EVEN */ char d ; if ( c==1 ) d = 0 ; else if ( c==2 ) d = 3 ; else d = 2 ; REG_CONF0 = (REG_CONF0 & ~0x3) | d ; } else { /* * Do not acknowledge unknown parity value */ state = ST_WAIT_IAC ; return ; } /* Acknowledge parity */ DBG(" %d\n", c ); espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_PARITY,c,IAC,SE},7 ); state = ST_WAIT_IAC ; return ; } if ( state == ST_STOPSIZE ) { if ( c == 0 ) { /* * Get actual stop bits: CONF0 bits 5..4 */ c = REG_CONF0>>4 & 0x03 ; if ( c==2 ) c = 3 ; else if ( c==3 ) c = 2 ; } else if ( c >= 1 && c <= 3 ) { /* * Set stop bits * 1 1 bit * 2 2 bits * 3 1.5 bit */ char d = c ; if ( c==2 ) d = 3 ; else if ( c==3 ) d = 2 ; REG_CONF0 = (REG_CONF0 & ~0x30) | (d<<4) ; } else { /* * Do not acknowledge unknown stop bits value */ state = ST_WAIT_IAC ; return ; } /* Acknowledge stop bits */ DBG(" %d\n", c ); espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_STOPSIZE,c,IAC,SE},7 ); state = ST_WAIT_IAC ; return ; } if ( state == ST_CONTROL ) { if ( c == 1 ) { /* * Use No Flow Control (outbound/both) * * Disable TX hardware flow: CONF0 bit 15 = 0 * Disable RX hardware flow: CONF1 bit 23 = 0 */ REG_CONF0 &= ~(1ULL<<15) ; REG_CONF1 &= ~(1ULL<<23) ; } else if ( c == 5 ) { /* * Set BREAK State ON */ REG_CONF0 |= (1ULL<<8) ; } else if ( c == 6 ) { /* * Set BREAK State OFF */ REG_CONF0 &= ~(1ULL<<8) ; } else if ( c == 8 ) { #ifdef USE_UART_CONTROL_LINES /* * Set DTR Signal State ON: CONF0 bit 7 = 1 */ REG_CONF0 |= (1ULL<<7) ; #else /* * Set DTR Signal State ON: drive MCU reset LOW */ if (mcu_reset_pin >= 0) { DBG("MCU reset gpio%d\n", mcu_reset_pin); GPIO_OUTPUT_SET(mcu_reset_pin, 0); } else DBG("MCU reset: no pin\n"); #endif } else if ( c == 9 ) { #ifdef USE_UART_CONTROL_LINES /* * Set DTR Signal State OFF: CONF0 bit 7 = 0 */ REG_CONF0 &= ~(1ULL<<7) ; #else /* * Set DTR Signal State OFF: drive MCU reset HIGH */ if (mcu_reset_pin >= 0) { DBG("MCU reset gpio%d\n", mcu_reset_pin); GPIO_OUTPUT_SET(mcu_reset_pin, 1); } else DBG("MCU reset: no pin\n"); #endif } else if ( c == 11 ) { #ifdef USE_UART_CONTROL_LINES /* * Set RTS Signal State ON: CONF0 bit 6 = 1 */ REG_CONF0 |= (1ULL<<6) ; #else /* * Set RTS Signal State ON: drive MCU ISP pin LOW */ if (mcu_isp_pin >= 0) { DBG("MCU ISP gpio%d\n", mcu_isp_pin); GPIO_OUTPUT_SET(mcu_isp_pin, 0); os_delay_us(100L); } else DBG("MCU isp: no pin\n"); slip_disabled++; #endif } else if ( c == 12 ) { #ifdef USE_UART_CONTROL_LINES /* * Set RTS Signal State OFF: CONF0 bit 6 = 0 */ REG_CONF0 &= ~(1ULL<<6) ; #else /* * Set RTS Signal State OFF: drive MCU ISP pin HIGH */ if (mcu_isp_pin >= 0) { GPIO_OUTPUT_SET(mcu_isp_pin, 1); os_delay_us(100L); } if (slip_disabled > 0) slip_disabled--; #endif } else { /* * Do not acknowledge unknown control */ state = ST_WAIT_IAC ; return ; } /* Acknowledge control */ DBG(" %d\n", c ); espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+SET_CONTROL,c,IAC,SE},7 ); state = ST_WAIT_IAC ; return ; } if ( state == ST_PURGE ) { if ( c == 1 ) { /* * Purge access server receive data buffer * * Reset RX FIFO: CONF0 bit 17 = 1 */ // REG_CONF0 |= (1ULL<<17) ; } else if ( c == 2 ) { /* * Purge access server transmit data buffer * * Reset TX FIFO: CONF0 bit 18 = 1 */ // REG_CONF0 |= (1ULL<<18) ; } else { state = ST_WAIT_IAC ; return ; } /* Acknowledge purge */ DBG(" %d\n", c ); espbuffsend( conn, (char[]){IAC,SB,COM_PORT_OPTION,100+PURGE_DATA,c,IAC,SE},7 ); state = ST_WAIT_IAC ; return ; } /* Unknown state */ #undef state #undef opt #undef vlen #undef value } /* Process bytes comming from a telnet connection */ static void ICACHE_FLASH_ATTR telnet_process_buf ( serbridgeConnData *conn, uint8_t *buf, int len ) { #ifdef SERBR_DBG os_printf("TELNET:"); for ( int i=0; i= 0) { #ifdef SERBR_DBG os_printf("MCU reset gpio%d\n", mcu_reset_pin); #endif GPIO_OUTPUT_SET(mcu_reset_pin, 0); os_delay_us(2000L); // esp8266 needs at least 1ms reset pulse, it seems... GPIO_OUTPUT_SET(mcu_reset_pin, 1); } #ifdef SERBR_DBG else { os_printf("MCU reset: no pin\n"); } #endif } // Receive callback static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) { serbridgeConnData *conn = ((struct espconn*)arg)->reverse; //os_printf("Receive callback on conn %p\n", conn); if (conn == NULL) return; bool startPGM = false; // at the start of a connection we're in cmInit mode and we wait for the first few characters // to arrive in order to decide what type of connection this is.. The following if statements // do this dispatch. An issue here is that we assume that the first few characters all arrive // in the same TCP packet, which is true if the sender is a program, but not necessarily // if the sender is a person typing (although in that case the line-oriented TTY input seems // to make it work too). If this becomes a problem we need to buffer the first few chars... if (conn->conn_mode == cmInit) { // If the connection starts with the Arduino or ARM reset sequence we perform a RESET if ((len == 2 && strncmp(data, "0 ", 2) == 0) || (len == 2 && strncmp(data, "?\n", 2) == 0) || (len == 3 && strncmp(data, "?\r\n", 3) == 0)) { startPGM = true; conn->conn_mode = cmPGM; // If the connection starts with a telnet negotiation we will do telnet } // else if (len >= 3 && strncmp(data, (char[]){IAC, WILL, COM_PORT_OPTION}, 3) == 0) { else if ( len>2 && data[0]==IAC && (data[1]==WILL || data[1]==DO) ) { conn->conn_mode = cmTelnet; conn->tn_state = ST_NORMAL; // note that the three negotiation chars will be gobbled-up by telnetUnwrap #ifdef SERBR_DBG os_printf("telnet mode\n"); #endif // looks like a plain-vanilla connection! } else { conn->conn_mode = cmTransparent; } // if we start out in cmPGM mode due to a connection to the second port we need to do the // reset dance right away } else if (conn->conn_mode == cmPGMInit) { conn->conn_mode = cmPGM; startPGM = true; } // do the programming reset dance if (startPGM) { #ifdef SERBR_DBG os_printf("MCU Reset=gpio%d ISP=gpio%d\n", mcu_reset_pin, mcu_isp_pin); os_delay_us(2*1000L); // time for os_printf to happen #endif // send reset to arduino/ARM, send "ISP" signal for the duration of the programming if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 0); os_delay_us(100L); if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 0); os_delay_us(2000L); if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1); //os_delay_us(100L); //if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1); os_delay_us(1000L); // wait a millisecond before writing to the UART below conn->conn_mode = cmPGM; slip_disabled++; // disable SLIP so it doesn't interfere with flashing #ifdef SKIP_AT_RESET serledFlash(50); // short blink on serial LED return; #endif } if ( conn->conn_mode == cmTelnet ) { /* * Process Telnet protocol */ telnet_process_buf( conn, (uint8_t *)data, len ); } else { // write the buffer to the uart uart0_tx_buffer( data, len ); } serledFlash(50); // short blink on serial LED } //===== UART -> TCP // Send all data in conn->txbuffer // returns result from espconn_sent if data in buffer or ESPCONN_OK (0) // Use only internally from espbuffsend and serbridgeSentCb static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) { sint8 result = ESPCONN_OK; if (conn->txbufferlen != 0) { //os_printf("TX %p %d\n", conn, conn->txbufferlen); conn->readytosend = false; result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen); conn->txbufferlen = 0; if (result != ESPCONN_OK) { os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); conn->txbufferlen = 0; if (!conn->txoverflow_at) conn->txoverflow_at = system_get_time(); } else { conn->sentbuffer = conn->txbuffer; conn->txbuffer = NULL; conn->txbufferlen = 0; } } return result; } // Escape '\xFF' bytes in buffer for Telnet protocol before sending it over the // air // static sint8 ICACHE_FLASH_ATTR espbuffsend_tn ( serbridgeConnData *conn, const char *data, uint16 len ) { sint8 r ; for ( char const *p=data ; ptxbufferlen >= MAX_TXBUFFER) goto overflow; // make sure we indeed have a buffer if (conn->txbuffer == NULL) conn->txbuffer = os_zalloc(MAX_TXBUFFER); if (conn->txbuffer == NULL) { os_printf("espbuffsend: cannot alloc tx buffer\n"); return -128; } // add to send buffer uint16_t avail = conn->txbufferlen+len > MAX_TXBUFFER ? MAX_TXBUFFER-conn->txbufferlen : len; os_memcpy(conn->txbuffer + conn->txbufferlen, data, avail); conn->txbufferlen += avail; // try to send sint8 result = ESPCONN_OK; if (conn->readytosend) result = sendtxbuffer(conn); if (avail < len) { // some data didn't fit into the buffer if (conn->txbufferlen == 0) { // we sent the prior buffer, so try again return espbuffsend(conn, data+avail, len-avail); } goto overflow; } return result; overflow: if (conn->txoverflow_at) { // we've already been overflowing if (system_get_time() - conn->txoverflow_at > 10*1000*1000) { // no progress in 10 seconds, kill the connection os_printf("serbridge: killing overlowing stuck conn %p\n", conn); espconn_disconnect(conn->conn); } // else be silent, we already printed an error } else { // print 1-time message and take timestamp os_printf("serbridge: txbuffer full, conn %p\n", conn); conn->txoverflow_at = system_get_time(); } return -128; } //callback after the data are sent static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) { serbridgeConnData *conn = ((struct espconn*)arg)->reverse; //os_printf("Sent CB %p\n", conn); if (conn == NULL) return; //os_printf("%d ST\n", system_get_time()); if (conn->sentbuffer != NULL) os_free(conn->sentbuffer); conn->sentbuffer = NULL; conn->readytosend = true; conn->txoverflow_at = 0; sendtxbuffer(conn); // send possible new data in txbuffer } void ICACHE_FLASH_ATTR console_process(char *buf, short len) { // push buffer into web-console for (short i=0; i 0) { //os_printf("SLIP: disabled got %d\n", length); console_process(buf, length); } else { slip_parse_buf(buf, length); } serledFlash(50); // short blink on serial LED } //===== Connect / disconnect // Disconnection callback static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) { serbridgeConnData *conn = ((struct espconn*)arg)->reverse; if (conn == NULL) return; // Free buffers if (conn->sentbuffer != NULL) os_free(conn->sentbuffer); conn->sentbuffer = NULL; if (conn->txbuffer != NULL) os_free(conn->txbuffer); conn->txbuffer = NULL; conn->txbufferlen = 0; // Send reset to attached uC if it was in programming mode if (conn->conn_mode == cmPGM && mcu_reset_pin >= 0) { if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1); os_delay_us(100L); GPIO_OUTPUT_SET(mcu_reset_pin, 0); os_delay_us(100L); GPIO_OUTPUT_SET(mcu_reset_pin, 1); } conn->conn = NULL; } // Connection reset callback (note that there will be no DisconCb) static void ICACHE_FLASH_ATTR serbridgeResetCb(void *arg, sint8 err) { os_printf("serbridge: connection reset err=%d\n", err); serbridgeDisconCb(arg); } // New connection callback, use one of the connection descriptors, if we have one left. static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { struct espconn *conn = arg; // Find empty conndata in pool int i; for (i=0; iproto.tcp->local_port, conn, i); #endif if (i==MAX_CONN) { #ifdef SERBR_DBG os_printf("Aiee, conn pool overflow!\n"); #endif espconn_disconnect(conn); return; } os_memset(connData+i, 0, sizeof(struct serbridgeConnData)); connData[i].conn = conn; conn->reverse = connData+i; connData[i].readytosend = true; connData[i].conn_mode = cmInit; // if it's the second port we start out in programming mode if (conn->proto.tcp->local_port == serbridgeConn2.proto.tcp->local_port) connData[i].conn_mode = cmPGMInit; espconn_regist_recvcb(conn, serbridgeRecvCb); espconn_regist_disconcb(conn, serbridgeDisconCb); espconn_regist_reconcb(conn, serbridgeResetCb); espconn_regist_sentcb(conn, serbridgeSentCb); espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY); } //===== Initialization void ICACHE_FLASH_ATTR serbridgeInitPins() { mcu_reset_pin = flashConfig.reset_pin; mcu_isp_pin = flashConfig.isp_pin; #ifdef SERBR_DBG os_printf("Serbridge pins: reset=%d isp=%d swap=%d\n", mcu_reset_pin, mcu_isp_pin, flashConfig.swap_uart); #endif if (flashConfig.swap_uart) { PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 4); PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 4); PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTCK_U); if (flashConfig.rx_pullup) PIN_PULLUP_EN(PERIPHS_IO_MUX_MTDO_U); else PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U); system_uart_swap(); } else { PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, 0); PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, 0); PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); if (flashConfig.rx_pullup) PIN_PULLUP_EN(PERIPHS_IO_MUX_U0RXD_U); else PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0RXD_U); system_uart_de_swap(); } // set both pins to 1 before turning them on so we don't cause a reset if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1); if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1); // switch pin mux to make these pins GPIO pins if (mcu_reset_pin >= 0) makeGpio(mcu_reset_pin); if (mcu_isp_pin >= 0) makeGpio(mcu_isp_pin); } // Start transparent serial bridge TCP server on specified port (typ. 23) void ICACHE_FLASH_ATTR serbridgeInit(int port1, int port2) { serbridgeInitPins(); os_memset(connData, 0, sizeof(connData)); os_memset(&serbridgeTcp1, 0, sizeof(serbridgeTcp1)); os_memset(&serbridgeTcp2, 0, sizeof(serbridgeTcp2)); // set-up the primary port for plain bridging serbridgeConn1.type = ESPCONN_TCP; serbridgeConn1.state = ESPCONN_NONE; serbridgeTcp1.local_port = port1; serbridgeConn1.proto.tcp = &serbridgeTcp1; espconn_regist_connectcb(&serbridgeConn1, serbridgeConnectCb); espconn_accept(&serbridgeConn1); espconn_tcp_set_max_con_allow(&serbridgeConn1, MAX_CONN); espconn_regist_time(&serbridgeConn1, SER_BRIDGE_TIMEOUT, 0); // set-up the secondary port for programming serbridgeConn2.type = ESPCONN_TCP; serbridgeConn2.state = ESPCONN_NONE; serbridgeTcp2.local_port = port2; serbridgeConn2.proto.tcp = &serbridgeTcp2; espconn_regist_connectcb(&serbridgeConn2, serbridgeConnectCb); espconn_accept(&serbridgeConn2); espconn_tcp_set_max_con_allow(&serbridgeConn2, MAX_CONN); espconn_regist_time(&serbridgeConn2, SER_BRIDGE_TIMEOUT, 0); }