Показ дописів із міткою example. Показати всі дописи
Показ дописів із міткою example. Показати всі дописи

субота, 7 травня 2022 р.

MTB: PSoC4 CY8CKIT-149 дослідимо аналогові блоки ADC та LPCOMP за допомоги PDL

 Передмова

Продовжимо знайомство з мікроконтролерами Cypress, а нині це Infineon, та засобом розробки ModusToolBoxIDE. В цій статті пропоную розібратись, як працювати з аналоговими блоками, такими як ADC та LPCOMP. Програмувати будемо в засобі розробки MTB_IDE, та драйверами PDL. В попередній статті "Встановлення ModusToolBox та перший проект на CY8CKIT-149 PSoC® 4100S Plus Prototyping Kit" розглянули як встановити MTB_IDE, та створили перший проект. Використовувати будемо все той же ж  KIT Prototyping CY8CKIT-149 PSoC4, але все що буде в цій статті, буде підходити до всіх девайсів які підтримуються MTB та драйверами PDL.

Створення проекту

Запускаємо ModuToolBoxIDE, та створюємо новий проект як на малюнку, на швидкій панелі тиснемо 1:

Створення нового проекту

Далі потрібно обрати мікроконтролер або плату розробника, в цьому випадку це KIT Prototyping CY8CKIT-149 PSoC4, у вас це може бути інший чип. Головне, щоб він підтримувався та мав на борту ADC та LPCOMP:

Вибір цілі

  1. Обираємо серію МК - PSoC4
  2. Обираємо плату з цієї серії
  3. Можна переглянути документацію по цій платі
  4. Тиснемо "Next"
Створювач проектів "Project Creator" запропонує обрати шаблон проекту з прикладом до якоїсь периферії, або пустий проект. Обираємо пустий. Нам потрібно з нуля розібратись зі всіма налаштуваннями і ньюансами.
Обираємо шаблон для проекту
  1. Обираєом шаблон пустого проекту
  2. Називаємо свій проект за смаком і своїми вподобаннями
Після того, як проект створено і він з'явився в "Project explorer", пропоную розгорнути дерево проекту і відкрити головний файл "main.c":
Відкриваємо файл main.c
Залишилось всього трішки - дописати демо-код. Чим і займемось. Але перед тим відкриємо зі швидкої панелі "device-configurator", та налаштуємо периферію, як слід, для наших потреб.
Панель швидкого доступу
  1. Побудувати проект
  2. Очистити проект
  3. Прошити МК і перейти в режим відладки
  4. Залити прошивку і запустити її
  5. Відкрити утіліту "Library Manager" 
  6. Відкрити утіліту "Device Configurator"
Саме зараз нас цікавить "Device Configurator". Запусимо його і оберемо ADC:
Обираємо ADC
  1. Периферія
  2. Аналогова периферія
  3. Програмований аналог PASS
  4. 12-bit SAR ADC
Далі перейдемо до налаштувань самого ADC:
Налаштування ADC частина 1

Налаштування ADC частина 2
  1. Оберемо опірну напругу для порівняння, таку як 3.3 Вольта
  2. Поки обиремо 2 канали, далі можливо додамо ще каналів
  3. Джерело тактування оберемо один з вільних дільників
  4. Назначемо вільний та доступний пін для 0 канала
  5. Назначемо вільний та доступний пін для 1 канала
Тиснемо "CTRL + S" та зберігаємо файл налаштувань. Ще можна переглянути що за код згенерувався для налаштування ADC і двох каналів:
Попередній перегляд коду налаштувань
  1. Перейдемо до вкладки "Code Preview"
  2. Переглянемо, сам код, який генерується в проект
Зберемо такий макет для дослідження, та напишемо демо-код:
Схема макету для дослідження ADC

Демо-код ADC

Перед тим як писати демо-код, додамо бібліотеку retarget до проекту за допомоги "Library manager", для того щоб друкувати повідомлення серіал термінал по UART:

Додаємо бібліотеку retarget
  1. Тиснемо вкладку "бібліотека"
  2. Обираємо периферію
  3. Та позначаємо необхідну бібліотеку
  4. Оновлюємо перелік бібліотек для проекту
