ESP-01 GPIO3 Bootup State

Hello, I have an ESP-01s running in a sleep cycle of 500mS, it goes to sleep for 500mS then wakes and checks an input then goes back to sleep again.
The input I'm using is GPIO3 (the RX pin) and I use:

Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);

For serial debug output while leaving RX as general purpose input.
The above all works fine, except GPIO3 is held high at startup by the MCU.

Does anyone know how long GPIO3 remain high at boot? I can't find this info in the datasheet, perhaps I'm overlooking it.

I would much appreciate any input.
Thanks.

GPIO3 is also RX. I guess that as soon as you issue Serial.begin(), the pin pullup resistor is activated because the RX signal is active LOW. See ESP-01 pin number confusion - Everything ESP8266 for a pinout diagram.
If you are merely transmitting but not receiving data, you could probably issue a pinMode( 3, INPUT) or pinMode( 3, OUTPUT) after the Serial.begin() statement if you want to use the pin for another purpose.

when using the ESP-01 U0Rx (GPIO3) and U0Tx (GPIO1) i wait for 5 seconds for Serial.begin() to start, e.g.

 // testing using an Arduino Due
 // DUE 3.3 V to ESP-01 3.3V (VCC)
 // DUE GND to ESP-01 GND
 // DUE RX1 (pin 19) to ESP-01 Tx
 // DUE TX1 (pin 18) to ESP-01 Rx

