субота, 24 квітня 2021 р.

Встановлення ModusToolbox та перший проект на CY8CKIT-149 PSoC® 4100S Plus Prototyping Kit

Передмова

На фоні різкого здорожчання чипів STM32, та їх дефіциту, на часі розглянути якісь альтернативи. Звісно, тієї ж вагової категорії. Це має бути 32 розрядний MCU, та архітектури ARM. Пропоную до вашої уваги дошку розробника KIT Prototyping CY8CKIT-149 PSoC4 від компанії Cypress + Infineon. Розписувати характеристики і опис дублюючи документацію - не бачу сенсу. Можете самі перейти за наданими ланками і ознайомитись більш детально. 

CY8CKIT-149 PSoC® 4100S Plus Prototyping Kit


Мета цієї статті:

  • показати як встановити засіб розробки Eclipse ModusToolBox IDE
  • створення проекту "Hello, World" для ознайомлення з процесом розробки

Встановлення ModusToolbox

Перейдіть за ланкою, щоб завантажити ModusToolbox, та оберіть вашу операційну систему. ModusToolBox доступний для Windows, Linux, macOS. Ну звісно, це ж Eclips :)
Після завантаження, встановіть ModusToolBox. 

Встановлення без особливостей, достатньо:

  1. тиснути "Next"
  2. погодитись з ліцензійною угодою
  3. залишити шлях встановлення за замовчуванням, або вказати свій шлях (шлях не має містити кирилічні символи)
  4. обрати тип інсталяції "Default installation"
  5. кнопкою "Finish" завершити інсталяцію та запускаємо ModusToolBox
Завершення встановлення

Оберіть свій WorkSpaces, тека де будуть зберігатись ваші проекти, або залиште як є:

Обираємо шлях workspaces

Тиснемо кнопку "Launch" і перейдемо до створення першого проекту "Hello World" надрукуємо текстовий рядок в серіал порт та поблимаємо світлодіодами. А світлодіодів на цій платі розробника дуже багато :)

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

Створення нового проекту
Створення нового проекту:
  1. На панелі швидкого доступу "Quick panel"
  2. Тиснемо "New Application". Запуститься утіліта "Project Creator"
  3. Обираємо свою дошку розробника "CY8CKIT-149" з чипом CY8C4147AZI-S475
  4. Тиснемо кнопку "Next"
Обираємо приклад з репозиторію

    5. Переконаємось чи вірний шлях
    6. Оберемо приклад "Hello World"
    7. Та назвемо його "PSoC4_Hello_World"
    8. Ознайомимось з описом прикладу
    9. Підсумуємок, що створили
    10. Тиснемо кнопку "Create"

Структура проекту

Коли проект створиться, то в "Workspaces" створяться дві теки "mtb_shared" з бібліотеками, які будуть доступні всім проектам в цьому "Workspaces" та тека, власне, з самим проектом. В нашому випадку це "PSoC4_Hello_World":

Структура проекту

  1. mtb_shared - тека з бібліотеками, які доступні будь якому проекту в цьому workspaces
  2. pdl (peripheral driver library) та hal (high abstraction level) бібліотеки
  3. PSoC4_Hello_World - тека з проектом який ми створили
  4. COMPONENT_CUSTOM_DESING_MODUS - тека яка стосується утіліти Device Manager. Про це в наступних статтях.
  5. Файл main.c - тут і будемо дивитись/писати код
  6. Панель швидкого доступу. Ознайомимиось з нею нижче.

Панель швидкого доступу

Для кожного проекту в "workspaces" викликається своя панель швидкого доступу. Де можна  швидко знайти всі необхідні речі для роботи над цим проектом:

Панель швидкого доступу

  1. Побудувати і очистити проект
  2. Запустити відладку або просто залити прошивку за допомоги Jlink або KitProg3
  3. Викликати утіліти, наприклад Library Manager, Device Configurator, тощо
  4. Та швидкий доступ до документації на саму дошку розробника, всіх бібліотек, що дуже зручно

Приклад Hello World

Подвійним кліком на файлі main.c відкриємо файл на редагування, та перш за все, пропоную побудувати проект, натиснувши в панелі швидкого доступу "Quick panel" -> "Build PSoC4_Hello_World Application". Це для того щоб переконатись що з проектом все гаразд і всі файли створились як слід, та щоб всі інклюди, змінні, функції, дефайни і таке інше проіндесувались. Як приклад зібрався без помилок, тиснемо на панелі швидкого доступу "PSoC4_Hello_World program (KitProg3_MiniProg4)". Прошивка заллється до мікроконтролеру і почне працювати. В серіал порт один раз надішлеться текстове повідомлення "Hello World", та почне блимати набортний світлодіод.

Сам файл main.c виглядає так. Він, приклад, побудований на бібліотеці PDL, більш низького рівня чим HAL бібліотека.

/******************************************************************************
* File Name: main.c
*
* Description: This is the source code for the PSoC 4 MCU Hello World Example
*              for ModusToolbox.
*
* Related Document: See README.md
*
*******************************************************************************
* (c) 2020, Cypress Semiconductor Corporation. All rights reserved.
*******************************************************************************
* This software, including source code, documentation and related materials
* ("Software"), is owned by Cypress Semiconductor Corporation or one of its
* subsidiaries ("Cypress") and is protected by and subject to worldwide patent
* protection (United States and foreign), United States copyright laws and
* international treaty provisions. Therefore, you may use this Software only
* as provided in the license agreement accompanying the software package from
* which you obtained this Software ("EULA").
*
* If no EULA applies, Cypress hereby grants you a personal, non-exclusive,
* non-transferable license to copy, modify, and compile the Software source
* code solely for use in connection with Cypress's integrated circuit products.
* Any reproduction, modification, translation, compilation, or representation
* of this Software except as specified above is prohibited without the express
* written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress
* reserves the right to make changes to the Software without notice. Cypress
* does not assume any liability arising out of the application or use of the
* Software or any product or circuit described in the Software. Cypress does
* not authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ("High Risk Product"). By
* including Cypress's product in a High Risk Product, the manufacturer of such
* system or application assumes all risk of such use and in doing so agrees to
* indemnify Cypress against all liability.
*******************************************************************************/