В початковий код додаємо бібліотеку ""cy_retarget_io.h"":
#include "cy_pdl.h"
#include "cybsp.h"
#include "cy_retarget_io.h"
В головну функцію main додаємо ініціалізацію бібліотеки retarget для друку повідомлень в термінал:
result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX, CY_RETARGET_IO_BAUDRATE);

if (result != CY_RSLT_SUCCESS)
{
	CY_ASSERT(0);
}
І на загал, код в файлі main.c буде виглядати таким чином:
#include "cy_pdl.h"
#include "cybsp.h"
#include "cy_retarget_io.h"

int main(void)
{
    cy_rslt_t result;
    uint16_t resADC[2];

    /* Initialize the device and board peripherals */
    result = cybsp_init() ;

    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(0);
    }

    result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX, CY_RETARGET_IO_BAUDRATE);

    if (result != CY_RSLT_SUCCESS)
    {
		CY_ASSERT(0);
    }

    /* Enable global interrupts */
    __enable_irq();

    printf("\x1b[2J\x1b[;H");
    printf("*------------------------------*\r\n");
    printf("* Demo ADC and LPCOMP started! *\r\n");
    printf("*------------------------------*\r\n");

    result = Cy_SAR_Init(SAR0, &pass_0_sar_0_config);

    if (result != CY_RSLT_SUCCESS)
    {
	CY_ASSERT(0);
    }

    Cy_SAR_Enable(SAR0);

    for (;;)
    {
    	Cy_SAR_StartConvert(SAR0, CY_SAR_START_CONVERT_SINGLE_SHOT);
    	Cy_SAR_IsEndConversion(SAR0, CY_SAR_WAIT_FOR_RESULT);
    	resADC[0] = Cy_SAR_GetResult16(SAR0, 0);
    	resADC[1] = Cy_SAR_GetResult16(SAR0, 1);
    	printf("ADC Result Channel 0 = %d mV   \r\n", Cy_SAR_CountsTo_mVolts(SAR0, 0, resADC[0]));
    	printf("ADC Result Channel 1 = %d mV   \r\n", Cy_SAR_CountsTo_mVolts(SAR0, 1, resADC[1]));
    	printf("\x1b[1F");
    	printf("\x1b[1F");
    	Cy_SysLib_Delay(1000);
    }
}
Заливаємо, запускаємо, відкриваємо термінал і спостерігаємо щось подібне:
Результат
Крутимо потенциометри і спостерігаємо за результатом.

Відео посібник


Вмикаємо і налаштовуємо LPCOMP

LPCOMP (Low Power Comparator) - компаратор з низьким споживанням. Відкриємо знову "device configurator" та увімкнемо і налаштуємо один з компараторів:
Вмикаємо LPCOMP
Та налаштовуємо як на цьому скріншоті:
Налаштування LPCOMP

  1. Конфігуруємо вихід компаратора як "Direct" коли напруга на позитивному вході менша за ту, що на негативному вході, то на виході 0, і навпаки, коли на позитивному вході напруга перевищила поріг, що є на негативному вході, то на виході буде 1. 
  2. Живлення і швидкість порівняння обираємо "Normal power/Fast".
  3. Виникає переривання в обох випадках, як по фронту, так і по спаду.
  4. Обираємо шпильку для позитивного входу.
  5. Обираємо шпильку для негативного входу.
  6. Обираємо шпильку для виходу.
  7. Обиремо, саме той пін для виходу, де є під'єднаний світлодіод, щоб наочно бачити результат порівняння вхідних напруг. Так як світлодіод під'єднаний постійно до 3.3В, то світитиме, коли на виході буде 0, а як на виході компаратора з'явиться 1 то світлодіод згасне.
Вибір шпильки для виходу компаратора

Зберігаємо зміни "CTRL+S" додаємо з'єднання компаратора і дільників.

Схема під'єднання

Додамо дротів з дільників на вхід компаратора

Демо-код для компаратора

#include "cy_pdl.h"
#include "cybsp.h"
#include "cy_retarget_io.h"
#include "stdbool.h"

