起因

​ 女友说晚上睡觉屋子里好黑,有点害怕,做为贴身小保镖的我能想到什么办法呢,当然是买个小夜灯了,可是市面上面的灯又不能在手机上控制(不是说不能啊,不是丑就是小贵),那何不动手做个呢,说干就干,先计划在行动,冲冲冲~~~

构思

​ 目的明确,我们要做一个可以在手机上面控制的小夜灯,构思咋构思 睡一晚第二天就目明心扉了,需要的硬件设备请看图,整体的设计流程就是

手机发送信号给服务器,

服务器吧信息发送给esp8266,

esp8266 通过控制GPIO高低电平通过三极管来控制电路的通断。

电路设计

程序部分

​ 因需要服务端主动发送数据给esp8266所以选择了socket通讯, 服务器端负责接受与发送消息,手机负责发送消息给服务器,esp8266连接服务器的socket服务端,等待服务端发送消息,因socket长连接长时间不进行通讯就会自动断开,所有设计中esp8266过一段时间就会进行一次断开操作,防止长时间socket连接双方无通讯信息造成连接自动断开问题。

首先是服务端

因为我们的目的很明确,所以服务端可以写的比较简单一些,就在收到某些信息的时候广播指定的信息就好。

from socketserver import BaseRequestHandler, ThreadingTCPServer
import threadingBUF_SIZE = 1024all_clients = []class Handler(BaseRequestHandler):def handle(self):address, pid = self.client_addressprint('%s connected!' % address)global all_clientsif self.request not in all_clients:all_clients.append(self.request)while True:try:data = self.request.recv(BUF_SIZE)if len(data) > 0:print('receive=', data.decode('utf-8').strip())cur_thread = threading.current_thread()# response = '{}:{}'.format(cur_thread.ident,data)response = ''# 通知esp 切换灯状态if "on" in data.decode('utf-8'):response = "start"# 当前状态为开灯 通知给appif "1" in data.decode('utf-8'):response = "1"# 当前状态为关灯 通知给appif "0" in data.decode('utf-8'):response = "0"if len(all_clients) == 1:# 如果发现就一个用户则通知不在线response = 'no'self.request.sendall(response.encode('utf-8'))else:# 给 所有连接者 广播消息all_clientsto = []for clients in all_clients:try:if clients != self.request:clients.sendall(response.encode('utf-8'))all_clientsto.append(clients)except :print("发现僵尸客户端.自动剔除")all_clients.clear()all_clients = all_clientstoprint('发送:', response)else:print('close')breakexcept ConnectionResetError:print(address,"  应用断开连接..")try:all_clients.remove(self.request)except:passbreakif __name__ == '__main__':HOST = '172.25.18.127'   ## 如果是外网服务器这里必须是外网ip,不能使用127.0.0.1 ,这样会导致 客户端连接不上PORT = 1112ADDR = (HOST, PORT)server = ThreadingTCPServer(ADDR, Handler)  # 参数为监听地址和已建立连接的处理类print('listening')server.serve_forever()  # 监听,建立好TCP连接后,为该连接创建新的socket和线程,并由处理类中的handle方法处理print(server)

ESP8266客户端

这里原理很简单,

第一步直接开灯。

第二步连接wifi。

第三步连接服务器。

第四步等待接受消息。

第五步接受相应消息做出相应的动作(设置GPIO端口高低电平)控制电路通断。

