关键字:go、get、代理
时间:2019年9月

第一步,搭建代理

root@debian:~# ssh -D 1080 -CN root@1.2.3.4

注:1.2.3.4是代理服务器的IP,需要有ssh登录权限。这里使用的ssh建立socks5代理,相对比较简单。

第二步,设置git代理

root@debian:~# git config --global http.proxy socks5://127.0.0.1:1080
root@debian:~# git config --global https.proxy socks5://127.0.0.1:1080

第三步,使用代理执行go get

root@debian:~# http_proxy=socks5://127.0.0.1:1080 go get golang.org/x/net/proxy

第四步,取消代理

root@debian:~# git config --global --unset http.proxy
root@debian:~# git config --global --unset https.proxy

注:与修改“`~/.gitconfig“`文件效果相同。

关键字:esp、调试、串口、74880
时间:2019年8月

前言

ESP-01开发板是基于ESP8266芯片的8针脚微型开发板。

调试口

虽然ESP-01开发板总共只有8个针脚,但有两串口可以使用,Uart0和Uart1。Uart0有TX和RX两个针脚,Uart1只有TX针脚。调试信息通过Uart0输出。

串口设置

ESP-01开发板串口默认使用的波特率是74880,是一个特殊的波特率,通常串口工具都无法选择这个波特率。

推荐工具1

miniterm.py,命令行指定波特率。

root@debian:~# chmod 777 miniterm.py
root@debian:~# miniterm.py /dev/ttyUSB0 74880

miniterm.py内容见参考文献

推荐工具2

minicom,需要手动修改配置文件。

root@debian:~# vim /etc/minicom/minirc.dfl
# Machine-generated file - use "minimum -s" to change parameter.
pu port          /dev/ttyUSB0
pu baud rate     74880
pu bits          8
pu parity        N
pu stopbits      1
:wq!
root@debian:~# minicom

参考

https://blog.csdn.net/ustccw/article/details/85797692

注意事项

1、用户权限需要添加android.permission.INTERNET
2、不能在主线程上发送http请求,因为主线程是UI控制线程

manifests配置权限

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

子线程代码

@Override
public void onClick(View view) {
	new Thread() {
		public void run() {
			try {
				URL url = new URL("https://www.baidu.com");
				HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
				httpURLConnection.setRequestMethod("GET");
				httpURLConnection.setDoInput(true);
				httpURLConnection.setDoOutput(false);
				httpURLConnection.setConnectTimeout(30000);
				httpURLConnection.setReadTimeout(30000);
				httpURLConnection.connect();
				BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "utf-8"));
				String line;
				StringBuilder stringBuilder = new StringBuilder();
				String response;
				while ((line = bufferedReader.readLine()) != null) {
					stringBuilder.append(line);
				}
				response = stringBuilder.toString();
				bufferedReader.close();
				httpURLConnection.disconnect();
		   } catch (Exception e) {
			   e.printStackTrace();
		   }
		}
	}.start();
}
...

关键字:android、ble、蓝牙、gatt
时间:2019年06月

前言

阅读本文前,需要熟悉Activity的开发,例如:onCreate()、onResume()等。

基本概念

经典蓝牙

通迅前需要手机在设置中填写密钥配对

通过建立socket连接发送数据

BLE

4.0以上版本均为BLE

BLE以Characteristic作为数据传输通道获取数据

每个蓝牙设备可提供多个service

每个service由多个Characteristic组成

每个Characteristic又有多个Descriptor

BLE开发的关键点

通过manager获取adapter,BluetoothManager.getAdapter()

通过adapter获取scannser,BluetoothAdapter.getBluetoothLeScanner()

启动scanner产生回调,BluetoothLeScanner.startScan( ScanCallback )

通过回调函数中的result获取device,ScanCallback.onScanResult( ScanResult ) { ScanResult.getDevice() }

通过device获取gatt,BluetoothDevice.connectGatt( BluetoothGattCallback )