/*******************************************************************************
 * Include header files
 ******************************************************************************/
#include "cy_pdl.h"
#include "cybsp.h"


/*******************************************************************************
* Macros
*******************************************************************************/
#define LED_DELAY_MS              (500u)
#define CY_ASSERT_FAILED          (0u)


/*******************************************************************************
* Function Name: main
********************************************************************************
* Summary:
*  System entrance point. This function performs
*  - initial setup of device
*  - configure the SCB block as UART interface
*  - prints out "Hello World" via UART interface
*  - Blinks an LED under firmware control at 1 Hz
*
* Parameters:
*  none
*
* Return:
*  int
*
*******************************************************************************/
int main(void)
{
    cy_rslt_t result;
    cy_stc_scb_uart_context_t CYBSP_UART_context;

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

    /* Board init failed. Stop program execution */
    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(CY_ASSERT_FAILED);
    }

    /* Configure and enable the UART peripheral */
    Cy_SCB_UART_Init(CYBSP_UART_HW, &CYBSP_UART_config, &CYBSP_UART_context);
    Cy_SCB_UART_Enable(CYBSP_UART_HW);

    /* Enable global interrupts */
    __enable_irq();

    /* Send a string over serial terminal */
        Cy_SCB_UART_PutString(CYBSP_UART_HW, "Hello world\r\n");

    for(;;)
    {
        /* Toggle the user LED state */
        Cy_GPIO_Inv(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN);

        /* Wait for 0.5 seconds */
        Cy_SysLib_Delay(LED_DELAY_MS);
    }
}

/* [] END OF FILE */

Пропоную ознайомитись з задіяними в цьому прикладі функціями та структурами. Як побудоване налаштування пінів і периферії. Спробувати внести в приклад свої зміни. Ознайомитись з результатами, своїх внесків в код, шляхом заливки оновленої прошивки.

Ще я підготував відео в підтримку до цієї статті, де покажу як встановити "ModusToolBox", створю пустий проект, та напишу більш цікавий приклад мікроконтролерного "Hello World", за допомоги, вже, HAL бібліотек.

Відео версія статті





пʼятниця, 8 січня 2021 р.

Запускаємо FreeRTOS на ESP32. Створення задач. Видалення задач. Призупинення виконання задачі і її відновлення. Семафори MUTEX - справжня бійня за периферію

 Передмова


Оволодіння вмінням працювати з FreeRTOS надає великі переваги в проектах на мікроконтролерах. Двох-ядерний  мікроконтролер ESP32 дозволяє з легкістю спробувати цю операційну систему для мікроконтролерів. Складемо макет та напишемо невеличкий приклад коду. Де будемо надсилати дані для різних пристроїв по одному SPI з різних задач, використаємо семафор, створимо декілька задач, призупинимо/відновимо роботу задачі, видалимо задачу з пам'яті.

Приклад

Складіть макет схеми як на малюнку. Створімо новий проект в Platformio, дамо проекту назву "ESP32_HC595_MAX7219_FreeRTOS". Для роботи з драйвером "max7219" завантажте цю бібліотеку, та додайте її до свого проекту, який щойно створили. Тепер можемо додати демонстраційний код:

#include <Arduino.h>
#include <SPI.h>
#include "max7219.h"

#define CS_HC595    5
#define CS_MAX7219  15
#define KEY_PIN     0
#define LED_BLUE    2

#define CS_HC595_SET()  digitalWrite(CS_HC595, LOW)
#define CS_HC595_RESET()  digitalWrite(CS_HC595, HIGH)

void Task0(void* parameters);
void Task1(void* parameters);
void Task2(void* parameters);
void Task3(void* parameters);
void Task4(void* parameters);
void Task5(void* parameters);

bool keyFlag = false;
bool task4Active = true;

TaskHandle_t Task2Handle = NULL;

SemaphoreHandle_t mutexSPI;
SemaphoreHandle_t mutexSerial;

Max7219 max7219 = Max7219(CS_MAX7219, 8, 9);

void setup() 
{
  Serial.begin(115200);
  Serial.println("Serial is OK!");
  SPI.begin(18, 21, 23);
  max7219.Begin();

  mutexSPI = xSemaphoreCreateMutex();
  mutexSerial = xSemaphoreCreateMutex();

  xTaskCreate(Task0, "Task0", 2048, NULL, 1, NULL);
  xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0);
  xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, &Task2Handle, 1);
  xTaskCreate(Task3, "Task3", 2048, NULL, 2, NULL);
  xTaskCreate(Task4, "Task4", 2048, NULL, 1, NULL);
  xTaskCreate(Task5, "Task5", 2048, NULL, 1, NULL);
}

void loop() 
{
  
}

void Task0(void* parameters)
{
  xSemaphoreTake(mutexSerial, portMAX_DELAY);
  Serial.printf("[%8lu] Run Task0 only once: %d\r\n", millis(), xPortGetCoreID());
  xSemaphoreGive(mutexSerial);

  vTaskDelete(NULL);
}

void Task1(void* parameters)
{
  //TODO: код що виконується раз при запускі задачі
  pinMode(CS_HC595, OUTPUT);
  CS_HC595_RESET();
  
  while (1)
  {
    //TODO: код що виконується безкінечно
    for (size_t i = 0; i < 8; i++)
    {
      xSemaphoreTake(mutexSerial, portMAX_DELAY);
      Serial.printf("[%8lu] Run Task1 on Core: %d\r\n", millis(), xPortGetCoreID());
      xSemaphoreGive(mutexSerial);
      
      xSemaphoreTake(mutexSPI, portMAX_DELAY);
      CS_HC595_SET();
      SPI.transfer(1 << i);
      CS_HC595_RESET();
      xSemaphoreGive(mutexSPI);

      vTaskDelay(500 / portTICK_PERIOD_MS);
    }
  }
}

void Task2(void* parameters)
{
  while (1)
  {
    max7219.Clean();
    max7219.DecodeOn();

    xSemaphoreTake(mutexSerial, portMAX_DELAY);
    Serial.printf("[%8lu] Run Task2 on Core: %d\r\n", millis(), xPortGetCoreID());
    xSemaphoreGive(mutexSerial);
    
    xSemaphoreTake(mutexSPI, portMAX_DELAY);
    max7219.PrintNtos(8, millis(), 8);
    xSemaphoreGive(mutexSPI);

    vTaskDelay(973 / portTICK_PERIOD_MS);
  }  
}