import socket  # 连接socket 通讯
import time  # 延时函数
import network  # 连接wifi
import machine  # 串口通讯# 服务器地址
socketHost = '8.142.9.82'# 服务器端口号
socketPost = 1112# PIO 端口号  esp8266开发版上面的小LED端口,用于警示当前程序所处的状态  如未连接wifi则LED会相隔一秒闪烁一次
GPIO = 2
# GIPO5  就是三极管的中间脚位所连接的端口
GPIOTO = 5# wifi 列表
WIFI = [{"name": "CMCC-jAGb","password": "2gxju227"
}, {"name": "Mi 10 Ultra","password": "11111111"
}, {"name": "秋枫","password": "11111111"
}]
# 开机开灯  防止找不到网络导致灯不能正常打开
machine.Pin(GPIOTO, machine.Pin.OUT, value=1)sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)a = Truewhile a:sta_if.scan()  # 扫描当前可连接的WiFi名称for wifi in sta_if.scan():for meWifi in WIFI:if meWifi['name'] == wifi[0].decode('utf8'):print('当前wifi:' + meWifi['name'])sta_if.connect(meWifi['name'], meWifi['password'])sta_if.isconnected()a = Falseif not a:p2se = machine.Pin(GPIO, machine.Pin.OUT)p2se.value(0)breaktime.sleep(1)p2 = machine.Pin(GPIO, machine.Pin.IN)p2a = machine.Pin(GPIO, machine.Pin.OUT)if p2.value() == 0:p2a.value(1)print('灭')else:p2a.value(0)print('亮')if 2 == 1:breakwhile True:print("esp8266连接服务器>>>")try:client = socket.socket()client.settimeout(100)client.connect((socketHost, socketPost))while True:try:data = client.recv(1024)print(data.decode('utf-8'))if "1" in data.decode('utf-8'):print('开灯..')# 获取状态p2a = machine.Pin(GPIOTO, machine.Pin.OUT, value=1)elif "0" in data.decode('utf-8'):print('关灯..')# 获取状态p2a = machine.Pin(GPIOTO, machine.Pin.OUT, value=0)except Exception as e:if "ETIMEDOUT" in str(e):client.send('hear'.encode('utf-8'))print("断开与服务器连接")else:raise Exception(str(e))except Exception as e:print("网络异常正在重连接..." + str(e))# 下次连接time.sleep(2)finally:client.close()

手机端

为了方便操作就把开关做成了一个小组件样式,可以随意拖放到桌面上,解决频繁切换应用的麻烦。

package cn.qsub.smarthome;import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.RemoteViews;
import android.widget.TextView;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** Implementation of App Widget functionality.*/
public class NewAppWidget extends AppWidgetProvider {//定义一个action,这个action要在AndroidMainfest中去定义,不然识别不到,名字是自定义的private static final String CLICK_ACTION = "cn.qsub.smarthome.CLICK";private static final String CLICK_ACTION2 = "cn.qsub.smarthome.CLICK2";//服务器地址public static final String HOST = "8.142.9.82";//服务器端口号public static final int PORT = 1112;@Overridepublic void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {// There may be multiple widgets active, so update all of themsuper.onUpdate(context, appWidgetManager, appWidgetIds);Log.e("===>", "11");for (int appWidgetId : appWidgetIds) {//创建一个远程view,绑定我们要操控的widget布局文件RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);Intent intentClick = new Intent();//这个必须要设置,不然点击效果会无效intentClick.setClass(context, NewAppWidget.class);intentClick.setAction(CLICK_ACTION);//PendingIntent表示的是一种即将发生的意图,区别于Intent它不是立即会发生的PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intentClick, PendingIntent.FLAG_UPDATE_CURRENT);//为布局文件中的按钮设置点击监听remoteViews.setOnClickPendingIntent(R.id.smartLamp, pendingIntent);Intent intentClick2 = new Intent();//这个必须要设置,不然点击效果会无效intentClick2.setClass(context, NewAppWidget.class);intentClick2.setAction(CLICK_ACTION2);//PendingIntent表示的是一种即将发生的意图,区别于Intent它不是立即会发生的PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, intentClick2, PendingIntent.FLAG_UPDATE_CURRENT);//为布局文件中的按钮设置点击监听remoteViews.setOnClickPendingIntent(R.id.smartLampOff, pendingIntent2);//告诉AppWidgetManager对当前应用程序小部件执行更新appWidgetManager.updateAppWidget(appWidgetId, remoteViews);}}//onReceive不存在widget生命周期中,它是用来接收广播,通知全局的private Context context = null;/*** 接收窗口小部件点击时发送的广播** @param contexts* @param intent*/@Overridepublic void onReceive(final Context contexts, Intent intent) {System.out.println("111");super.onReceive(contexts, intent);//当我们点击桌面上的widget按钮(这个按钮我们在onUpdate中已经为它设置了监听),widget就会发送广播//这个广播我们也在onUpdate中为它设置好了意图,设置了action,在这里我们接收到对应的action并做相应处理context = contexts;if (intent.getAction().equals(CLICK_ACTION)) {//            //因为点击按钮后要对布局中的文本进行更新,所以需要创建一个远程viewToast.makeText(context, "早~", Toast.LENGTH_LONG).show();Thread Thread =  new Thread(new Runnable() {@Overridepublic void run() {AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);System.out.println("11122");Socket socket = null;try {//创建一个流套接字并将其连接到指定主机上的指定端口号socket = new Socket(HOST, PORT);//读取服务器端数据DataInputStream input = new DataInputStream(socket.getInputStream());//向服务器端发送数据DataOutputStream out = new DataOutputStream(socket.getOutputStream());// 开灯out.writeUTF("1");remoteViews.setInt(R.id.smartLamp, "setBackgroundResource", R.color.tm);remoteViews.setInt(R.id.smartLampOff, "setBackgroundResource", R.drawable.moon);out.close();input.close();} catch (Exception e) {e.printStackTrace();} finally {if (socket != null) {try {socket.close();System.out.println("socket is closed");} catch (IOException e) {socket = null;System.out.println("客户端 finally 异常:" + e.getMessage());}}}//更新widgetappWidgetManager.updateAppWidget(new ComponentName(context, NewAppWidget.class), remoteViews);}});Thread.start();}else if (intent.getAction().equals(CLICK_ACTION2)){Toast.makeText(context, "晚安", Toast.LENGTH_LONG).show();Thread Thread =  new Thread(new Runnable() {@Overridepublic void run() {AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);System.out.println("111");Socket socket = null;try {//创建一个流套接字并将其连接到指定主机上的指定端口号socket = new Socket(HOST, PORT);//读取服务器端数据DataInputStream input = new DataInputStream(socket.getInputStream());//向服务器端发送数据DataOutputStream out = new DataOutputStream(socket.getOutputStream());// 开灯out.writeUTF("0");remoteViews.setInt(R.id.smartLampOff, "setBackgroundResource", R.color.tm);remoteViews.setInt(R.id.smartLamp, "setBackgroundResource", R.drawable.sun);out.close();input.close();} catch (Exception e) {e.printStackTrace();} finally {if (socket != null) {try {socket.close();System.out.println("socket is closed");} catch (IOException e) {socket = null;System.out.println("客户端 finally 异常:" + e.getMessage());}}}//更新widgetappWidgetManager.updateAppWidget(new ComponentName(context, NewAppWidget.class), remoteViews);}});Thread.start();}}@Overridepublic void onEnabled(Context context) {Log.e("===>", "33");// Enter relevant functionality for when the first widget is created}@Overridepublic void onDisabled(Context context) {Log.e("===>", "44");// Enter relevant functionality for when the last widget is disabled}static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {Log.e("===>", "55");
//        CharSequence widgetText = context.getString(R.string.appwidget_text);// Construct the RemoteViews objectRemoteViews views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
//        views.setTextViewText(R.id.smartLamp, widgetText);// Instruct the widget manager to update the widgetappWidgetManager.updateAppWidget(appWidgetId, views);}
}
  • 权限文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="cn.qsub.smarthome" ><uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme" ><receiver android:name=".NewAppWidget" ><intent-filter><action android:name="android.appwidget.action.APPWIDGET_UPDATE" /><!--这个和SimpleWidgetProvider中的CLICK_ACTION对应--><action android:name="cn.qsub.smarthome.CLICK"/><action android:name="cn.qsub.smarthome.CLICK2"/></intent-filter><meta-dataandroid:name="android.appwidget.provider"android:resource="@xml/new_app_widget_info" /></receiver><activity android:name=".MainActivity" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

