ESP8266 WiFi Android Controller

Нам понадобится:

Шаг 1 (настройка Arduino IDE):

Самое первое что нам нужно сделать это установить в Arduino IDE две библиотеки:

На видео я показал где это можно сделать. После того как это сделано убедитесь что у вас также установлена поддержка плат ESP8266 и выбрана в разделе “Инструменты – Платы“. Если все выбрано то подключаем к ПК микроконтроллер ESP8266 и выбираем в “Инструменты – Порт” порт к которому подключен ESP8266

Шаг 2 (копируем скетч в Arduino IDE):

#include <ESP8266WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>

bool led1IsOn = false;
bool led2IsOn = false;
bool led3IsOn = false;
const int LED_0 = 16;
const int LED_1 = 5;
const int LED_2 = 2;
const int oneWireBus = 4; 
const char* ssid = "";
const char* password = "";
float temperatureC = 0.0f;
// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);
OneWire oneWire(oneWireBus);
DallasTemperature sensors(&oneWire);


void setup() {
 
  Serial.begin(115200);
  pinMode(LED_0, OUTPUT);
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  delay(1000);
  digitalWrite(LED_0, HIGH);
  digitalWrite(LED_1, HIGH);
  digitalWrite(LED_2, HIGH);
  delay(1000);
  digitalWrite(LED_0, LOW);
  digitalWrite(LED_1, LOW);
  digitalWrite(LED_2, LOW);
  delay(10);
  sensors.begin();
  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  // Start the server
  server.begin();
  Serial.println("Server started");

  // Print the IP address
  Serial.println(WiFi.localIP());
}

void loop() {
  // Check if a client has connected
  sensors.requestTemperatures(); 
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
  // Wait until the client sends some data
  Serial.println("new client");
  while(!client.available()){
    delay(1);
  }
   // Read the first line of the request
  String req = client.readStringUntil('\r');
  Serial.println(req);
  // Match the request
  controller(req, client);
  client.flush(); 
  // Prepare the response
  String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
  s += temperatureC;
  // Send the response to the client
  client.print(s);
  delay(1);
  Serial.println("Client disonnected");

  // The client will actually be disconnected 
  // when the function returns and 'client' object is detroyed
}
void controller(String req,  WiFiClient client){
   if (req.indexOf("/temperature") != -1){
     temperatureC = sensors.getTempCByIndex(0);
     Serial.println("Showing temperature");
    } else if(req.indexOf("/led1") != -1){
     setLedState(LED_0, led1IsOn);
     led1IsOn = !led1IsOn;
    } else if(req.indexOf("/led2") != -1){
      setLedState(LED_1, led2IsOn);
      led2IsOn = !led2IsOn;
    } else if(req.indexOf("/led3") != -1){
      setLedState(LED_2, led3IsOn);
      led3IsOn = !led3IsOn;
    } else {
    Serial.println("invalid request");
    client.stop();
    return;
  }
  }
void setLedState(int led, bool state){
  if(!state){
    digitalWrite(led, HIGH);
  } else {
    digitalWrite(led, LOW);}
  }

Шаг 3 (Указываем ssid и password сети):

В самом начале скетча есть две переменные это ssid и password. В них нужно указать название вашей сети WiFi и пароль сети чтобы ESP8266 мог к ней подключится. 

Шаг 4 (Загружаем скетч на ESP8266):

Загружаем скетч на микроконтроллер. Если все сделали правильно то после загрузки все 3 светодиода должны загореться и потухнуть.

Шаг 5 (Загружаем скетч на ESP8266):

Загружаем скетч на микроконтроллер. Если все сделали правильно то после загрузки все 3 светодиода должны загореться и потухнуть. А также открываем в Arduino IDEИнструменты – Монитор порта” там увидим сообщение удалось подключится к сети или нет. Если удалось значит мы готовы для сборки схемы.

Шаг 6 (Сборка схемы):

Шаг 7 (Создаем приложение):

MainActivity:

import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import com.neco_desarrollo.esp8266controller2.databinding.ActivityMainBinding
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException

class MainActivity : AppCompatActivity() {
private lateinit var request: Request
private lateinit var binding: ActivityMainBinding
private lateinit var pref: SharedPreferences
private val client = OkHttpClient()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
pref = getSharedPreferences("MyPref", MODE_PRIVATE)
onClickSaveIp()
getIp()
binding.apply {
bLed1.setOnClickListener(onClickListener())
bLed2.setOnClickListener(onClickListener())
bLed3.setOnClickListener(onClickListener())
}
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
if(item.itemId == R.id.sync) post("temperature")
return true
}

private fun onClickListener(): View.OnClickListener{
return View.OnClickListener {
when(it.id){
R.id.bLed1 -> { post("led1") }
R.id.bLed2 -> { post("led2") }
R.id.bLed3 -> { post("led3") }
}
}
}

private fun getIp() = with(binding){
val ip = pref.getString("ip", "")
if(ip != null){
if(ip.isNotEmpty()) edIp.setText(ip)
}
}

private fun onClickSaveIp() = with(binding){
bSave.setOnClickListener {
if(edIp.text.isNotEmpty())saveIp(edIp.text.toString())
}
}

private fun saveIp(ip: String){
val editor = pref.edit()
editor.putString("ip", ip)
editor.apply()
}