void Task3(void* parameters)
{
  pinMode(KEY_PIN, INPUT);

  while (1)
  {
    if(!digitalRead(KEY_PIN) && !keyFlag)
    {
      keyFlag = true;

      xSemaphoreTake(mutexSerial, portMAX_DELAY);
      Serial.printf("[%8lu] KEY is pressed!\r\n", millis());
      xSemaphoreGive(mutexSerial);      
    }

    if (digitalRead(KEY_PIN) && keyFlag)
    {
      keyFlag = false;

      xSemaphoreTake(mutexSerial, portMAX_DELAY);
      Serial.printf("[%8lu] KEY is unpressed!\r\n", millis());
      xSemaphoreGive(mutexSerial);

      if (task4Active)
      {
        task4Active = false;
        vTaskSuspend(Task2Handle);
      }
      else
      {
        task4Active = true;        
        vTaskResume(Task2Handle);
      }           
    }

    vTaskDelay(50 / portTICK_PERIOD_MS);
  }
  
}

void Task4(void* parameters)
{ 
  uint8_t brightness[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 
                           0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 };
  
  while (1)
  {
      for (size_t i = 0; i < sizeof(brightness) / sizeof(brightness[0]); i++)
      {
        xSemaphoreTake(mutexSPI, portMAX_DELAY);
        max7219.SetIntensivity(brightness[i]);
        xSemaphoreGive(mutexSPI);

        vTaskDelay(150 / portTICK_PERIOD_MS); 
      }            
  }  
}

void Task5(void* parameters)
{
  ledcSetup(0, 5000, 8);
  ledcAttachPin(LED_BLUE, 0);

  uint8_t n = 0;
  bool direction = false;

  while (1)
  {  
    if (!direction && n == 255)    
    {
      direction = true;
    }
    
    if (direction && n == 0)
    {
      direction = false;
    }
    
    ledcWrite(0, n);

    if (!direction)
    {
      n++;
    }
    else
    {
      n--;
    }
    
    vTaskDelay(10 / portTICK_PERIOD_MS); 
  }
}

Компілюємо, заливаємо, відкриваємо Serial-monitor, спостерігаємо за роботою.

Файли

Файли прикладів можна завантажити з репозиторію GitHub

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



середа, 16 грудня 2020 р.

Blynk: підключення до точки WiFi та серверу Blynk, очікування WiFi точки, перепідключення до WiFi та серверу Blynk, робота основного коду без блокування при очікування з'єднання для ESP32/ESP8266

Передмова

Помітив, що багато початківців, які хочуть створити для себе IoT пристрій на базі WiFi мікроконтролера ESP8266 або ESP32 з системою Blynk, стикаються з такою проблемою як:

  • не виконання коду, поки пристрій не підключиться до точки WiFi;
  • відсутность спроб відновити з'єднання у разі втрати зв'язку з мережею;
  • блокування коду при втраті зв'язку з сервером Blynk.
Як не вирішити ці проблеми пристрій і залишиться поробкою вихідного дня для спробувати. Якийсь путній і завершений пристрій створити з таким підходом неможливо. Бібліотека Blynk потужна і універсальна бібліотека, до якої входять і інші бібліотеки, такі як робота з таймерами, датою, керування підключеннями, і багато іншого. Але! Без власного, продуманого коду створити завершений пристрій, який можна вже і виводити на ринок, або хоча б, щоб він був зручний для власного користування, для дому, для сім'ї - не побудувати. Так що пропоную все ж таки попрацювати над власним кодом, який працює з мережею, та усуває недолік блокування власного коду у разі втрати з'єднання з сервером Blynk. Та досить балачок, давайте розпочнемо, бо насправді не все так складно.

Проблематика

Ми можемо взяти за основу будь який приклад Blynk і вже навколо нього "наростити" свій функціонал. Але це буде не зовсім вірно. Тому пишемо в першу чергу код, який буде, наш модуль WiFi, завжди тримати на зв'язку з точкою WiFi. Та при втраті підключення до Blynk зробити логіку, яка дозволить не блокувати основний код, та періодично намагатись під'єднатись до серверу Blynk.

Поширений приклад для підключення до WiFi нам пропонують в такому вигляді:

#include <ESP8266WiFi.h>

void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.begin("network-name", "pass-to-network");

  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {}

В цьому прикладі далі циклу while (WiFi.status() != WL_CONNECTED), поки не буде з'єднання з точкою WiFi, програма не виконається. Нам потрібно, щоб спроби приєднатись до точки WiFi були постійними, а код в циклі loop() виконувався та контролював стан з'єднання з точкою WiFi. 

А поширений приклад для підключення до Blynk нам пропонують в такому вигляді:

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "YourAuthToken";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "YourNetworkName";
char pass[] = "YourPassword";

void setup()
{
  // Debug console
  Serial.begin(9600);

  Blynk.begin(auth, ssid, pass);
  // You can also specify server:
  //Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 80);
  //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);
}

void loop()
{
  Blynk.run();
  // You can inject your own code or combine it with other sketches.
  // Check other examples on how to communicate with Blynk. Remember
  // to avoid delay() function!
}

В цьому випадку, при відсутності WiFi ваш пристрій так і буде "мертвою залізякою", а у разі наявності WiFi і втрати зв'язку з сервером Blynk, ваш код блокуватиметься функцією Blynk.run() яка викликається з циклу loop() і користуватись пристроєм буде не можливо. 

Приклад рішення для ESP8266

Я пропоную такий варіант коду для підтримки зв'язку з точкою WiFi та сервером Blynk без блокування основного коду:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "BlynkSimpleEsp8266.h"

const char* ssid        = "MY_SSID";
const char* password    = "MY_PASS";
const char* blynkToken  = "MY_TOKEN";

bool isWiFiConnected = false;
int  numTimerReconnect = 0;

WiFiEventHandler gotIpEventHandler;
WiFiEventHandler disconnectedEventHandler;
BlynkTimer timer;

