折腾斐讯K2刷LEDE

昨天又看到有人上车,于是没有经受住诱惑,撸了一台蓝色的斐讯K2,不知道能不能安全下车。颜值嘛还可以。做功嘛略显粗糙。打开看了一下配置。MT7620,64M的RAM和8M的ROM。。。这也能标399的价格,也是呵呵了。

首先刷一个优秀的Bootloader,这里我选择Breed,毕竟当年的U-Boot已经不行了。
这里有一个神奇的方法可以刷入Breed,不禁让我对作者的脑洞感到由衷的佩服。只有一句“这TM也行”可以表达我的内心想法了。

http://www.right.com.cn/forum/thread-204435-1-1.html

刷完Breed以后重启长按reset就可以进入刷机模式,就可以刷入系统固件了。
多年经验告诉我Openwrt是个好固件,然而去年openwrt内部出现了一些问题。导致有一部分核心人员独立出来成立了LEDE-Project。全称Linux Embedded Development Environment(名字起的好霸气啊),想想以前那么多的openwrt版本,那么混乱的Issue管理和补丁发布。这次还是试试LEDE吧。毕竟是新项目,又是openwrt的核心团队搞的,应该会不错的。

LEDE官网在此

在官网一搜,居然有K2的专门版本。这下好了,不用自己编译了。交叉编译搞死人。搜索PHICOMM K2 PSG1218就可以找到了。这里也贴一下下载地址
刷完之后重启。看到一个丑丑的Luci界面。

配置好密码和网络等基本配置。还剩90%的剩余空间。爽啊。果断准备安装全套[不可描述]、[不可描述]和[不可描述]的软件。
安装过程中就不写了。水表已拆。说一句现在安装和编译东西比几年前我搞hg255d的时候简单方便多了。又不用考虑剩余内存。简直是一帆风顺啊。
嗯再放一个获取中国国内IP列表的脚本吧。

wget -O- 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | awk -F\| '/CN\|ipv4/ { printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > /etc/不可描述.txt

最后装一个luci-i18n汉化包和theme包美化一下。结束战斗。

测试一下2.4G+5G(虽然我只开了5G)信号都还是不错的。速度也还算稳定。[不可描述]的速度也能跑满带宽。十分满意。最后贴个界面图,这个 luci-theme-material还是挺好看的。
图

采用Arduino IDE对ESP8266进行编程

话说ESP8266真是个神奇的芯片,不仅可以使用NodeMCU和micropython等固件,还可以直接支持采用Arduino IDE环境进行编程和烧写。本身又有wifi和gpio,又便宜。简直是折腾神器。

采用Arduino编程的最大好处是,很多库可以直接引用。不用再写一些很底层的代码去驱动外设。这大大提高的开发效率,并且像上篇文章里写的,采用lua语言的NodeMCU还是有一些局限性的。比如不支持微秒级的延时。

要采用Arduino对ESP8266进行编程,首先要有一个1.6.4版本以上的Arduino IDE

其次在设置里需要把additional board manager URLs 设置为http://arduino.esp8266.com/stable/package_esp8266com_index.json

这样在工具里的开发板选择里就有了NodeMCU1.0选项。

下载完工具链就可以直接编写了。

下面是用Arduino写的gp2y1010au0f粉尘检测来测AQI的代码。测试通过