volatile bool flagLPComp = false;
cy_stc_lpcomp_context_t lpcomp_0_context;

static void lpcomp_interrupt_handler(void);

int main(void)
{
    cy_rslt_t result;
    uint16_t resADC[2];


    cy_stc_sysint_t lpcomp_interrupt_config = {
    	.intrSrc = lpcomp_0_comp_0_IRQ,
	.intrPriority = 3
    };

    /* Initialize the device and board peripherals */
    result = cybsp_init() ;

    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(0);
    }

    result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX, CY_RETARGET_IO_BAUDRATE);

    if (result != CY_RSLT_SUCCESS)
    {
	CY_ASSERT(0);
    }

    /* Enable global interrupts */
    __enable_irq();

    printf("\x1b[2J\x1b[;H");
    printf("*------------------------------*\r\n");
    printf("* Demo ADC and LPCOMP started! *\r\n");
    printf("*------------------------------*\r\n");

    result = Cy_SAR_Init(SAR0, &pass_0_sar_0_config);

    if (result != CY_RSLT_SUCCESS)
    {
	CY_ASSERT(0);
    }

    Cy_SAR_Enable(SAR0);

    Cy_LPComp_Init(LPCOMP, CY_LPCOMP_CHANNEL_0, &lpcomp_0_comp_0_config, &lpcomp_0_context);
    Cy_LPComp_Enable(LPCOMP, CY_LPCOMP_CHANNEL_0, &lpcomp_0_context);

    Cy_LPComp_SetInterruptMask(LPCOMP, CY_LPCOMP_CHANNEL0_INTR);
    Cy_SysInt_Init(&lpcomp_interrupt_config, &lpcomp_interrupt_handler);
    NVIC_ClearPendingIRQ(lpcomp_interrupt_config.intrSrc);
    NVIC_EnableIRQ(lpcomp_interrupt_config.intrSrc);

    for (;;)
    {
    	Cy_SAR_StartConvert(SAR0, CY_SAR_START_CONVERT_SINGLE_SHOT);
	Cy_SAR_IsEndConversion(SAR0, CY_SAR_WAIT_FOR_RESULT);
	resADC[0] = Cy_SAR_GetResult16(SAR0, 0);
	resADC[1] = Cy_SAR_GetResult16(SAR0, 1);
	printf("ADC Result Channel 0 (-input) = %d mV   \r\n", Cy_SAR_CountsTo_mVolts(SAR0, 0, resADC[0]));
	printf("ADC Result Channel 1 (+input) = %d mV   \r\n", Cy_SAR_CountsTo_mVolts(SAR0, 1, resADC[1]));
	if(flagLPComp)
	{
	    printf("Output PIN LPComp is HIGT\r\n");
	}
	else
	{
	    printf("Output PIN LPComp is LOW \r\n");
	}
	printf("\x1b[1F");
	printf("\x1b[1F");
	printf("\x1b[1F");
	Cy_SysLib_Delay(1000);
    }
}

static void lpcomp_interrupt_handler(void)
{
	if(CY_LPCOMP_CHANNEL0_INTR == Cy_LPComp_GetInterruptStatusMasked(LPCOMP))
	{
	    if(Cy_LPComp_GetCompare(LPCOMP, CY_LPCOMP_CHANNEL_0))
	    {
		flagLPComp = true;
	    }
	    else
	    {
		flagLPComp = false;
	    }
	    Cy_LPComp_ClearInterrupt(LPCOMP, CY_LPCOMP_CHANNEL0_INTR);
	    NVIC_ClearPendingIRQ(lpcomp_interrupt_IRQn);
	}
}
Крутимо потенциометром на позитивному вході компаратора, і спотерігаємо, що як напруга на позитивному вході перевищує напругу на негативному вході, то вихід компаратора встановлює високий рівень і навпаки.
Результат роботи

Відео посібник



вівторок, 25 вересня 2018 р.

MyHomeIoT: Рівень води Water Level шина I2C

MyHomeIoT Water Level