连接成功的回调中启动discoverServices, BluetoothGattCallback.onConnectionStateChange( BluetoothGatt ){ BluetoothGatt.discoverServices() }

发现服务回调中获取service,BluetoothGattCallback.onServicesDiscovered( BluetoothGatt ){ gatt.getServices() }

通过service获取characteristic,BluetoothGattService.getCharacteristics()

修改characteristic获取数据方式 BluetoothGatt.setCharacteristicNotification( characteristic, true | false )

发送读characteristic数据请求,BluetoothGatt.readCharacteristic( characteristic )

在读到数据的回调中获取数据,BluetoothGattCallback.onCharacteristicRead( characteristic ) { characteristic.getValue() }

manifests设置权限

manifests文件中,需要增加蓝牙权限和定位权限。蓝牙权限需要解释,定位权限如果没有,会扫描不到任何蓝牙设备。

<manifest xmlns:...
	...
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    ...
</manifests>

获取定位授权

在高版本Android系统中,定位需要用户授权才能使用。如果用户未授权,仍然扫描不到任何蓝牙设备。获取用户授权有两种方法。

方法一

在Android设置中找到定位服务,找到App,允许获取位置。

方法二

在程序中弹出对话框,提示用户是否允许。

protected void onResume() {
	...
	if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
	ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                    1);
	}
	...
}

检查手机是否支持蓝牙

protected void onCreate(Bundle savedInstanceState) {
	...
	if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
		Toast.makeText(this, "不支持BLE", Toast.LENGTH_SHORT).show();
		finish();
	}
	...
}

获取BluetoothManager实例

protected void onCreate(Bundle savedInstanceState) {
	...
	final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
	...

通过BluetoothManager实例获取BluetoothAdapter实例

BluetoothAdapter变量定义为成员变量,便于其他地方使用。

...
private BluetoothAdapter mBluetoothAdapter;
...

protected void onCreate(Bundle savedInstanceState) {
	...
	mBluetoothAdapter = bluetoothManager.getAdapter();
	if (mBluetoothAdapter == null) {
		Toast.makeText(this, "不支持蓝牙", Toast.LENGTH_SHORT).show();
		finish();
	}
	...
} 

判断蓝牙是否开启

检查蓝牙是否打开,没打开则弹框提示用户打开。

protected void onResume() {
	...
	if (!mBluetoothAdapter.isEnabled()) {
		Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
		startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
	}
	...
}

扫描附近的蓝牙设备

扫描需要用到BluetoothLeScanner类,该类实例通过mBluetoothAdapter.getBluetoothLeScanner()获取。
部分文章提到使用LeScanCallback类和startLeScan()方法扫描,该方法已经弃用,不建议使用。

BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
scanner.startScan(new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            BluetoothDevice device = result.getDevice();
            if (!mDevices.contains(device)) {
                mDevices.add(device);
                // mAdapter是和界面上ListView绑定的ArrayAdapter,具体看完整代码。
                mAdapter.add(device.getAddress());
                mAdapter.notifyDataSetChanged();
            }
        }
    }

小结

通过上面几步已经能够扫描到周围的蓝牙设备了,还可以通过ScanResult的getRssi()方法获取设备的信号强度,以及使用BluetoothDevice的getName()方法获取设备名称,但getName()有可能为null,使用时请注意。getName()为空非常不便于用户辨识设备,个人感觉是否为空可能与信号强度有关系,可以改变距离进行尝试。

扫描获取到device对象已经可以进行连接了,但device对象无法序列化,传递很不方便,可以使用device.getAddress()代替device进行参数传递。

其他Activity获取device对象

地址可以作为设备唯一标识进行参数传递,再还原成BluetoothDevice对象使用。

BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter adapter = manager.getAdapter();
BluetoothDevice device = adapter.getRemoteDevice("43:41:3C:F2:85:EE");

获取BluetoothGatt并进行连接

BluetoothDevice device = adapter.getRemoteDevice("43:41:3C:F2:85:EE");
mBluetoothGatt = device.connectGatt(this, false, mGattCallback); // mGattCallback说明见下文