最后来看看效果吧!

参考文献

https://blog.csdn.net/weixin_45020839/article/details/105807767

https://zhuanlan.zhihu.com/p/64299874

https://codingdict.com/sources/py/machine/17375.html

http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/esp8266-nodemcu-tutorial-index/nodemcu-board/

LmacRxBlk:1 问题

https://forum.micropython.org/viewtopic.php?t=3602

动手制作一个可以手机APP控制的智能小夜灯相关推荐

  1. 毕业设计——基于STM32单片机的绿植养护系统(物联网、智能家居、手机APP控制、自动监测土壤湿度)

    本工程包括一下功能:1.环境温湿度监测 2.土壤湿度监测 3.环境可燃气体浓度监测 4.RTC万年历功能 5.数据的实时显示(OLED屏幕.手机app) 6.自动浇水模式.手动浇水模式 ①自动浇水:系 ...

  2. 怎么DIY快速制作一款手机App软件?

    随着APP软件的普及,越来越多的企业以及个人都加入创建自己的APP应用软件,现在我们来分享一下如何DIY快速制作一款手机App软件. 市面上有很多DIY App制作工具,它们普遍使用的是拼装式.这样的 ...

  3. ios一个app调起另一个app_电商app开发价格:制作一个电商app需要多少钱?

    智能手机的发展,带动了各式各样手机app的市场,现在大家网购大多数都是通过电商app实现,再加上分销.配送等模式发展,自建电商app成为很多企业的选择,电商app开发成本大概多少?制作一个电商app需 ...

  4. 阿里云IOT入门教程(三)阿里云IOT Studio自建手机App控制Wemos D1 Mini( ESP8266 )板载灯亮灭

    阿里云IOT入门教程(一)阿里云IOT Studio自建手机App控制Wemos D1 Mini( ESP8266 )板载灯亮灭 概述 所需材料 Mqtt预备知识 hacklab端开发 * 硬件端上报 ...

  5. 最简单DIY基于ESP8266的智能彩灯③(在网页用按钮+滑动条+手机APP控制RGB灯)

    ESP8266和ESP32智能彩灯开发系列文章目录 第一篇:最简单DIY基于ESP8266的智能彩灯①(在网页用按钮点亮普通RGB灯) 第二篇:最简单DIY基于ESP8266的智能彩灯②(在网页用按键 ...

  6. 智能灯控制页面用HTML编写,3分钟教你创建手机APP控制全彩智能灯泡应用,图形化编程!...

    本帖最后由 bigfanofloT 于 2016-9-22 22:33 编辑 对,你没有看错!!!3分钟教你创建手机APP控制全彩智能灯泡应用,图形化编程,自动代码生成,麻麻再也不用担心我不会编程了, ...

  7. 虚幻4学习笔记(8)动手制作一个小游戏

    动手制作一个小游戏 新节点介绍 前期准备 搭建场景 门蓝图 灯蓝图 创建关卡蓝图 B站UP谌嘉诚课程:https://www.bilibili.com/video/BV164411Y732 新节点介绍 ...

  8. 广州app制作:那个手机app制作平台好?

    随着移动互联网的发展,APP开发技术的逐渐成熟,现在要制作一款手机APP应用软件也变得越来越轻易,市面上也出现了许多可以不需要懂专业APP开发技术和语言,可以提供给普通人开发专属APP应用软件的App ...

  9. 通过手机App控制RGB调节灯带的亮度

    通过手机App控制RGB调节灯带的亮度,Arduino.esp8266.RGB灯带.APP.Android **很久没有更新了,最近过年回家有空拿出来东西玩玩,花了半天的时间搞这个,主要实现的原理,通 ...

最新文章

  1. 华为android8适配进度,华为 荣耀 O版本(Android 8.0)适配进度公告
  2. Vivado中单端口和双端口RAM的区别
  3. Silverlight + Model-View-ViewModel (MVVM)
  4. 第九天2017/04/18(2、类的继承、面试题:继承访问修饰符、组合、static、构造、多态)
  5. agile organization
  6. JS中的this好神奇,都把我弄晕了
  7. 定义一个Matrix类,实现矩阵的加法和乘法
  8. 再谈UDP协议—浅入理解深度记忆
  9. 面试被问高并发流量控制,我脸都绿了...
  10. javaweb工程中web.xml配置
  11. 《机器学习Python实践》第5章——数据导入
  12. 洛谷 1984 [SDOI2008]烧水问题
  13. uni-app uniCloud 开发实践 ,天气预报,小程序,h5,app ,001章
  14. Atitit保证架构超前性 前瞻性 目录 第一章 为什么需要修改代码 1 第一节 业务增加功能 1 第二节 增加字段 1 第三节 增加表数据需要查询 修改 1 第四节 类库升级 1 第二章 简单抽象
  15. mysql5.6.38 设置密码_Percona Server MySQL 5.6.38修改root密码(忘记root密码)
  16. 程序员最爱字体_网页设计师最爱的十大字体
  17. MAC-多开程序两种方法
  18. python实现WGS84平面大地坐标(X,Y)转换经纬度BL类
  19. 【深度学习】【ICLR2019】DARTS代码解读
  20. 点亮一盏灯,温暖一个梦

热门文章

  1. 【WOA三维路径规划】灰狼算法无人机三维航迹规划【含Matlab源码 2445期】
  2. IT实现工作流程自动化的8个步骤
  3. 拍摄360全景照片多少钱?全景图片用什么软件看?
  4. [Gurobi] 简单模型的建立
  5. 一起学习gurobi
  6. java计算机毕业设计购物网站源码+数据库+系统+lw文档+mybatis+运行部署
  7. web端仿微信朋友圈定位检索功能
  8. codeforces 708B-Recover the String
  9. python处理视频动漫化_太牛逼了!用 Python 实现抖音上的“人像动漫化”特效,原来这么简单!...
  10. Day837.StampedLock-Java 并发编程实战