Рівень води MyHomeIoT Water Level призначений для контролю рівня води, та керуванням впускним і випускним клапаном по шині I2C з мікроконтролера. Приєднавши цю плату до Sonoff Basic (TH), або до будь якого пристрою на ESP8266, схемно сумісного з Sonoff basic (TH) та прошитого прошивкою MyHomeIoT починаючи з версії 1.1.4 і вище, до шини I2C, отримуєте контроль рівня води (чотири рівня: пустий, 1/4, 1/2, 3/4 і повний) плюс керування впускним і випускним клапаном зі світлодіодною індикацією і зворотнім зв'язком, як з додатку blynk app, так і з кнопки на самій платі. В додачу два тижневих планувальника для встановлення потрібного рівня води.
Також цей Water Level можна використовувати з будь яким мікроконтролером для своїх поробок автоматики і систем розумного будинку написавши програмну підтримку до свого мікроконтролеру який будете використовувати.

Схема пристрою

Схема рівня води (тицяйте в зображення для збільшення)
Схема складається з мікросхеми PCF8574P, яка є двонаправленим розширювачем портів вводу/виводу з керуванням по I2C шині і має адресу 0x21. Та мікросхеми ULN2803A, яка є масивом транзисторів Дарлінгтона і має 8 транзисторів з загальним емітером та внутрішніми діодами для індуктивних навантажень (реле).
Перші чотири входи IN1 - IN4 мікросхеми ULN2803A використовуються для визначення рівня води. Виходи OUT5 і OUT6 мікросхеми ULN2803A керують впускним і випускним клапаном. А OUT7 керує світлодіодним індикатором режиму роботи. OUT8 не використовується (резерв), тому можна замість мікросхеми ULN2803A застосувати UNL2003.

Мікросхема PCF8574P - двонаправлений розширювач портів вводу/виводу з шиною I2C
Кнопка S1 - встановлення потрібного рівня води.
Світлодіод D2 - для індикації режимів роботи та помилок сенсору.
Реле К1, К2 - керування впускним і випускним клапаном
Транзистор Q1 - захист від переливу води у випадку, якщо мікроконтролер "завис" або не працює з якихось причин. Можна без транзистору, тоді OUT5 мікросхеми ULN2803A з'єднати на пряму з реле К1 (вивід 2 на схемі).

Демонстраційний код


/*
 Name:  pcf8574_water_level.ino
 Created: 7/16/2018 8:59:19 AM
 Author: Andriy
*/
#include <Ticker.h>
#include <Wire.h>
#include <pcf8574_esp.h>


#define ADDRESS_WATER_LEVEL  0x21 // Address PCF8574 on the I2C bus
#define SDA    4 // Pin SDA wire 
#define SCL    5 // Pin SCL wire

typedef enum {
 empty  = 0x00,
 quarter  = 0x40,
 half  = 0x80,
 threeQuarters = 0xC0,
 full  = 0xFE,
 errorSensor = 0xFF
}WaterLevelEnum;

typedef enum {
 sensor_0,
 sensor_1,
 sensor_2,
 sensor_3,
 intake_pump,
 outlet_pump,
 led_indicator,
 button
}WaterLevelPortEnum;

typedef struct
{
 bool triggerInit  = false;   // трігер чи є такий пристрій в системі
 bool triggerButton  = false;   // трігер натискання і відпускання кнопки
 bool triggerStart  = false;   // трігер старту встановлення рівня води 
 bool stateLed   = false;   // стан світлодіодного індікатора увімк/вимкн 
 bool stateRelay[2]  = { false, false }; // стан впускного і випускного реле 
 uint8_t requiredWaterLevel = full;
 uint8_t currentWaterLevel = empty;
}WaterLevelTypeDef;

WaterLevelTypeDef waterLevelStruct;

PCF857x waterLevelDevice(ADDRESS_WATER_LEVEL, &Wire, false);

Ticker tickerWaterLevel;

static void wl_Run(bool setOutletPump);
static uint8_t wl_GetWaterLevel(void);
static void wl_SetWaterLevel(uint8_t level);
static void wl_ReadKey(void);
static void wl_Control(bool setOutletPump);
static void wl_ErrorLedStatus(void);
static void wl_Action(bool state, bool stateIntakePump, bool stateOutletPump, String str);