回调函数

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            Log.d(TAG, "onConnectionStateChange()");
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.i(TAG, "Connected to GATT server.");
                Log.i(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.i(TAG, "Disconnected from GATT server.");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                List services = gatt.getServices();
                for (BluetoothGattService service : services) {
                    Log.d(TAG, "service: " + service.getUuid().toString());
                    for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
                        Log.d(TAG, "characteristic: " + characteristic.getUuid().toString());
                        if (characteristic.getUuid().toString().equals("00002a29-0000-1000-8000-00805f9b34fb")) {
                            if ((characteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_READ)>0) {
                                Log.d(TAG, "PROPERTY_READ");
                                mBluetoothGatt.setCharacteristicNotification(characteristic, false);
                                mBluetoothGatt.readCharacteristic(characteristic);
                            }
                            if ((characteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY)>0) {
                                Log.d(TAG, "PROPERTY_NOTIFY");
                                mBluetoothGatt.setCharacteristicNotification(characteristic, true);
                            }
                        }
                        for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
                            Log.d(TAG, "descriptor: " + descriptor.getUuid().toString());
                        }
                    }
                }
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            Log.d(TAG, "onCharacteristicRead()");
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, characteristic.getUuid().toString() + " " + new String(characteristic.getValue()));
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            Log.d(TAG, "onCharacteristicChanged()");
        }
    };

结束语

在研究Android的BLE使用时,第一个手机蓝牙硬件存在问题,导致代码未按预期执行,安装其他蓝牙调试App现象相同,后来更换手机后问题解决。

关键字:AT、指令、TCP
时间:2019年5月

指令

发:AT
收:OK

发:AT+CPIN?
收:+CPIN: READY
收:OK

发:AT+CSQ
收:+CSQ: 15,0
收:OK

发:AT+CGREG?
收:+CGREG: 0,1
收:OK

发:AT+CIPMUX?
收:+CIPMUX: 0
收:OK

发:AT+CIPMUX=1
收:OK

发:AT+CIPMODE?
收:+CIPMODE: 0
收:OK

发:AT+CGATT?
收:+CGATT: 1

发:AT+CGATT=1
收:OK

发:AT+CSTT?
收:+CSTT: "CMNET","",""
收:OK

发:AT+CSTT="CMIOT"
收:OK

发:AT+CIICR
收:OK

发:AT+CIFSR
收:10.113.91.97

发:AT+CIPSTART=1,"TCP","1.2.3.4",9999
收:OK
收:CONNECT OK

发:AT+CIPSEND=1,10
收:> 
发:abcdefhijk
收:1, SEND OK

收:+RECEIVE,1,6:
收:12345

发:AT+CIPCLOSE
收:CLOSE OK

发:AT+CIPSHUT
收:SHUT OK

注:
1、CSTT、CIPMUX、CIPMODE修改都需要在CGATT=1之前,或CIPSHUT之后;
2、CIPSHUT之后需CGATT=1,才能再次CIICR。

关键字:golang、发邮件
时间:2019年5月

代码

package main

import (
	"log"
	"net/smtp"
	"strings"
)

func main() {
	log.Println("start...")

	addr := "smtp.163.com:25"
	a := smtp.PlainAuth("", "user2019", "password", "smtp.163.com")
	from := "user2019@163.com"
	to := []string{"12345678@qq.com"}
	msg := "To: "
	msg = msg + strings.Join(to, ",")
	msg = msg + "\r\nFrom: " + from
	msg = msg + "\r\nSubject: " + "test5"
	msg = msg + "\r\n\r\n" + "This is a test mail."
	err := smtp.SendMail(addr, a, from, to, []byte(msg))
	if err != nil {
		log.Println(err)
		return
	}
}

注:smtp.PlainAuth的第二个参数是账户名,某些邮箱不需要加“@xxx.com”,某些邮箱需要加。

