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

четвер, 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 секунди - немає підключення до серверу, або мережі.

вівторок, 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;
}

четвер, 31 травня 2018 р.

MyHomeIoT: Розширювач портів Expander 4x4 шина I2C

MyHomeIoT Expander 4x4

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

Схема розширювача

Принципова схема розширювача MyHomeIoT Expander 4x4
Схема складається з мікросхеми PCF8574P, яка є двонаправленим розширювачем портів вводу/виводу з керуванням по I2C шині і має адресу 0x20. Та мікросхеми ULN2803A, яка є масивом транзисторів Дарлінгтона і має 8 транзисторів з загальним емітером та внутрішніми діодами для індуктивних навантажень (реле). 

Мікросхема U1 (стабілізатор напруги) може бути як на 3.3 вольта, так і на 5 вольт. В залежності від ваших потреб.

Якщо поставити перемичку на J2 то мікросхема U2 PCF8574 буде живитись від свого внутрішнього живлення, а також ці 3.3 вольта, або 5 вольт буде подано на роз'єм J5 і можна буде живити плату з мікроконтролером. 

Якщо перемичку не ставити, то для мікросхеми U2 PCF8574 живлення 3.3 вольта, або 5 вольт очікується з плати мікроконтролеру. І в такому разі стабілізатор напруги на мікросхемі U1 можна не встановлювати. 

Комутувати навантаження на контактах реле можна до 5 ампер. Якщо потрібно комутувати навантаження з більшим струмом, потрібно запаяти на доріжки додаткові дроти.

Печатна плата

Печатна плата з елементами
Плата вид зверху
Плата вид з низу
Плата в зборі
Добре по розміру підходить така коробочка:
107*87*59mm Plastic Electronics Box Project Case DIN Rail PLC Junction Box DIY
Замовити можна, наприклад, тут, або тут, або ще деінде. Отвори в платі під кріплення в цій коробочці прийдеться висвердлити самому.

Варіанти підключення

Плата розширювача з Sonoff basic

Підключення розширювача до Sonoff Basic

Плата розширювача з NodeMCU та іншими ESP8266

Підключення розширювача до NodeMCU

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

Хто хоче використовувати цей розширювач портів в своїх розробках, то ось для прикладу демонстраційний код для системи Blynk:

/*
 Name:  Blynk_pcf8574_demo.ino
 Created: 7/11/2018 10:17:12 AM
 Author: Andriy Honcharenko
*/

/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial

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

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

#define BUTTON_1   V1 // Expander I/O 4x4 relay1
#define BUTTON_2   V2 // Expander I/O 4x4 relay2
#define BUTTON_3   V3 // Expander I/O 4x4 relay3
#define BUTTON_4   V4 // Expander I/O 4x4 relay4

// Macros to work with bits
#define BIT_SET(reg, bit) reg |= 1 << bit
#define BIT_RESET(reg, bit) reg &= ~(1 << bit)
#define BIT_TOGGLE(reg, bit) reg ^= 1 << bit
#define BIT_IS_SET(reg, bit) (reg & (1 << bit)) != 0
#define BIT_IS_RESET(reg, bit) (reg & (1 << bit)) == 0
//--------------------------

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

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

//----------------------------------
//PCF8574
//----------------------------------
typedef struct {  
 bool triggerButton[4] = { false, false, false, false };
 bool stateButton[4] = { false, false, false, false };
}PCF8574TypeDef;

PCF8574TypeDef pcf8574Struct;

PCF857x pcf8574(ADDRESS_PCF8574, &Wire, false);

// Prototypes of functions
static void readExpanderKey(void);  // Read the state of the buttons
static void writeStateExpanderRelay(void); // Write the state of the relay
//-----------------------------------

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

 Wire.begin(SDA, SCL);
 
 pcf8574.begin(0xF0);

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

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!

 // Read the state of the buttons on the PCF8574
 readExpanderKey();
}

BLYNK_CONNECTED()
{
 Blynk.notify("Demo PCF8574 online!");
 
 Blynk.syncAll();
}

BLYNK_WRITE(BUTTON_1)
{
 pcf8574Struct.stateButton[0] = param.asInt(); 
 //Let's record the state of the relay on PCF8574
 writeStateExpanderRelay();
}

BLYNK_WRITE(BUTTON_2)
{
 pcf8574Struct.stateButton[1] = param.asInt();
 //Let's record the state of the relay on PCF8574
 writeStateExpanderRelay();
}

BLYNK_WRITE(BUTTON_3)
{
 pcf8574Struct.stateButton[2] = param.asInt();
 //Let's record the state of the relay on PCF8574
 writeStateExpanderRelay();
}

BLYNK_WRITE(BUTTON_4)
{
 pcf8574Struct.stateButton[3] = param.asInt();
 //Let's record the state of the relay on PCF8574
 writeStateExpanderRelay();
}

static void readExpanderKey(void)
{ 
 // Read all 8 bits at once
 uint8_t buff_in = pcf8574.read8();
 // We are only interested in 4 high bits (button state)
 buff_in >>= 4;
 // Analyze the state of high bits
 for (int i = 0; i < 4; i++)
 {
  if (BIT_IS_RESET(buff_in, (i)) && !pcf8574Struct.triggerButton[i])
  {
   pcf8574Struct.triggerButton[i] = true;
   pcf8574Struct.stateButton[i] = !pcf8574Struct.stateButton[i];

   if (Blynk.connected())
   {
    switch (i)
    {
    case 0:
     Blynk.virtualWrite(BUTTON_1, pcf8574Struct.stateButton[i]);
     break;
    case 1:
     Blynk.virtualWrite(BUTTON_2, pcf8574Struct.stateButton[i]);
     break;
    case 2:
     Blynk.virtualWrite(BUTTON_3, pcf8574Struct.stateButton[i]);
     break;
    case 3:
     Blynk.virtualWrite(BUTTON_4, pcf8574Struct.stateButton[i]);
     break;
    default:
     break;
    }    
   }
  }
  else if (BIT_IS_SET(buff_in, (i)) && pcf8574Struct.triggerButton[i])
  {
   pcf8574Struct.triggerButton[i] = false;
  }
 }
 // Let's record the new state of the relay
 writeStateExpanderRelay();
}

static void writeStateExpanderRelay(void)
{ 
 uint8_t buff_out = 0xFF;

 for (int i = 0; i < 4; i++)
 {

  if (pcf8574Struct.stateButton[i])
  {   
   BIT_SET(buff_out, i);
  }
  else
  {
   BIT_RESET(buff_out, i);
  }
 }

 pcf8574.write8(buff_out);
}
Отримайте "AUTH TOKEN" для свого пристрою, та вставте його в демонстраційний код замість "auth". А також вставте в демокод "SSID" і "PASS" вашої WiFi мережі.

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

До проекту Blynk додайте 4 кнопки і назначте їм і'я, колір - за вашим смаком. А також назначте кнопкам віртуальні шпильки: V1, V2, V3, V4. Режим всіх кнопок "SWITCH".
Додайте віджет "Notification".

Або скануйте цей код і отримаєте готовий демо-проект на свій смартфон:

Замовлення

  • Плата - 3$ (у.о)
  • Плата + деталі для самостійного збирання - 9$ (у.о)
  • Плата зібрана і протестована -  12$ (у.о)
Щодо придбання плат пишіть на taburyak@gmail.com

Файли

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