// the setup function runs once when you press reset or power the board
void setup()
{
 Serial.begin(115200);

 Wire.begin(SDA, SCL);
 // ініціалізуємо рівень води
 waterLevelDevice.begin(0x8F);
 // встановлюємо потрібний рівень
 wl_SetWaterLevel(half);
 // взнаємо поточний рівень води і заносимо до структури
 waterLevelStruct.currentWaterLevel = wl_GetWaterLevel();

 if (waterLevelStruct.currentWaterLevel != errorSensor)
 {
  wl_Action(false, false, false, "Device water level is OK!");

  if (tickerWaterLevel.active())
  {
   tickerWaterLevel.detach();
   waterLevelDevice.write(led_indicator, LOW);
  }
 }
 else
 {
  wl_Action(false, false, false, "Device water level is sensor error!");
  
  if (!tickerWaterLevel.active())
  {
   tickerWaterLevel.attach(0.2, wl_ErrorLedStatus);
  }  
 }
}

// the loop function runs over and over again until power down or reset
void loop() 
{ 
 wl_Run(true);
}

static void wl_Run(bool setOutletPump)
{
 wl_ReadKey();
 wl_Control(setOutletPump);  
}

static void wl_ReadKey(void)
{
 if (!waterLevelDevice.read(button) && !waterLevelStruct.triggerButton)
 {
  waterLevelStruct.triggerButton = true;
  waterLevelStruct.triggerStart = !waterLevelStruct.triggerStart; 
 }
 else if (waterLevelDevice.read(button) && waterLevelStruct.triggerButton)
 {
  waterLevelStruct.triggerButton = false;  
 }
}

static void wl_Control(bool setOutletPump)
{
 if (waterLevelStruct.triggerStart) 
 {  
  waterLevelStruct.currentWaterLevel = wl_GetWaterLevel();

  if (waterLevelStruct.currentWaterLevel == errorSensor)
  {  
   wl_Action(false, false, false, "Error water sensor");
   
   if (!tickerWaterLevel.active())
   {
    tickerWaterLevel.attach(0.2, wl_ErrorLedStatus);
   }   
  }
  else
  {      
   if (tickerWaterLevel.active())
   {
    tickerWaterLevel.detach();
    waterLevelDevice.write(led_indicator, LOW);
   }
   
   if (waterLevelStruct.currentWaterLevel == waterLevelStruct.requiredWaterLevel)
   {    
    wl_Action(false, false, false, "The tank has already reached the required level");    
   }
   else if (waterLevelStruct.currentWaterLevel < waterLevelStruct.requiredWaterLevel)
   {
    if (!waterLevelStruct.stateRelay[0])
    {     
     wl_Action(true, true, false, "Intake Pump ON");
    }
   }
   else if (waterLevelStruct.currentWaterLevel > waterLevelStruct.requiredWaterLevel)
   {
    if (setOutletPump)
    {     
     if (!waterLevelStruct.stateRelay[1])
     {
      wl_Action(true, false, true, "Outlet Pump ON");
     }
    }
   }
  }  
 }
 else
 {
  if (waterLevelStruct.stateRelay[0])
  {   
   wl_Action(false, false, false, "Intake Pump OFF");
  }
  
  if (waterLevelStruct.stateRelay[1])
  {
   wl_Action(false, false, false, "Outlet Pump OFF");
  }
 } 
}

static void wl_Action(bool state, bool stateIntakePump, bool stateOutletPump, String str)
{ 
 waterLevelStruct.triggerStart = state;
 waterLevelStruct.stateLed = state;
 waterLevelStruct.stateRelay[0] = stateIntakePump;
 waterLevelStruct.stateRelay[1] = stateOutletPump; 
 Serial.println(str);
 Serial.print("Current water level is ");
 switch (waterLevelStruct.currentWaterLevel)
 {
 case empty:
  Serial.println("empty");
  break;
 case quarter:
  Serial.println("1/4");
  break;
 case half:
  Serial.println("1/2");
  break;
 case threeQuarters:
  Serial.println("3/4");
  break;
 case full:
  Serial.println("full");
  break;
 case errorSensor:
  Serial.println("error");
  break;
 default:
  break;
 } 

 waterLevelDevice.write(led_indicator, waterLevelStruct.stateLed);
 waterLevelDevice.write(intake_pump, waterLevelStruct.stateRelay[0]);
 waterLevelDevice.write(outlet_pump, waterLevelStruct.stateRelay[1]); 
}