关键字:ssh、局域网、公网、反相连接
时间:2019年5月

原理

原理流程如下:
1、局域网主机先发起一个ssh请求登录公网主机,这个ssh登录不提供命令行输入,而是在登录的主机上打开一个tcp端口进行监听;
2、当有请求访问这个tcp端口时,数据会通过已经建立的连接转发到局域网主机上指定的端口,如果这个局域网主机上指定的端口是22端口,那么就实现了ssh登录到局域网。

准备

主机 IP
公网 1.2.3.4
局域网 192.168.1.100

公网主机

安装ssh服务端和客户端

root@www:~# apt-get install openssl-server
root@www:~# apt-get install openssl-client
root@www:~# adduser user1
...

注:由于会在局域网主机上建立免密登录,为保障公网主机安全,建议添加一个新用户用于远程登录。

局域网主机

只用ssh客户端本身是能够实现这个功能的,单次可以直接使用ssh。如果要长期保持能够远程登录,就需要用到autossh工具。

root@debian:~# apt-get install autossh
root@debian:~# ssh-keygen
...
root@debian:~# ssh-copy-id user1@1.2.3.4
...
root@debian:~# autossh -M 4567 -NR 8022:localhost:22 user1@1.2.3.4

注:
“-M 4567”是autossh监控会用到的一个端口,任意都可以只要不冲突;
“-NR 8022:localhost:22”是指在远端监听8022端口,并且8022端口来的数据转发到“localhost:22”;
“user1@1.2.3.4”是ssh访问的用户和主机

远程访问

在公网主机上

root@debian:~# ssh user1@127.0.0.1 -p 8022
password:

高级用法

主机 IP
公网 1.2.3.4
局域网X主机A 192.168.1.100
局域网Y主机B 192.168.2.110

在局域网主机A上执行脚本

root@hostA:~# autossh -M 4567 -NR 8022:localhost:22 user1@1.2.3.4

在公网主机上执行脚本

root@www:~# autossh -M 4568 -gNL 8023:localhost:8022 localhost

在局域网主机B上访问公网主机8023端口时,其实访问的是局域网主机A。

root@hostB:~# ssh root@1.2.3.4 -p 8023

关键字:zabbix、监控、股价
时间:2019年5月

一、准备

1、主机

一台Linux操作系统主机,内存1G以上,硬盘20G以上,能够访问Internet。

2、邮箱

a、发件邮箱

一个发件的邮箱,用于Zabbix发送提醒邮件。因为发件邮箱的密码会填写到Zabbix系统中,所以建议使用一个闲置邮箱或新申请一个邮箱作为发件邮箱,从而减少安全隐患。

b、收件邮箱

一个收件的邮件,用于接收Zabbix发送的提醒邮件。建议使用139邮箱作为收件箱,由于139邮箱可配置短信提醒,因此zabbix邮件提醒同时具备了短信提醒功能,并且短信提醒免费。

二、安装Zabbix

在准备的主机上安装Zabbix服务器。详细安装步骤请查阅https://www.zabbix.com,此处不做详细介绍。

三、配置

1、股票实时价格获取脚步

编辑文件/root/getprices.sh