/*
   NodeMCU连接夏普GP2Y1010AU0F空气质量传感器检测PM2.5
*/
/* 定义引脚 */
#define PIN_DATA_OUT A0 //连接空气质量传感器模拟量输出的IO口, NodeMCU只有A0可以作为ADC
#define PIN_LED_VCC 5 //空气质量传感器中为内部Led供电的引脚
/* 定义时间 */
const int DELAY_BEFORE_SAMPLING = 280; //采样前等待时间
const int DELAY_AFTER_SAMPLING = 40; //采样后等待时间
const int DELAY_LED_OFF = 9680; //间隔时间
/**
   读取输出电压
*/
double getOutputV() {
  digitalWrite(PIN_LED_VCC, LOW);
  delayMicroseconds(DELAY_BEFORE_SAMPLING);
  double analogOutput = analogRead(PIN_DATA_OUT);
  delayMicroseconds(DELAY_AFTER_SAMPLING);
  digitalWrite(PIN_LED_VCC, HIGH);
  delayMicroseconds(DELAY_LED_OFF);
  //Arduino模拟量读取值的范围为0~1023,以下换算为0~5v
  double outputV = analogOutput / 1024 * 5;
  return outputV;
}
/**
   根据输出电压计算灰尘密度
*/
double getDustDensity(double outputV) {
  //输出电压和灰尘密度换算公式: ug/m3 = (V - 0.9) / 5 * 1000
  double ugm3 = (outputV - 0.9) / 5 * 1000;
  //去除检测不到的范围
  if (ugm3 < 0) {
    ugm3 = 0;
  }
  return ugm3;
}
/**
   根据灰尘密度计算AQI
   环境空气质量指数(AQI)技术规定(试行)](http://kjs.mep.gov.cn/hjbhbz/bzwb/dqhjbh/jcgfffbz/201203/t20120302_224166.htm
*/
double getAQI(double ugm3) {
  double aqiL = 0;
  double aqiH = 0;
  double bpL = 0;
  double bpH = 0;
  double aqi = 0;
  //根据pm2.5和aqi对应关系分别计算aqi
  if (ugm3 >= 0 && ugm3 <= 35) {
    aqiL = 0;
    aqiH = 50;
    bpL = 0;
    bpH = 35;
  } else if (ugm3 > 35 && ugm3 <= 75) {
    aqiL = 50;
    aqiH = 100;
    bpL = 35;
    bpH = 75;
  } else if (ugm3 > 75 && ugm3 <= 115) {
    aqiL = 100;
    aqiH = 150;
    bpL = 75;
    bpH = 115;
  } else if (ugm3 > 115 && ugm3 <= 150) {
    aqiL = 150;
    aqiH = 200;
    bpL = 115;
    bpH = 150;
  } else if (ugm3 > 150 && ugm3 <= 250) {
    aqiL = 200;
    aqiH = 300;
    bpL = 150;
    bpH = 250;
  } else if (ugm3 > 250 && ugm3 <= 350) {
    aqiL = 300;
    aqiH = 400;
    bpL = 250;
    bpH = 350;
  } else if (ugm3 > 350) {
    aqiL = 400;
    aqiH = 500;
    bpL = 350;
    bpH = 500;
  }
  //公式aqi = (aqiH - aqiL) / (bpH - bpL) * (desity - bpL) + aqiL;
  aqi = (aqiH - aqiL) / (bpH - bpL) * (ugm3 - bpL) + aqiL;
  return aqi;
}
/**
   根据aqi获取级别描述
*/
String getGradeInfo(double aqi) {
  String gradeInfo;
  if (aqi >= 0 && aqi <= 50) {
    gradeInfo = String("Perfect");
  } else if (aqi > 50 && aqi <= 100) {
    gradeInfo = String("Good");
  } else if (aqi > 100 && aqi <= 150) {
    gradeInfo = String("Mild polluted");
  } else if (aqi > 150 && aqi <= 200) {
    gradeInfo = String("Medium polluted");
  } else if (aqi > 200 && aqi <= 300) {
    gradeInfo = String("Heavily polluted");
  } else if (aqi > 300 && aqi <= 500) {
    gradeInfo = String("Severely polluted");
  } else {
    gradeInfo = String("Broken roof!!!");
  }
  return gradeInfo;
}
void setup() {
  Serial.begin(115200);
  pinMode(PIN_DATA_OUT, INPUT); //定义为输入(ADC读取模拟量)
  pinMode(PIN_LED_VCC, OUTPUT); //定义为输出
}
void loop() {
  double outputV = getOutputV(); //采样获取输出电压
  double ugm3 = getDustDensity(outputV); //计算灰尘浓度
  double aqi = getAQI(ugm3); //计算aqi
  String gradeInfo = getGradeInfo(aqi); //计算级别
  //打印到串口
  Serial.println(String("outputV=") + outputV + "\tug/m3=" + ugm3 + "\tAQI=" + aqi + "\tgradeInfo=" + gradeInfo);
  //间隔1秒执行下次检测
  delay(1000);
}

采用NodeMCU和GP2Y1010AU0F检测空气质量

夏普GP2Y1010AU0F传感器用于检测空气中的灰尘浓度。可以检测非常细小的灰尘,例如香烟烟雾(粒径0.1~2um)。
该传感器中心有一个孔洞,可使空气自由穿过。内部有一个LED向孔洞进行照射。当空气中的灰尘穿过孔洞时,光线反射到接收端,通过放大电路将反射光强放大并转化为输出电压。通过测量输出电压并进行响应的换算,即可得知空气中灰尘的浓度。