void WiFiStationConnected(const WiFiEventStationModeGotIP& event);
void WiFiStationDisconnected(const WiFiEventStationModeDisconnected& event);
void ReconnectBlynk(void);
void BlynkRun(void);

void setup() 
{
  Serial.begin(115200);
  
  gotIpEventHandler = WiFi.onStationModeGotIP(&WiFiStationConnected);
  disconnectedEventHandler = WiFi.onStationModeDisconnected(&WiFiStationDisconnected);

  WiFi.begin(ssid, password);

  Blynk.config(blynkToken);

  if(Blynk.connect())
  {
    Serial.printf("[%8lu] setup: Blynk connected\r\n", millis());
  }
  else
  {
    Serial.printf("[%8lu] setup: Blynk no connected\r\n", millis());
  }
  
  Serial.printf("[%8lu] Setup: Start timer reconnected\r\n", millis());
  numTimerReconnect = timer.setInterval(60000, ReconnectBlynk);
}

void loop() 
{
  BlynkRun();  
  timer.run();
}

void WiFiStationConnected(const WiFiEventStationModeGotIP& event)
{
  isWiFiConnected = true;
  Serial.printf("[%8lu] Interrupt: Connected to AP, Ip: ", millis());
  Serial.println(WiFi.localIP());
}

void WiFiStationDisconnected(const WiFiEventStationModeDisconnected& event)
{
  isWiFiConnected = false;
  Serial.printf("[%8lu] Interrupt: Disconnected to AP!\r\n", millis());
}

void BlynkRun(void)
{
  if (isWiFiConnected)
  {
    if(Blynk.connected())
    {
      if (timer.isEnabled(numTimerReconnect))
      {
        timer.disable(numTimerReconnect);
        Serial.printf("[%8lu] BlynkRun: Stop timer reconnected\r\n", millis());
      }

      Blynk.run();
    }
    else
    {
      if (!timer.isEnabled(numTimerReconnect))
      {
        timer.enable(numTimerReconnect);
        Serial.printf("[%8lu] BlynkRun: Start timer reconnected\r\n", millis());
      }      
    }
  }
}

void ReconnectBlynk(void)
{
  if (!Blynk.connected())
  {
    if (Blynk.connect())
    {				        
      Serial.printf("[%8lu] ReconnectBlynk: Blynk reconnected\r\n", millis());
    }
    else
    {
      Serial.printf("[%8lu] ReconnectBlynk: Blynk not reconnected\r\n", millis());
    }
  }
  else
  {
    Serial.printf("[%8lu] ReconnectBlynk: Blynk connected\r\n", millis());
  }
}


Приклад рішення для ESP32

Приклад для ESP32 дещо відрізняється з попереднім для ESP8266, але логіка роботи залишається такою самою:

#include <Arduino.h>
#include <WiFi.h>
#include "BlynkSimpleEsp32.h"

const char* ssid        =  "MY_SSID";
const char* password    =  "MY_PASS";
const char* blynkToken  =  "MY_TOKEN";

bool isWiFiConnected = false;
int  numTimerReconnect = 0;

BlynkTimer timer;

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info);
void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
void ReconnectBlynk(void);
void BlynkRun(void);

void setup() 
{
  Serial.begin(115200);

  WiFi.onEvent(WiFiStationConnected, SYSTEM_EVENT_STA_GOT_IP);
  WiFi.onEvent(WiFiStationDisconnected, SYSTEM_EVENT_STA_DISCONNECTED);
  WiFi.begin(ssid, password);

  Blynk.config(blynkToken);

  if(Blynk.connect())
  {
    Serial.printf("[%8lu] setup: Blynk connected\r\n", millis());
  }
  else
  {
    Serial.printf("[%8lu] setup: Blynk no connected\r\n", millis());
  }

  Serial.printf("[%8lu] Setup: Start timer reconnected\r\n", millis());
  numTimerReconnect = timer.setInterval(60000, ReconnectBlynk);
}

void loop() 
{
  BlynkRun();
  timer.run();
}

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info)
{
  isWiFiConnected = true;
  Serial.printf("[%8lu] Interrupt: Connected to AP, IP: ", millis());
  Serial.println(WiFi.localIP());
}

void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info)
{
  isWiFiConnected = false;
  Serial.printf("[%8lu] Interrupt: Disconnected to AP!\r\n", millis());
}

void ReconnectBlynk(void)
{
  if (!Blynk.connected())
  {
    if (Blynk.connect())
    {				        
      Serial.printf("[%8lu] ReconnectBlynk: Blynk reconnected\r\n", millis());
    }
    else
    {
      Serial.printf("[%8lu] ReconnectBlynk: Blynk not reconnected\r\n", millis());
    }
  }
  else
  {
    Serial.printf("[%8lu] ReconnectBlynk: Blynk connected\r\n", millis());
  }
}

void BlynkRun(void)
{
  if (isWiFiConnected)
  {
    if(Blynk.connected())
    {
      if (timer.isEnabled(numTimerReconnect))
      {
        timer.disable(numTimerReconnect);
        Serial.printf("[%8lu] BlynkRun: Stop timer reconnected\r\n", millis());
      }

      Blynk.run();
    }
    else
    {
      if (!timer.isEnabled(numTimerReconnect))
      {
        timer.enable(numTimerReconnect);
        Serial.printf("[%8lu] BlynkRun: Start timer reconnected\r\n", millis());
      }      
    }
  }
}

Розумію, що для початківця це доволі складний код і потребує детальних пояснень. Тому  я підготував відео з поясненням і демонстрацією роботи з кнопкою, світлодіодом і таймером. А також демонстраційний код доступний і для завантаження і для ESP8266 , і для ESP32. Приємного перегляду.

Відео з поясненням і демонстрацією



середа, 25 листопада 2020 р.

Бібліотека драйвера MAX7219 в Arduino framework

 Передмова

Бібліотека для роботи з семисегментним-восьмирозрядним модулем з драйвером на мікросхемі max7219. Дозволяє друкувати окремі цифри-символи в окремих розрядах, друкувати цілі числа, цілі числа певної ширини, числа з плаваючою комою, засвічувати окремі сегменти. Бібліотека написана в межах Arduino framework, тому має працювати на будь якому чипові який підтримує Arduino framework (але це не точно).
Модуль 7 сегментного 8 розрядного дисплею на max7219