static void wl_ErrorLedStatus(void)
{
 waterLevelDevice.toggle(led_indicator);
}

static uint8_t wl_GetWaterLevel(void)
{ 
 uint8_t level = (waterLevelDevice.read8() & 0xF) ^ 0x0F;
 
 if (level != 0b0000 && level != 0b0001 &&\
  level != 0b0011 && level != 0b0111 && level != 0b1111)
 {
  return errorSensor;
 }
 else
 {
  if (level == 0b0000)
   return empty;
  else if (level == 0b0001)
   return quarter;
  else if (level == 0b0011)
   return half;
  else if (level == 0b0111)
   return threeQuarters;
  else if (level == 0b1111)
   return full;  
 } 
}

static void wl_SetWaterLevel(uint8_t level)
{
 waterLevelStruct.requiredWaterLevel = level;
}

вівторок, 3 жовтня 2017 р.

ESP8266: Вихід з режиму deep-sleep як по RTC так і по зовнішньому перериванню

Передмова

При розробці одного пристрою IoT на ESP8266, виникла необхідність живити цей пристрій від батарейок. Щоб забезпечити довге життя батарейкам потрібно вводити ESP8266 в режим глибокого сну (deep sleep), а прокидатись по зовнішній події, наприклад в моєму випадку це спрацювання сенсору присутності людини "Human detector sensor". Що ж, режим deep-sleep у чипа ESP8266 в наявності, а також в наявності штатні способи виходу зі сну, як по внутрішньому RTC, так і по зовнішньому перериванню. Для пробудження по RTC достатньо з'єднати піни GPIO16 і RST. І тоді, наприклад, по команді:
ESP.deepSleep(10e6);
ESP8266 увійде в режим глибокого сну, а через 10 секунд прокинеться і почне виконувати код з самого початку.
Якщо потрібно пробудження по зовнішньому перериванню то потрібно забезпечити короткий імпульс логічного нуля на виводі RST чипу ESP8266. І тоді відправивши чип у глибокий сон "навічно":
ESP.deepSleep(0);
Можна кнопкою "Reset", яка під'єднана до виводу RST чипу і до GND схеми, вивести з режиму глибокого сну, або замість кнопки буде якийсь датчик чи сенсор, який при спрацюванні буде давати короткий імпульс логічного нуля.
Наче нічого складного. Але є одне але.

Чому не все так просто

Наприклад, в моєму випадку з PIR сенсором:
HC-SR501 Infrared PIR Motion Sensor Module
Цей сенсор, при спрацюванні має на виході стійку логічну одиницю протягом від декількох секунд до декількох хвилин. В залежності від налаштувань сенсора. А нам потрібен короткий імпульс логічного нуля. Бо чип ESP8266 не перезавантажиться поки на сенсорі PIR буде логічна одиниця. Значить потрібна схема не тільки інвертору , яка з логічної одиниці перетворить сигнал на логічний нуль, а ще й сформувати короткий імпульс логічного нуля по фронту що наростає на вході.
Це добре, є такі схеми, але ж хочеться мати і пробудження по внутрішньому RTC (а раптом при подальшій розробці пристрою захочеться розширити функціонал?). Здавалося нічого складного, з'єднав GPIO16 з RST і користуйся. Але так не можна робити, бо в нас крім GPIO16 до RST буде під'єднано, як кнопку "reset" для ручного перезавантаження ESP8266, так і "формувач короткого імпульсу". Потрібно GPIO16 з'єднати з RST через струмообмежувальний резистор близько 470 Ом. Спробувавши з'єднання через резистор, мій ESP8266 так і не зміг прокинутись за допомоги внутрішнього RTC. Додавши між GPIO16 та RST "буферний емітерний повторювач", чип ESP8266 чудово і безвідмовно прокидався через назначений час. Добре, додамо до схеми і емітерний повторювач. Готового рішення в інтернеті я не знайшов, хоч по всіляким форумам люди шукають рішення. Наведені схеми не робочі, або взагалі не мають ніякого логічного сенсу. Тож втілимо своє рішення-схему самі.