private fun post(post: String){
Thread{

request = Request.Builder().url("http://${binding.edIp.text}/$post").build()
try {
var response = client.newCall(request).execute()
if(response.isSuccessful){
val resultText = response.body()?.string()
runOnUiThread {
val temp = resultText + "Cº"
binding.tvTemp.text = temp
}
}
} catch (i: IOException){

}

}.start()
}
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:id="@+id/tvTemp"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="sans-serif-condensed-medium"
android:gravity="center"
android:text="0Cº"
android:textColor="@color/teal_700"
android:textSize="80sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/bLed3"
app:layout_constraintEnd_toEndOf="@+id/bSave"
app:layout_constraintHorizontal_bias="0.332"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="@+id/bSave"
app:layout_constraintTop_toBottomOf="@+id/bSave" />

<EditText
android:id="@+id/edIp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:ems="10"
android:hint="Ip address"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/bSave"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Save Ip"
app:layout_constraintEnd_toEndOf="@+id/edIp"
app:layout_constraintStart_toStartOf="@+id/edIp"
app:layout_constraintTop_toBottomOf="@+id/edIp" />

<Button
android:id="@+id/bLed1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Led 1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<Button
android:id="@+id/bLed2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="led 2"
app:layout_constraintBottom_toTopOf="@+id/bLed1"
app:layout_constraintEnd_toEndOf="@+id/bLed1"
app:layout_constraintStart_toStartOf="@+id/bLed1" />

<Button
android:id="@+id/bLed3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Led 3"
app:layout_constraintBottom_toTopOf="@+id/bLed2"
app:layout_constraintEnd_toEndOf="@+id/bLed2"
app:layout_constraintStart_toStartOf="@+id/bLed2" />

</androidx.constraintlayout.widget.ConstraintLayout>

main_menu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/sync"
android:title="Sync"
android:icon="@drawable/ic_sync"
app:showAsAction="ifRoom"/>
</menu>

Библиотека OkHttp(добавить в build.gradle):

implementation 'com.squareup.okhttp3:okhttp:3.10.0'

Permission(добавить в AndroidManifest.xml):

<uses-permission android:name="android.permission.INTERNET"/>

19 комментариев для “ESP8266 WiFi Android Controller”

  1. Доброго времнни суток Сергей,

    Подскажите как отойти от локальной wi-fi сети, т.е. развернуть сервачок который будет через интернет принимать и отправлять статусы и команды из/в андроид приложение?

    Подскажите как это гуглить?
    Всё что я нахожу это статьи про mqtt серверы, но они платные и дорогие.

    1. Вам нужен серый или белый IP адрес у провайдера, на роутере пробросить какой-нибудь нестандартный порт с внешнего адреса, который выдает провайдер, на внутренний адрес вашей платы wifi
      Например яндекс пишет что у вас адрес 8.8.8.8, а у платы адрес 192.168.0.51
      В роутере вам нужно пробросить порт скажем 85 на внутренний адрес 192.168.0.51 на уже стандарный порт http 80
      При этом с внешнего адреса обращаться как http:\\8.8.8.8\led1:85
      Провайдеры не всегда выдают серые адреса – который при каждом подключении новый, но по которому можно обратиться к вашему роутеру из интернета, часто сеть провайдера со своими адресами и тогда точно придется арендовать белый ip.
      Есть ещё сервисы в которых можно прикрутить имя к вашему внешнему серому динамическому ip гуглить freeDns. Ну или купить доменное имя и прикрутить к своему арендованному белому ip, но это уже другая история.

  2. Единственное, что не получится, так это простое копирование кода. Андроид Студио требует местами синхронизации, импортирования. B месте добавления иконки в файле main_menu.xml пришлось вручную указывать рисунок. Приложение запустилось, а вот скетч ещё не собирал.

  3. Заработало на ESP32! Кроме датчика температуры, которого просто у меня нет. А светодиодики включает. Только нужно заменить первую строку счетча с #include на #include . И конечно установить требующиеся библиотеки.

  4. ничего не получается почемуто, первый раз в андроидстудио.
    как окно с рисованием меню достать? нажимаю на ActivityMainBinding(как в видео), и ничего не происходит.

    1. Надо вам посмотреть видеоурок про работу с Binding. Сначала надо добавить в файл buildgradle(app) такую строку buildFeatures{viewBinding true}. Потом нажать справа вверху кнопку Sync Now. Так он включается. Это всё есть и в этом видео. Короче разбираться надо досконально.

  5. Заработало не сразу в части ESP32.
    Библиотеку пришлось поменять на WiFi.h
    И не получался нормальный ответ ESP32 на запрос, т.е. светодиоды включались, а температура в ответ не приходила. При этом если браузером зайти на ESP по адресу скажем 192.168.0.51/led1 то пишет что сервер внезапно разорвал соединение(светодиод включается). Ну и в студии если в блоке catch выводить ошибку, тоже самое показывает.
    Победить удалось убрав client.flush(); и строку ответа поменял на раздельные строки
    client.println(“HTTP/1.1 200 OK”);
    client.println(“Content-Type: text/html”);
    client.println();
    client.println(“25.4”);
    почему то символы переноса /r/n не срабатывают Arduino IDE версии 2.0.1

    1. спасибо за подсказку
      Просто убрал client.flush(); и показ температуры заработал.
      До этого код try {} не запускался.

  6. Здравствуйте, возникла проблема при private lateinit var binding: ActivityMainBinding
    выделяет красным, bilding включил

  7. Пожалуйста помогите, возникла проблема при private lateinit var binding: ActivityMainBinding
    выделяет красным, binding включил

Добавить комментарий

Ваш адрес email не будет опубликован.