Приклад використання

Завантажуєте бібліотеку з GitHub репозиторію. Додаєте до свого проекту файли бібліотеки. Вставляєте до головного файлу вашого проекту цей приклад:

#include <Arduino.h>
#include <SPI.h>
#include "max7219.h"

// 15 - GPIO15 (CS), 8 - number of digits, 5 - intensivity (brightness)
Max7219 max7219 = Max7219(15, 8, 5);

/* ESP32 SPI
  SCK   - GPIO_18
  MISO  - GPIO_21
  MOSI  - GPIO_23
*/

void setup() 
{
  SPI.begin(18, 21, 23);
  max7219.Begin();
}

void loop() 
{
  max7219.DecodeOn();
  max7219.SetIntensivity(5);
  
  max7219.PrintNtos(max7219.DIGIT_7, 1234, 6);
  delay(1000);
  max7219.Clean();

  max7219.PrintItos(max7219.DIGIT_4, 5678);
  delay(1000);
  max7219.Clean();

  max7219.PrintFtos(max7219.DIGIT_8, -45.678f, 2);
  delay(1000);
  max7219.Clean();

  for (uint8_t i = 0; i < 8; i++)
  {
    max7219.PrintDigit(i + 1, i + 1, false);
    delay(100);
  }

  for (uint8_t i = 0x0F; i > 0x00; i--)
  {
    max7219.SetIntensivity(i);
    delay(100);
  }
  
  for (uint8_t i = 0; i < 0x0F; i++)
  {
    max7219.SetIntensivity(i);
    delay(100);
  }
  
  for (uint8_t i = 8; i > 0 ; i--)
  {
    max7219.PrintItos(i, 87654321);
    delay(200);
    max7219.Clean();  
  }
  
  max7219.Clean();
  delay(1000);
}

Відео демострація



понеділок, 5 жовтня 2020 р.

Прошивка ESP32 власною firmware за допомоги Flash Download Tools

Передмова

Коли ви створили власну прошивку для ESP32, наприклад в Platformio, чи Arduino IDE. І вам потрібно передати бінарний код для прошивки пристроїв ESP32 іншим людям, не розголошуючи сирцевий код, або прошивати серію власних пристроїв. То IDE не підходить для цих цілей. Краще і правильно користуватись фірмовою утилітою "Flash Download Tools". Як це зробити, читаємо далі в статті.

Завантаження і встановлення

Щоб завантажити утиліту, перейдіть за цією ланкою

Завантажити FLASH DOWNLOAD TOOL

Теку з утилітою потрібно витягнути з архіву і розмістити в зручне для вас місце на диску ПК. Але зауважу, шлях до утиліти не має містити кириличних літер. Лише латиницею.

Запуск утиліти:

Запуск утиліти

Обирайте "Developer Mode":

Оберіть "Developer Mode"

Оберіть тип чипу, в мене ESP32:

Вибір чипу

Тепер з'явиться головне вікно прошивальщика:

Додаємо файли і налаштування

Треба додати 4 файли і призначити адреси розташування в пам'яті модуля:

  • 0x1000 bootloader_dio_40m.bin
  • 0x8000 partitions.bin
  • 0xe000 boot_app0.bin
  • 0x10000 firmware.bin
Крім власного бінарного файла прошивки, що ми зкомпілювали firmware.bin нам протрібні ще три файли:
  • bootloader file
  • partition table file
  • firmware/app file
І де їх взяти?

Arduino IDE

Підготовка файлів для прошивання в Arduino IDE:
Підготовка файлів
Після компіляції створіть окрему папку для всіх чотирьох файлів де вам зручно. Перший файл "firmware.bin" буде розташований в теці скетча. Можна перейти з меню Arduino IDE "Скетч -> Показати теку скетчів". Або перейти за шляхом: "C:\Users\{user}\Documents\Arduino\{назва_вашого_скетчу}. Щоб добути інші файли, в файловому провідникові, потрібно дозволити перегляд прихованих тек. І так, розташування всіх файлів:
C:\Users\"Користувач"\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\partitions\boot_app0.bin
C:\Users\"Користувач"\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\sdk\bin\bootloader_dio_40m.bin
C:\Users\"Користувач"\AppData\Local\Temp\arduino_build_491506м'я_вашого_скетчу.ino.bin
C:\Users\"Користувач"\AppData\Local\Temp\arduino_build_491506м'я_вашого_скетчу.ino.partitions.bin
Кладемо всі ці чотири файли до теки, яку вже заздалегідь приготували і вказуємо їх для "FLASH DOWNLOAD TOOL". Або архівуємо теку і відправляємо третій стороні для прошивання.

Адреси для цих файлів для "FLASH DOWNLOAD TOOL", як вже було зазначено вище, мають бути такі:
  • 0x1000 bootloader_dio_40m.bin
  • 0x8000 Ім'я_вашого_скетчу.ino.partitions.bin
  • 0xe000 boot_app0.bin
  • 0x10000 Ім'я_вашого_скетчу.ino.bin

Platformio

Спершу підготуємо потрібні файли. В терміналі вашого проекту виконайте цей рядок:
pio run -v -t upload

Підготовка файлів


Після роботи компілятора і прошивача створяться всі потрібні файли за таким шляхом:
0x1000 C:\Users\"Ім'я_користувача"\.platformio\packages\framework-arduinoespressif32\tools\sdk\bin\bootloader_dio_40m.bin
0xe000 C:\Users\"Ім'я_користувача"\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin
0x8000 .pio\build\esp32dev\partitions.bin
0x10000 .pio\build\esp32dev\firmware.bin 
В файловому провідникові, потрібно дозволити перегляд прихованих тек. Файл partitions.bin та firmware.bin знаходяться в прихованій теці .pio вашого поточного проекту.

Кладемо всі ці чотири файли до теки, яку вже заздалегідь приготували і вказуємо їх для "FLASH DOWNLOAD TOOL". Або архівуємо теку і відправляємо третій стороні для прошивання.