GP2Y1010AU0F引脚连接

(1)V-LED + 150ohm电阻 + 220uF电容->VCC
(2)LED-GND->GND
(3)LED->D1(GPIO 5)
(4)S-GND->GND
(5)VO->A0
(6)VCC->VCC

其中需要加150ohm电阻和220uF电容,连接方式如下图中红框所示:

connect

可以直接使用NodeMCU的adc即A0接口进行采样。先对GPIO 5拉低产生一个采样脉冲,读取A0的adc数据,即为输出电压。转换后可得粉尘浓度。代码如下

pin1 = 1
gpio.mode(pin1, gpio.OUTPUT) 
tmr.alarm(1, 2000, 1, function()
    gpio.write(pin1,gpio.LOW)
    adcv = adc.read(0)
    gpio.write(pin1,gpio.HIGH)
    outv = adcv/ 1024 * 5
    ugm3 = (outv - 0.9) / 5 * 1000
    if (ugm3 >= 0 and ugm3 <= 35) then
	    aqiL = 0
	    aqiH = 50
	    bpL = 0
	    bpH = 35
		aqi = (aqiH - aqiL) / (bpH - bpL) * (ugm3 - bpL) + aqiL
	elseif (ugm3 > 35 and ugm3 <= 75) then
	    aqiL = 50
	    aqiH = 100
	    bpL = 35
	    bpH = 75
		aqi = (aqiH - aqiL) / (bpH - bpL) * (ugm3 - bpL) + aqiL
	elseif (ugm3 > 75 and ugm3 <= 115) then
	    aqiL = 100
	    aqiH = 150
	    bpL = 75
	    bpH = 115
	    aqi = (aqiH - aqiL) / (bpH - bpL) * (ugm3 - bpL) + aqiL
	elseif (ugm3 > 115 and ugm3 <= 150) then
	    aqiL = 150
	    aqiH = 200
	    bpL = 115
	    bpH = 150
	    aqi = (aqiH - aqiL) / (bpH - bpL) * (ugm3 - bpL) + aqiL
	elseif (ugm3 > 150 and ugm3 <= 250) then
	    aqiL = 200
	    aqiH = 300
	    bpL = 150
	    bpH = 250
	    aqi = (aqiH - aqiL) / (bpH - bpL) * (ugm3 - bpL) + aqiL
	elseif (ugm3 > 250 and ugm3 <= 350) then
	    aqiL = 300
	    aqiH = 400
	    bpL = 250
	    bpH = 350
	    aqi = (aqiH - aqiL) / (bpH - bpL) * (ugm3 - bpL) + aqiL
	elseif (ugm3 > 350) then
	    aqiL = 400
	    aqiH = 500
	    bpL = 350
	    bpH = 500
	    aqi = (aqiH - aqiL) / (bpH - bpL) * (ugm3 - bpL) + aqiL
	else
		aqi = 0
	end
	print(aqi)
end)

其中adcv/ 1024 * 5和(outv - 0.9) / 5 * 1000为1024级采样精度转换为电压和相应电压下的粉尘浓度换算,具体可以查阅datasheet得到。如果换算后为负数,即可认为浓度太低检测不到。

下方的aqi换算公式为国家标准环境空气质量指数(AQI)技术规定(试行)( HJ 633—2012 2016-01-01实施)。

实际运行时发现数据不稳定,时有时无。查阅资料发现LED开启过程中有一个上升期。当LED开启持续0.28ms时,对输出电压进行采样最为准确。而NodeMCU的elua脚本并不支持微秒级别的延时。于是考虑其他方法。

查阅资料发现在不更换其他芯片单片机的情况下,ESP8266这块芯片可以采用Arduino的IDE进行C语言程序的编写。下一篇文章会具体写一下如何采用Arduino IDE环境对ESP8266进行编程。

使用NodeMCU和AM2302(DHT22)制作的在线温湿度监控系统

NodeMCU可以用了,而手头上还有一块AM2302(DHT22)的温湿度测量模块。于是就想折腾一个可以实时监控温湿度并且上传到服务器,存到数据库供随时查看变化的温湿度监控系统。