Схемна реалізація виходу з режиму DEEP-SLEEP


Коли спрацьовує PIR Sensor на його виході йде перемикання стану з "0" на "1". Конденсатор C1 за короткий час заряджається і на короткий час відкривається транзистор Q1, а також на короткий час відкриється буферний емітерний повторювач на транзисторі Q2. Забезпечивши короткий імпульс логічного нуля на виводі RST чипу ESP8266, саме під час перекидання стану виходу PIR sensor з 0 на 1. Подальша поведінка PIR sensor не впливає на чип поки сенсор не перемикнеться в початковий стан, а для програмного контролю, чи спрацював PIR сенсор, чи вже скинувся в початковий стан можна контролювати на якомусь вільному виводі GPIO чипу ESP8266. В мене на схемі це "D1" для NODE MCU, або "GPIO5" для ESP8266.
Щоб мати можливість прокидатись і по внутрішньому RTC, під'єднаємо на вхід буферного емітерного повторювача вихід "D0" для NODE MCU, або "GPIO16" для ESP8266, через резистор на сотні Ом (в мене під рукою був 330 Ом, але підійде мабуть будь який в межах 200 Ом - 10 кОм).
До виходу схеми під'єднаємо кнопку "Reset", для ручного скидання чипу і сам пін "RST" чипу "ESP8266".
Схема пройшла випробування і реально працює. Радіймо!

Модифікація схеми

При подальшому розвитку проекту, відпала необхідність контролювати стан PIR сенсору мікроконтролером. Плюс необхідно було передбачити неможливість перезавантаження пристрою від PIR сенсору, коли пристрій "пробуджений" і працює. Схему переробив і виконав на мікросхемі дрібної логіки 74HC00 з 4-ма елементами 2І-НІ.
Схема виходу з режиму deep-sleep як від внутрішнього RTC так і від зовнішнього переривання
Коли пристрій "спить" на виході D1 немає ніякого сигналу, але вхід логічного елементу D1.3 (інвертор) підтягнуто до загального дроту і має на вході логічний нуль. Цей нуль інвертується в логічну одиницю на виході і подається на один з входів елементу D1.1. Ця одиниця виконує роль дозволу на перезавантаження пристрою як від PIR сенсору, так і по RTC (короткий імпульс логічного "0" на D0). Коли PIR сенсор спрацює і на його виході буде логічна "1", конденсатор почне заряджатись і на короткий час на іншому вході елементу D1.1 з'явиться "1". І на цей короткий час на виході елементу D1.1 з'явиться логічний "0" і NodeMCU перезавантажиться, та почне виконувати програму. Достатньо на початку програми вихід D1 плати NodeMCU встановити в високий стан (логічна "1") і маємо на виході елементу D1.3 логічний "0", який буде виконувати роль "заборони" перезавантаження поки NodeMCU працює. Тоді незалежно від того який стан на іншому вході елементу D1.1 на його виході буде постійно логічна "1". Щоб працювало пробудження по таймеру, а це короткий логічний "0" на виводі D0 плати NodeMCU, в нашому випадку, треба його інвертувати до короткої логічної "1" елементом D1.2, та через діод в прямому напрямку подати на вхід елементу D1.1, там де вже під'єднано PIR сенсор. Така собі "імітація" спрацювання PIR сенсора. Діод виконує роль елементу АБО. Функцію іншого діода виконує конденсатор. Так що коли NodeMCU "спить", то короткий імпульс логічного "0" на D0, "АБО" перепад з "0" на "1" на PIR сенсорі - пробудить пристрій.