void setup() { 
    Serial.begin(115200); 
    delay(5000); // We start by connecting to a WiFi network 
    Serial.println();
    Serial.println(); Serial.print("Connecting to ");
    Serial.println(ssid);

6v6gt (good valve, in times when electrons behaved themselves), yes I'm using GPIO3 as an input and have set it as such:

Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
pinMode(3, INPUT);

I don't have an oscilloscope to see what's going on immediately after boot but GPIO3 is behaving as if the ESP-01 is driving it HIGH for a while. This link it states:

GPIOs 4 and 5 are the only ones that are always high impedance. All others do have internal pull-ups or are even driven low/high during boot.

GPIOs 3, 12, 13 and 14 pulled HIGH during boot. Their actual state does not influence the boot process.

Which confirms what you say, GPIO3 is pulled high on boot, i got carried away by the idea it was being driven high.
Either way because the sleep/poll cycle is short, 500mS it would be good to know when the internal pull-up is switched out, it seems to be pulled high long enough for my DVM to see it pulse - I wouldn't expect to see that.

I don't have an outstanding issue with the project as such, but had to 'fiddle' to work around reading a high on GPIO3 when I thought it should be low - it has an external 4k7 pull-down.

Thank you for your reply 6v6gt.

The main sleep code is:

/** Brief
 * Sleep for under a second and poll the sensor
 * Radio alarm to the nearest sensor using espnow
 * Periodically radio a keep alive
 * Notify by radio a low battery voltage
 */
#include <ESP8266WiFi.h>
#include <espnow.h>

#undef SERIAL_DEBUG

#define INCLUDE_CREDENTIALS
#include "../../shared.h"
#include "../../shared.cpp"

ADC_MODE(ADC_VCC); // vcc read-mode

// Include API-Headers
extern "C"
{
#include "ets_sys.h"
#include "os_type.h"
#include "osapi.h"
#include "mem.h"
#include "user_interface.h"
#include "cont.h"
}

// state definitions
#define STATE_COLDSTART 0
#define STATE_SLEEP_WAKE 1
#define STATE_ALARM 2
#define STATE_CONNECT_WIFI 4

#define ALARM_PIN 3												 // pin to test for alarm condition
#define SLEEP_TIME 500000									 // sleep intervals in us
//#define SLEEP_COUNTS_FOR_KEEP_ALIVE 0.5 * 360 // send live msg periodically
#define SLEEP_COUNTS_FOR_KEEP_ALIVE 24 * 3600 // send live msg periodically
#define SLEEP_COUNTS_FOR_BATT_CHECK 2 * 24 // send low-Batt msg once a day
#define BATT_WARNING_VOLTAGE 2.4					 // Voltage for Low-Bat warning
#define WIFI_CONNECT_TIMEOUT_S 20					 // max time for wifi connect to router, if exceeded restart

// RTC-MEM Adresses
#define RTC_COLD_START 65
#define RTC_STATE 66
#define RTC_WAKE_COUNT 67
#define RTC_MSG_TYPE 68				// type of message to send
#define RTC_ALARM_COUNT 69		// count of alarm msg's sent
#define RTC_SENSOR_ENABLED 70 // Sensor Enabled / Disabled

// msg Types
#define MSG_NO_MSG 0
#define MSG_COLD_START 1	// at startup
#define MSG_LIVE_SIGNAL 2 // Keep alive signal
#define MSG_ALARM 3				// Sensor alarm triggered
#define MSG_LOW_BAT 4

const char *msgStrs[] =
		{
				"None",
				"Cold Start",
				"Keep Alive",
				"Alarm",
				"Low Battery"};
const char *MsgUnknown = "Unknown";

#define VCC_ADJ 1.0

//#define DEBUG_NO_SEND

const char *transMsg(int n);

bool initEspNow();

const uint32_t magicWord = 0xaa5555aa;

// radio data sent and received
struct_rf_data rxData, txData;
// system state
machine_state sysState;

// system commands
SystemCmds sysCmds;

// global variables
byte event = 0;
uint32_t
		state, // state variable
		sleepCount,
		time1,
		time2,
		b = 0, // Temporary buffer
		alarmCount = 0;

int i;

void setup()
{
	// begin serial but only use the uart tx leaving the rx to use as an input (or output).
	Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
	// set what was RX to an input
	pinMode(ALARM_PIN, INPUT);

#ifdef SERIAL_DEBUG
	delay(10);
	Serial.println();
	Serial.println();
	Serial.println(F("Starting..."));
#endif
	time1 = rtcRead(RTC_COLD_START);
	state = rtcRead(RTC_STATE);
	b = rtcRead(RTC_MSG_TYPE);
	bool sensorActive = rtcRead(RTC_SENSOR_ENABLED) & 1;
#ifdef SERIAL_DEBUG
	Serial.printf("Cold St:%x, State:%x msgtype:%x\n", time1, state, b);
#endif
	WiFi.forceSleepBegin(); // send wifi to sleep to reduce power consumption
	yield();

	if (time1 != magicWord)
	{ // cold start, magic number is not present
		state = STATE_COLDSTART;
		rtcWrite(RTC_COLD_START, magicWord);
		rtcWrite(RTC_ALARM_COUNT, 0);
		rtcWrite(RTC_MSG_TYPE, MSG_NO_MSG);	// no msg pending
		alarmCount = 0;
		delay(7000);	// allow time to apply power and withdraw without triggering alarm
		///////////////////////////////////////////////
		// TODO: send batt voltage on cold start
		// float v = (float)ESP.getVcc() * VCC_ADJ;
		// Serial.printf("\nBatt:===== %d V\n\n", (uint32_t)(v * 100));

		// rtcWrite(RTC_MSG_TYPE, MSG_LOW_BAT); // set msg type to send
		// // prepare to activate wifi
		// WiFi.forceSleepWake();
		// WiFi.mode(WIFI_STA);
		// rtcWrite(RTC_STATE, STATE_CONNECT_WIFI); // one more sleep required to to wake with wifi on
		// ESP.deepSleep(10, WAKE_RFCAL);
		// yield();
	}
	else
	{																				 // reset was due to sleep-wake or external event
		alarmCount = rtcRead(RTC_ALARM_COUNT); // read state (rtc blocks of 4 bytes) from
	}

retrySwitch:
	// now the restart cause is clear, handle the different states
#ifdef SERIAL_DEBUG
	Serial.printf("State: %x\n", state);
#endif
	switch (state)
	{
	case STATE_COLDSTART: // first run after power on - initializes
#ifdef SERIAL_DEBUG
		Serial.printf("COLD START\n");
#endif
		sleepCount = 0;
		rtcWrite(RTC_WAKE_COUNT, 0); // initialize counter
		rtcWrite(RTC_MSG_TYPE, MSG_COLD_START); // set a cold start msg
		// prepare to activate wifi
		WiFi.forceSleepWake();
		WiFi.mode(WIFI_STA);
		rtcWrite(RTC_STATE, STATE_CONNECT_WIFI); // one more sleep required to to wake with wifi on
		ESP.deepSleep(10, WAKE_RFCAL);
		yield();
		break;
	case STATE_SLEEP_WAKE:
#ifdef SERIAL_DEBUG
		Serial.printf("Woke up from sleep\n");
#endif
		sleepCount = rtcRead(RTC_WAKE_COUNT);
		sleepCount++;
		rtcWrite(RTC_WAKE_COUNT, sleepCount);

		// could be a sleep wake or an alarm
		sensorActive = true;							 // TODO: needs to be stored in eeprom and rtc

		// read the alarm pin to find out whether an alarm is the reset cause
		int loopCount;
		delay(500); // mitigate PIR falsely going active when WiFi tx's

		for (loopCount = 5; digitalRead(ALARM_PIN) == HIGH && loopCount > 0; loopCount--) // ALARM_PIN is the esp01 serial rx pin
		{
			delay(10);
		}

		if (sensorActive && loopCount == 0)
		{ // this is an alarm!
			state = STATE_ALARM;
			goto retrySwitch;
		}
		if (sleepCount > SLEEP_COUNTS_FOR_KEEP_ALIVE)
		{																					 // it's time to send keep-live
			// float v = (float)ESP.getVcc() / 1024.0;
#ifdef SERIAL_DEBUG
			// Serial.printf("\r\n=====Batt:%.2fV\r\n", v-0.37);
#endif
			rtcWrite(RTC_MSG_TYPE, MSG_LIVE_SIGNAL); // set msg type to send
			// prepare to activate wifi
			WiFi.forceSleepWake();
			WiFi.mode(WIFI_STA);
			// set state for next wakeUp
			rtcWrite(RTC_STATE, STATE_CONNECT_WIFI); // one more sleep required to to wake with wifi on
			ESP.deepSleep(10, WAKE_RFCAL);
			yield();
		}

		if (sleepCount > SLEEP_COUNTS_FOR_BATT_CHECK)
		{ // check battery TODO: complete this...
			// float v = (float)(ESP.getVcc() / 1024) - 0.35;
			// Serial.printf("\n=====Batt: %fV\n\n", v);

			// if (v < BATT_WARNING_VOLTAGE)
			// // if ((float)ESP.getVcc() * VCC_ADJ < BATT_WARNING_VOLTAGE)
			// {
			// 	sleepCount = 0;											 // start from beginning so batt-warning is not sent every wakeup
			// 	rtcWrite(RTC_MSG_TYPE, MSG_LOW_BAT); // set msg type to send
			// 	// prepare to activate wifi
			// 	WiFi.forceSleepWake();
			// 	WiFi.mode(WIFI_STA);
			// 	rtcWrite(RTC_STATE, STATE_CONNECT_WIFI); // one more sleep required to to wake with wifi on
			// 	ESP.deepSleep(10, WAKE_RFCAL);
			// 	yield();
			// }
		}

		// no special event, go to sleep again
		rtcWrite(RTC_WAKE_COUNT, sleepCount);
		rtcWrite(RTC_STATE, STATE_SLEEP_WAKE); // set state for next wakeUp
#ifdef SERIAL_DEBUG
		Serial.printf("Back to sleep now...\n");
#endif
		ESP.deepSleep(SLEEP_TIME, WAKE_RF_DISABLED);
		yield(); // pass control back to background processes to prepate sleep
		break;

	case STATE_ALARM:
#ifdef SERIAL_DEBUG
		Serial.printf("Alarm State!\n");
#endif	
		rtcWrite(RTC_MSG_TYPE, MSG_ALARM); // set msg type to send
		// prepare to activate wifi
		WiFi.forceSleepWake();
		WiFi.mode(WIFI_STA);
		rtcWrite(RTC_STATE, STATE_CONNECT_WIFI); // one more sleep required to to wake with wifi
		ESP.deepSleep(10, WAKE_RFCAL);
		yield();
		break;

	case STATE_CONNECT_WIFI:
		char str[32];
		WiFi.forceSleepWake();
		delay(50);
		wifi_set_sleep_type(MODEM_SLEEP_T);
		bool espNowReady = initEspNow();
		yield();

		time1 = system_get_time();

		// Connect to WiFi network
#ifdef SERIAL_DEBUG
		Serial.println();
		Serial.println();
#endif	
		if (espNowReady)
		{
			alarmCount++;
			txData.sysState = sysState.flags;
			txData.txSentCount = alarmCount;
			rtcWrite(RTC_ALARM_COUNT, alarmCount);
			uint8_t cmdToSend;
			b = rtcRead(RTC_MSG_TYPE);
			switch (b)
			{
			case MSG_ALARM:
				cmdToSend = sysCmdAlarm;
				strcpy(str, "ALARM");
				break;
			case MSG_LIVE_SIGNAL:
				cmdToSend = sysCmdKeepAlive;
				strcpy(str, "KEEP ALIVE");
				break;
			case MSG_LOW_BAT:
				cmdToSend = sysCmdLowBat;
				strcpy(str, "LOW BAT");
				break;

			default:
				cmdToSend = sysCmdNone;
				strcpy(str, "UNKNOWN");
				break;
			}

			// sendCmd(cmdToSend, CONTROLLER_MAC);
			sendCmd(cmdToSend, FRONTDOOR_MAC);
#ifdef SERIAL_DEBUG
			Serial.printf("\nSent: %s SleepCount:%d\n", str, sleepCount);
#endif
			// delay(500); // TODO: tx shouldn't take this long

			time2 = system_get_time();
			if (((time2 - time1) / 1000000) > WIFI_CONNECT_TIMEOUT_S) // wifi connection lasts too long, retry
			{
				ESP.deepSleep(10, WAKE_RFCAL);
				yield();
			}
		}

		// now re-initialize
		rtcWrite(RTC_WAKE_COUNT, 0);					 // initialize counter
		rtcWrite(RTC_MSG_TYPE, MSG_NO_MSG);		 // no msg pending
		rtcWrite(RTC_STATE, STATE_SLEEP_WAKE); // set state for next wakeUp
		ESP.deepSleep(SLEEP_TIME, WAKE_RF_DISABLED);
		yield(); // pass control back to background processes to prepare sleep
		break;
	}
	delay(50);
	//
}

void loop()
{
	yield();
}

// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus)
{
#ifdef SERIAL_DEBUG
	Serial.print("Last Packet Send Status: ");
	if (sendStatus == 0)
	{
		Serial.println("Delivery success");
	}
	else
	{
		Serial.println("Delivery fail");
	}
#endif
}

// Callback function that will be executed when data is received
void OnDataRecv(uint8_t *macAddr, uint8_t *data, uint8_t dataLen)
{
	memcpy(&rxData, data, dataLen);

	// format the mac address
	char macStr[18];
	formatMacAddress(macAddr, macStr, 18);

	// debug log the message to the serial port
	char hdr[50];
	snprintf(hdr, 49, "Received message from: %s", macStr);
	printRfData(rxData, hdr);

	// what is the rx'd cmd
	switch (rxData.sysCmd)
	{
	case sysCmdDisable:
		clearState(STA_ACTIVE);
		break;
	case sysCmdEnable:
		setState(STA_ACTIVE);
		break;
	case sysCmdClearCounters: // clears counts of no. tx's, fails, etc
		txFailCount = 0;
		alarmCount = 0;
		break;
	}
	digitalWrite(2, getState(STA_ACTIVE));
}

// Initialise ESPNOW return true on success
bool initEspNow()
{
	// shut down wifi
	WiFi.disconnect();

	WiFi.mode(WIFI_STA);

	if (esp_now_init() != 0)
	{
#ifdef SERIAL_DEBUG
		Serial.println("!!!!!! ERROR initializing ESP-NOW");
#endif	
		return false;
	}

#ifdef SERIAL_DEBUG
	Serial.printf("Espnow init OK\n");
#endif

	// Once ESPNow is successfully Init, we will register for Send CB to
	// get the status of Transmitted packet
	// 	esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
	esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
	esp_now_register_recv_cb(OnDataRecv);
	esp_now_register_send_cb(OnDataSent);
	// Register peer
	esp_now_add_peer(CONTROLLER_MAC, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
#ifdef SERIAL_DEBUG
	// Serial.printf("add peer:%d\n", rv);
#endif
	return true;
}

void broadcast(uint8_t *destination)
{
	char buf[60];
	// todo: the following code doesn't work on esp8266?
	// Register peer
	// int ap = esp_now_is_peer_exist(destination);
	// Serial.printf("exists rv:%d\n", ap);
	// if (ap == 0)
	// {
	// 	esp_now_add_peer(destination, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
	// }
	// else
	// 	Serial.println("Found Peer");
	strcpy(txData.toSensor, "CO");
	strcpy(txData.fromSensor, "CP");
	txData.sysState = sysState.flags;
	txData.txFailCount = txFailCount;
	txData.arg2 = sleepCount;

	int rv = esp_now_send(destination, (uint8_t *)&txData, sizeof(txData));
	
	// debug: log to serial port
	char macStr[18];
	formatMacAddress(destination, macStr, 18);
	sprintf(buf, "esp_now_send to: %s (rv:%d)", macStr, rv);
	printRfData(txData, buf);
}

void sendCmd(uint8_t cmd, uint8_t *destination)
{
	if (!destination)
	{
		destination = &broadcastAllMac[0];
	}
	txData.sysCmd = cmd;
	broadcast(destination); // can be to all sensors
}

uint32_t rtcRead(uint16_t rtcAddr)
{
	system_rtc_mem_read(rtcAddr, &b, 4);
	return b;
}
void rtcWrite(uint16_t rtcAddr, uint32_t val)
{
	b = val;
	system_rtc_mem_write(rtcAddr, &b, 4);
}

const char *transMsg(uint n)
{
	if (n >= 0 && n < ARRAYSIZE(msgStrs))
		return msgStrs[n];
	return MsgUnknown;
}

horace, thanks for you input although it doesn't specifically relate to the question I was having.

Sorry. I didn't notice that your Serial.begin() explicitly excluded RX so that should not be the cause of the pull up resistor for gpio3 remaining on.

I'm not sure what stabilisation times you can expect on system start or wakeup from sleep. However, after a boot you can anyway expect a delay of several seconds before a usable WLAN connection has been made so maybe the behaviour of gpio3 is not so critical during this period.

Yes, WiFi connect takes between 2-4 seconds to connect, it's the reason in this case I use ESPNow instead, it connects much faster, tx time is reduced as is the tx power consumption.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.