Адреси для цих файлів для "FLASH DOWNLOAD TOOL", як вже було зазначено вище, мають бути такі:
  • 0x1000 bootloader_dio_40m.bin
  • 0x8000 partitions.bin
  • 0xe000 boot_app0.bin
  • 0x10000 firmware.bin

VisualMicro for MS Visual Studio

Хто використовує розширення ARDUINO IDE FOR VISUAL STUDIO, то добути потрібні файли можна за такими шляхами, як шлях до вашого проекту і тека "Debug" або "Release", де будуть два файли "назва_проекту.bin" та "назва_проекту.partitions.bin", а ще два файли беруться там де і для Arduino IDE файли bootloader_dio_40m.bin та boot_app0.bin. Наприклад для проекту "blink1":
C:\Users\"Ім'я_користувача"\source\repos\Blink1\Blink1\Release\Blink1.ino.bin
C:\Users\"Ім'я_користувача"\source\repos\Blink1\Blink1\Release\Blink1.partitions.bin
C:\Users\"Ім'я_користувача"\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\partitions\boot_app0.bin
C:\Users\"Ім'я_користувача"\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\tools\sdk\bin\bootloader_dio_40m.bin
Кладемо всі ці чотири файли до теки, яку вже заздалегідь приготували і вказуємо їх для "FLASH DOWNLOAD TOOL". Або архівуємо теку і відправляємо третій стороні для прошивання.

Адреси для цих файлів для "FLASH DOWNLOAD TOOL", як вже було зазначено вище, мають бути такі:
  • 0x1000 bootloader_dio_40m.bin
  • 0x8000 Ім'я_вашого_проекту.ino.partitions.bin
  • 0xe000 boot_app0.bin
  • 0x10000 Ім'я_вашого_проекту.ino.bin

четвер, 19 вересня 2019 р.

Blynk: шаблон-конструктор для створення прошивок з WiFi Manager, WebOTA та автоматичним перепідключенням до вашого WiFi і серверу Blynk

Передмова

Для зручності створив початковий шаблон-конструктор прошивки для системи Blynk з початковими налаштуваннями по WiFi зі смартфону (SSID, PASS, TOKEN, Name Device). З можливістю оновлювати прошивку по повітрю WebOTA, та перепідключенням до мережі WiFi і серверу Blynk у разі пропадання зв'язку. Плюс, якщо зв'язок буде втрачено, функціонал самого пристрою не буде "гальмувати", а буде працювати в режимі OFFLINE як слід, до наступного підключення до серверу. Є обробка натискання системної кнопки "FLASH" яка під'єднана до GPIO_0 і зазвичай присутня на всіх платах на базі "ESP8266" і пристроїв "IoT", наприклад, дуже популярних "Sonoff". Кнопка розпізнає довжину натискання. Коротке натискання - для свого коду, який буде щось вмикати/вимикати. Довге натискання від 5 до 10 секунд - перезавантаження пристрою. Довге натискання понад 10 секунд - скидання всіх налаштувань.

Код шаблону

Код шаблону можна завантажити на GitHub, Dropbox,  або скопіювати прямо звідси:

/*
 Name:  Blynk_Table_Menu.ino
 Created: 9/19/2019 9:04:46 AM
 Author: Andriy Honcharenko
*/

/* CODE BEGIN Includes */
#include <BlynkSimpleEsp8266.h>
#include <Ticker.h>
#include <EEPROM.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <WiFiManager.h>
/* CODE END Includes */

/* CODE BEGIN UD */
/* User defines ---------------------------------------------------------*/
#define BLYNK_PRINT Serial

#define NAME_DEVICE      "MyHomeIoT-ESP8266"

#define BUTTON_SYS0_PIN     0
#define LED_SYS_PIN      13

#define BUTTON_SYS_B0_VPIN    V20
#define WIFI_SIGNAL_VPIN    V80

#define INTERVAL_PRESSED_RESET_ESP  3000L
#define INTERVAL_PRESSED_RESET_SETTINGS 5000L
#define INTERVAL_PRESSED_SHORT   50
#define INTERVAL_SEND_DATA    30033L
#define INTERVAL_RECONNECT    60407L
#define INTERVAL_REFRESH_DATA   4065L
#define WIFI_MANAGER_TIMEOUT   180

#define EEPROM_SETTINGS_SIZE   512
#define EEPROM_START_SETTING_WM   0
#define EEPROM_SALT_WM     12661

#define LED_SYS_TOGGLE()    digitalWrite(LED_SYS_PIN, !digitalRead(LED_SYS_PIN))
#define LED_SYS_ON()     digitalWrite(LED_SYS_PIN, LOW)
#define LED_SYS_OFF()     digitalWrite(LED_SYS_PIN, HIGH)
/* CODE END UD */

/* CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
bool shouldSaveConfigWM  = false; //flag for saving data
bool btnSystemState0  = false;
bool triggerBlynkConnect = false;
bool isFirstConnect   = true; // Keep this flag not to re-sync on every reconnection

int startPressBtn = 0;

//structure for initial settings. It now takes 116 bytes
typedef struct {
 char  host[33] = NAME_DEVICE;    // 33 + '\0' = 34 bytes
 char  blynkToken[33] = "";     // 33 + '\0' = 34 bytes
 char  blynkServer[33] = "blynk-cloud.com"; // 33 + '\0' = 34 bytes
 char  blynkPort[6] = "8442";    // 04 + '\0' = 05 bytes
 int   salt = EEPROM_SALT_WM;    // 04   = 04 bytes
} WMSettings;         // 111 + 1  = 112 bytes (112 this is a score of 0)
//-----------------------------------------------------------------------------------------

WMSettings wmSettings;

BlynkTimer timer;

Ticker tickerESP8266;

//Declaration OTA WebUpdater
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;
/* CODE END PV */

/* CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
static void configModeCallback(WiFiManager* myWiFiManager);
static void saveConfigCallback(void);
static void tick(void);
static void untick(void);
static void readSystemKey(void);
static void timerRefreshData(void);
static void timerSendServer(void);
static void timerReconnect(void);
/* CODE END PFP */

// the setup function runs once when you press reset or power the board