#!/bin/sh
for i in $(wget -qO- http://hq.sinajs.cn/list=sz002008,sh600660|awk '{print $2}')
do
	echo `echo $i | awk -F '[\"]' '{print $2}'` > /tmp/`echo $i | awk -F '[_=]' '{print $3}'`
done

注:此脚本抓取股票实时价格,sz002008和sh600600是股票代码,可以自行添加,逗号隔开即可。此脚本执行后,会在/tmp目录下生成以股票代码为文件名的文件,文件内容即是价格相关数据。

2、定时执行脚本

root@debian:~# crontab -e
# m h  dom mon dow   command
30-59 9 * * 1,2,3,4,5 /bin/sh /root/getprices.sh
0-30 11 * * 1,2,3,4,5 /bin/sh /root/getprices.sh
*/1 10,13,14 * * 1,2,3,4,5 /bin/sh /root/getprices.sh
:wq

注:每周一至五,开盘时间9:30~11:30和13:00~15:00每分钟抓取一次数据。

3、Zabbix Agent采集数据

root@debian:~# vim /etc/zabbix/zabbix_agent.conf
...
UserParameter=stock.price.code[*],awk -F ',' '{print $$4}' /tmp/$1
UserParameter=stock.chg.code[*],awk -F ',' '{print 100*($$4-$$3)/$$3}' /tmp/$1
...
:wq
root@debian:~# systemctl restart zabbix-agent

4、Zabbix服务器配置监控项

* 打开zabbix的web页面,http://192.168.1.100/zabbix,用户名Admin,密码zabbix。
* 点击Configuration(配置)-> Hosts(主机)-> Zabbix Server/Items -> Create Item
* 主要参数
Name: 600660 price
Key: stock.price.code[sh600660]
Type of information: Numeric(float)
Update interval: 60s
注意:Key需要和UserParameter对应起来。

* 点击“`Add“`按钮

5、Zabbix服务器配置触发器

* 打开zabbix的web页面
* 点击Configuration(配置)-> Hosts(主机)-> Zabbix Server/Items -> Create trigger
* 主要参数
Name: sh600660 price > 23.00
Problem expression: {Zabbix server:stock.price.code[sh600660].last(#1)}>23.00
* 点击“`Add“`按钮

6、Zabbix服务器配置邮件提醒

a、配置发件邮箱

* 依次点击Administration(管理)-> Media types(媒介)-> Email
* 填写邮箱相关信息
* 点击“`Update“`按钮

b、配置发邮件动作

* 依次点击Configuration(配置)-> Actions(动作)-> Report problems to Zabbix administrators
* Enable打勾
* 点击“`Update“`按钮

c、配置管理员账户邮箱

* 依次点击Administration(管理)-> Users(用户) -> Admin
* 点击“`Media“`标签页
* 点击“`Add“`链接,添加邮箱
* 点击“`Update“`按钮保存

结束

整个配置已经完成,当600660的价格高于23元时,你就会收到邮件了。

关键字:sim800c、eat、串口、下载、升级
时间:2019年4月

关于SIM800C下载

SIM800C支持USB下载和UART下载两种方式。

UART下载

硬件链接

1、SIM800C有两个UART接口,下载使用UART1接口,UART1的RXD、TXD分辨接USB的TXD、RXD;
2、SIM800C的5V和GND与串口5V和GND相连;
3、SIM800C的PWRKEY已接开关或直接接GND。

下载步骤

1、断开SIM800C的5V供电;
2、打开Simcom Series download Tools Customer v1.19
3、依次选择SIM800C、UART、COM3(选自己串口号)、115200
4、点击Image Folder,选择文件cfg文件,例如:1418B06SIM800C24.cfg
5、点击Start Download
6、按住PWRKEY或保持PWRKEY接地
7、接通SIM800C的+5V
8、松开PWRKEY或断开PWRKEY接地
9、等待下载完成

注意

必须先点击Start Download,再接通SIM800C电源,并保证接通时PWRKEY处于下拉状态,发否无法下载。

关键字:sim800、sim900、simcom、eat、sdk
时间:2019年4月

SIM800与SIM900

SIM900已经停产,由SIM800系列相关型号代替

SIM800系列

2G产品

产品型号:SIM800、SIM800A、SIM800L、SIM800C、SIM800F、SIM800H等
SIM800x所有型号主要功能相同,封装不同,接口也有一定差异(蓝牙、PWM、SD卡等)

2G+定位产品

产品型号:SIM808、SIM868、SIM868E等

开发

方式一,外接串口

通过串口,外接单片机或者PC,使用AT指令进行编程。

方式二,Embedded AT(EAT)

不需要外接单片机或PC,直接使用C语言进行编程,使用SIMCom提供的工具进行编译和下载,程序直接运行在SIM800系列上。