首先查阅了NodeMCU的文档,初步确定了使用wifi模块连接wifi,http模块负责利用post上传数据,net模块来建立本地服务器供实时查看,tmr模块来做定时器定时获取数据,DHT模块来读取DHT22的数据,所以重新编译了内置了DHT Module的NodeMCU固件刷入。

首先在服务器上建立了一个数据库的两个数据表来存储相应数据,其中station表存储不同的NodeMCU模块的IP和上线时间,以供未来扩展多个监测点的时候使用,而data表存储具体的温度湿度和数据采集时间。

硬件连接首先是AM2302(DHT22)的VCC和GND分别接NodeMCU的3.3和GND输出。其次是DATA和NodeMCU的D5口相连,即可。

这里定义dhtpin=5

首先通过

wifi.setmode(wifi.STATION)
wifi.sta.config("wifi SSID","password")
wifi.sta.connect()

连接wifi,不要忘记设定dns地址

net.dns.setdnsserver("114.114.114.114", 0)

并且做了一个定时器,每隔一秒确认是否连接成功,连接成功以后会像服务器发起post请求把自己的IP和station ID汇报给服务器,代表监测站上线成功。其中监测站id即station变量我以地理位置+chipid来定义。

tmr.alarm(2, 1000, 1, function()
if wifi.sta.status()==5 then
http.post('URL',
      'Content-Type: application/json\r\n',
      '{"station":"'..station..'","ip":"'..wifi.sta.getip()..'","pass":"password"}',
      function(code, data)
        if (code < 0) then
          print("HTTP request failed")
        else
            tmr.stop(2)
          print(code, data)
        end
      end)
 else
    print("connecting")
 end
end)

json中的pass是为了服务器做简单的安全性校验用。
接下来是建立一个简单的服务器以供本地实时查询,代码如下

srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
    conn:on("receive", function(sck, payload)
        gpio.write(led,gpio.LOW)
        status, temp, humi, temp_dec, humi_dec = dht.read(dhtpin)
        if status == dht.OK then
            sck:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>DHT Temperature:"..temp..";".."Humidity:"..humi.."</h1>")
            gpio.write(led,gpio.HIGH)
        end
        end)
    conn:on("sent", function(sck) sck:close() end)
end)

其中status, temp, humi, temp_dec, humi_dec = dht.read(dhtpin)为DHT模块读取语句

最后是建立另一个定时器,定时采集温湿度参数并且发送给服务器,代码为一分钟间隔采集。

tmr.alarm(1, 60000, 1, function()
    status, temp, humi, temp_dec, humi_dec = dht.read(dhtpin)
    if status == dht.OK then
           http.post('url',
          'Content-Type: application/json\r\n',
          '{"temp":"'..temp..'","humi":"'..humi..'","station":"'..station..'","pass":"tms"}',
          function(code, data)
            if (code < 0) then
              print("HTTP request failed")
            else
              print(code, data)
            end
          end)
    end
end)

至此客户端的代码就算告一段落了。服务端代码无非是数据库插入,查询,echarts展示。

效果参见http://tms.im/Mo/tms.php

成品图
TH

折腾NodeMCU

从淘宝买回来一个CP2102+ESP8266的小模块准备刷NodeMCU固件玩下,却发现插上电脑以后没有任何反应,本来应该电脑提示发现新硬件,但是试了多次均没有。

装驱动,找文档,看了好久都没有类似这种现象的发生,找淘宝店家也不理我。只好自己折腾下。

nodemcu

经过测试发现,flash按钮和复位按钮是有效的,于是推测是USB转UART芯片CP2102坏掉了导致电脑不识别这块板。而ESP8266应该是完好的。

查了一下NodeMCU的资料发现这货其实已经把ESP8266的全部引脚都引出了。既然这样,为何不自己做外围下载电路和UART模块来救活这个ESP8266。

翻了一下抽屉,发现手头上只有一块古老的PL-2303。于是用他做了一个USB转UART的电路。对照这块板的pinmap把RX->TXD0和TX->RXD0,VCC->+5V和GND->GND分别接好。

pinmap

连上电脑,发现了虚拟串口设备,装好PL2303的驱动,打开ESP8266Flasher,选择好编译好的NodeMCU固件点击刷入。按住板子上的flash后点按reset。电脑成功识别到了ESP8266。

经过十几分钟的等待,终于刷入成功。

迫不及待的打开ESPlorer,连接成功。果然是CP2102的锅。终于救活了这块ESP8266,接下来就可以好好折腾NodeMCU了。