void setup() 
{
 Serial.begin(115200);

 pinMode(BUTTON_SYS0_PIN, INPUT_PULLUP);
 pinMode(LED_SYS_PIN, OUTPUT);

 // Read the WM settings data from EEPROM to RAM
 EEPROM.begin(EEPROM_SETTINGS_SIZE);
 EEPROM.get(EEPROM_START_SETTING_WM, wmSettings);
 EEPROM.end();

 if (wmSettings.salt != EEPROM_SALT_WM) 
 {
  Serial.println(F("Invalid wmSettings in EEPROM, trying with defaults"));
  WMSettings defaults;
  wmSettings = defaults;
 }

 // Print old values to the terminal
 Serial.println(wmSettings.host);
 Serial.println(wmSettings.blynkToken);
 Serial.println(wmSettings.blynkServer);
 Serial.println(wmSettings.blynkPort);

 tickerESP8266.attach(0.5, tick);   // start ticker with 0.5 because we start in AP mode and try to connect

 //Local intialization. Once its business is done, there is no need to keep it around
 WiFiManager wifiManager;

 //reset saved wmSettings
 //wifiManager.resetSettings();

 //set minimu quality of signal so it ignores AP's under that quality
 //defaults to 8%
 //wifiManager.setMinimumSignalQuality();

 //sets timeout before webserver loop ends and exits even if there has been no setup.
 //useful for devices that failed to connect at some point and got stuck in a webserver loop
 //in seconds setConfigPortalTimeout is a new name for setTimeout
 wifiManager.setConfigPortalTimeout(WIFI_MANAGER_TIMEOUT);

 // The extra parameters to be configured (can be either global or just in the setup)
 // After connecting, parameter.getValue() will get you the configured value
 // id/name placeholder/prompt default length
 WiFiManagerParameter custom_device_name_text("<br/>Enter name of the device<br/>or leave it as it is<br/>");
 wifiManager.addParameter(&custom_device_name_text);

 WiFiManagerParameter custom_device_name("device-name", "device name", wmSettings.host, 33);
 wifiManager.addParameter(&custom_device_name);

 WiFiManagerParameter custom_blynk_text("<br/>Blynk config.<br/>");
 wifiManager.addParameter(&custom_blynk_text);

 WiFiManagerParameter custom_blynk_token("blynk-token", "blynk token", wmSettings.blynkToken, 33);
 wifiManager.addParameter(&custom_blynk_token);

 WiFiManagerParameter custom_blynk_server("blynk-server", "blynk server", wmSettings.blynkServer, 33);
 wifiManager.addParameter(&custom_blynk_server);

 WiFiManagerParameter custom_blynk_port("blynk-port", "port", wmSettings.blynkPort, 6);
 wifiManager.addParameter(&custom_blynk_port);

 //set config save notify callback
 wifiManager.setSaveConfigCallback(saveConfigCallback);

 //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
 wifiManager.setAPCallback(configModeCallback);

 //set custom ip for portal
 //wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0));

 //fetches ssid and pass from eeprom and tries to connect
 //if it does not connect it starts an access point with the specified name
 //here  "AutoConnectAP"
 //and goes into a blocking loop awaiting configuration
 //wifiManager.autoConnect();
 //or use this for auto generated name ESP + ChipID

 if (wifiManager.autoConnect(wmSettings.host))
 {
  //if you get here you have connected to the WiFi
  Serial.println(F("Connected WiFi!"));
 }
 else
 {
  Serial.println(F("failed to connect and hit timeout"));
 }

 untick(); // cancel the flashing LED

 // Copy the entered values to the structure
 strcpy(wmSettings.host, custom_device_name.getValue());
 strcpy(wmSettings.blynkToken, custom_blynk_token.getValue());
 strcpy(wmSettings.blynkServer, custom_blynk_server.getValue());
 strcpy(wmSettings.blynkPort, custom_blynk_port.getValue());

 // Print new values to the terminal
 Serial.println(wmSettings.host);
 Serial.println(wmSettings.blynkToken);
 Serial.println(wmSettings.blynkServer);
 Serial.println(wmSettings.blynkPort);

 if (shouldSaveConfigWM)
 {
  LED_SYS_ON();
  // Write the input to the EEPROM
  EEPROM.begin(EEPROM_SETTINGS_SIZE);
  EEPROM.put(EEPROM_START_SETTING_WM, wmSettings);
  EEPROM.end();
  //---------------------------------
  LED_SYS_OFF();
 }

 // Run OTA WebUpdater
 MDNS.begin(wmSettings.host);
 httpUpdater.setup(&httpServer);
 httpServer.begin();
 MDNS.addService("http", "tcp", 80);
 Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", wmSettings.host);

 // Configure connection to blynk server
 Blynk.config(wmSettings.blynkToken, wmSettings.blynkServer, atoi(wmSettings.blynkPort));

 if (Blynk.connect())
 {
  //TODO: something to do if connected
 }
 else
 {
  //TODO: something to do if you failed to connect
 }

 timer.setInterval(INTERVAL_REFRESH_DATA, timerRefreshData);
 timer.setInterval(INTERVAL_SEND_DATA, timerSendServer);
 timer.setInterval(INTERVAL_RECONNECT, timerReconnect); 
}

// the loop function runs over and over again until power down or reset
void loop() 
{
 if (Blynk.connected())
 {
  Blynk.run(); // Initiates Blynk Server  
 }
 else
 {
  if (!tickerESP8266.active())
  {
   tickerESP8266.attach(2, tick);
  }
 }

 timer.run(); // Initiates BlynkTimer
 
 httpServer.handleClient(); // Initiates OTA WebUpdater  

 readSystemKey();
}

/* BLYNK CODE BEGIN */
BLYNK_CONNECTED()
{
 untick(); 

 Serial.println(F("Blynk Connected!"));
 Serial.println(F("local ip"));
 Serial.println(WiFi.localIP());  

 char str[32];
 sprintf_P(str, PSTR("%s Online!"), wmSettings.host); 
 Blynk.notify(str);

 if (isFirstConnect)
 {
  Blynk.syncAll();
  isFirstConnect = false;
 }
}

BLYNK_WRITE(BUTTON_SYS_B0_VPIN) // Example
{ 
 //TODO: something to do when a button is clicked in the Blynk app
 
 Serial.println(F("System_0 button pressed is App!")); 
}
/* BLYNK CODE END */

/* CODE BEGIN USER FUNCTION */
static void timerRefreshData(void)
{
 //TODO: here are functions for updating data from sensors, ADC, etc ...
}

static void timerSendServer(void)
{
 if (Blynk.connected())
 {
  //TODO: here are the functions that send data to the Blynk server
  Blynk.virtualWrite(WIFI_SIGNAL_VPIN, map(WiFi.RSSI(), -105, -40, 0, 100)); //Example send level WiFi signal
 }
 else
 {
  //TODO:
 }
}

static void timerReconnect(void)
{
 if (WiFi.status() != WL_CONNECTED)
 {
  Serial.println(F("WiFi not connected"));

  if (WiFi.begin() == WL_CONNECTED)
  {
   Serial.println(F("WiFi reconnected"));
  }
  else
  {
   Serial.println(F("WiFi not reconnected"));
  }
 }
 else// if (WiFi.status() == WL_CONNECTED)
 {
  Serial.println(F("WiFi in connected"));

  if (!Blynk.connected())
  {
   if (Blynk.connect())
   {
    Serial.println(F("Blynk reconnected"));
   }
   else
   {
    Serial.println(F("Blynk not reconnected"));
   }
  }
  else
  {
   Serial.println(F("Blynk in connected"));
  }
 }
}

static void configModeCallback(WiFiManager* myWiFiManager)
{
 Serial.println(F("Entered config mode"));
 Serial.println(WiFi.softAPIP());
 //if you used auto generated SSID, print it
 Serial.println(myWiFiManager->getConfigPortalSSID());
 //entered config mode, make led toggle faster
 tickerESP8266.attach(0.2, tick);
}

//callback notifying us of the need to save config
static void saveConfigCallback()
{
 Serial.println(F("Should save config"));
 shouldSaveConfigWM = true;
}

static void tick(void)
{
 //toggle state  
 LED_SYS_TOGGLE();     // set pin to the opposite state
}

static void untick(void)
{
 tickerESP8266.detach();
 LED_SYS_OFF(); //keep LED off 
 
}

static void readSystemKey(void)
{
 if (!digitalRead(BUTTON_SYS0_PIN) && !btnSystemState0)
 {
  btnSystemState0 = true;
  startPressBtn = millis();
 }
 else if (digitalRead(BUTTON_SYS0_PIN) && btnSystemState0)
 {
  btnSystemState0 = false;
  int pressTime = millis() - startPressBtn;

  if (pressTime > INTERVAL_PRESSED_RESET_ESP && pressTime < INTERVAL_PRESSED_RESET_SETTINGS)
  {
   if (Blynk.connected())
   {    
    Blynk.notify(String(wmSettings.host) + F(" reboot!"));
   }
   
   Blynk.disconnect();  
   tickerESP8266.attach(0.1, tick);   
   delay(2000);
   ESP.restart();
  }
  else if (pressTime > INTERVAL_PRESSED_RESET_SETTINGS)
  {
   if (Blynk.connected())
   {        
    Blynk.notify(String(wmSettings.host) + F(" setting reset! Connected WiFi AP this device!"));
   }   

   WMSettings defaults;
   wmSettings = defaults;

   LED_SYS_ON();
   // We write the default data to EEPROM
   EEPROM.begin(EEPROM_SETTINGS_SIZE);
   EEPROM.put(EEPROM_START_SETTING_WM, wmSettings);
   EEPROM.end();
   //------------------------------------------
   LED_SYS_OFF();

   delay(1000);
   WiFi.disconnect();
   delay(1000);
   ESP.restart();
  }
  else if (pressTime < INTERVAL_PRESSED_RESET_ESP && pressTime > INTERVAL_PRESSED_SHORT)
  {   
   Serial.println(F("System button_0 pressed is Device!"));
   // TODO: insert here what will happen when you press the ON / OFF button   
  }
  else if (pressTime < INTERVAL_PRESSED_SHORT)
  {   
   Serial.printf("Fixed false triggering %ims", pressTime);
   Serial.println();
  }  
 }
}
/* CODE END USER FUNCTION */

Можливості

  • При першому, після прошивання, увімкнені пристроєм утворюється точка WiFi з ім'ям "MyHomeIoT-ESP8266". Підключиться до цієї точки своїм смартфоном і відкрийте браузер та перейдіть за адресою 192.168.4.1 до налаштувань;
  • Для прикладу і перевірки на віртуальну шпильку V80 відправляються дані рівня сигналу WiFi. Додайте якийсь віджет "value" і назначте йому віртуальну шпильку V80;
  • Кнопка, яка підключена до GPIO_0 обробляється шаблоном та може реагувати на коротке натискання - вставляєте свій код для увімкнення/вимкнення чогось, або інших дій. Довге натискання кнопки від 5 до 10 секунд - перезавантаження пристрою. Довге натискання понад 10 секунд - скидання всіх налаштувань;
  • Для прикладу є обробка віртуальної кнопки V20. Якщо в BlynkApp додати до проекту кнопку на віртуальну шпильку V20, то пристрій в термінал буде надсилати повідомлення про те що кнопку натиснули з додатку Blynk. Інший код-функціонал на ваш розсуд;
  • Є можливість оновити прошивку по повітрю - WebOTA. Для цього потрібно знати IP пристрою і з будь якого браузера який знаходиться в тій же ж самій мережі набрати "IP_Your_Drvice/update". Наприклад ваш пристрій має IP - 192.168.100.111, тоді в браузер вводимо "192.168.100.111/update", обираємо файл прошивки і тиснемо кнопку "update". 
  • При втраті WiFi мережі, або зв'язку з сервером Blynk, функціонал пристрою не буде "гальмувати" і зв'язок автоматично відновиться при першій можливості;
  • Присутні в шаблоні три таймери для збору даних з датчиків (вставляєте свій код), для надсилання даних на сервер (вставляєте свій код. Є приклад з рівнем сигналу WiFi), та таймер для перепідключення до WiFi і Blynk;
  • Якщо до пристрою GPIO_13 підключити світлодіод, це додасть інформативності. Часте блимання - створена точка WiFi, повільне блимання - йде підключення до Wi-Fi і серверу Blynk, блимання раз на 2 секунди - немає підключення до серверу, або мережі.