分类 编程相关 下的文章

用Pro Micro做了个摇步器,感觉有点浪费。于是入手了个ATTiny85开发板(国外好像叫Digispark),计划把Pro Micro替换出来。

这个ATTiny85开发板非常迷你和便宜,面积比邮票还小(大约2cm*2.5cm),价格不到7rmb/个。自带Micro USB母口(插上USB就可以烧录程序),除了5v VCC、GND、VIN,还有6个针脚。采用ATTiny85芯片,集成8位CPU,主频最高20MHz,内存512B,闪存8KB等等。更详细的参数,参考以下PDF文档:
https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf

总体来说这,非常适合细小的项目。但是这货历史有点久远(网上很多资料都是2013年左右),相关资料不好找。踩了一些坑后,终于成功刷入程序并运行。

首先安装 Arduino IDE,版本是1.6以上,然后根据以下文章去配置:
Connecting and Programming Your Digispark
https://digistump.com/wiki/digispark/tutorials/connecting

遇到Linux的问题,主要是需要相关的安装包,可参考这个链接:
Linux Troubleshooting
https://digistump.com/wiki/digispark/tutorials/linuxtroubleshooting

然后,关于编写舵机控制程序,尝试了几个库,最后采用了SoftRcPulseOut并运行成功。参考以下链接:
Beginner Servo
https://digistump.com/board/index.php?topic=1157.0
Digispark (Attiny85) servo tester
http://www.circuitdb.com/?p=1203

最后再说说摇步机。摇步机的原理就是利用舵机进行单摆运动。原来使用手机摇的,现在入手了个二手小米手环2,解放了手机。接线图就不上了,简单描述如下:

ATTiny85 -> 9G舵机(SG90)
5V       -> 红(正极)
GND      -> 棕(负极)
P4       -> 橙(信号)

程序也很简单,舵机先复位到0度,再不断重复从0度转到120度后复位。代码如下:

#include <SoftRcPulseOut.h> 

SoftRcPulseOut myservo;
int pos = 0;
#define NOW  1

void setup() {
  myservo.attach(4); // P4,舵机信号
  myservo.setMaximumPulse(2200);

  for (int i = 0; i < 5; i++) { // 复位到0度
    myservo.write(pos);
    delay(100);
    SoftRcPulseOut::refresh(NOW);
  }
  delay(3000);
  SoftRcPulseOut::refresh(NOW);
} 

void loop() {
  for (pos = 0; pos < 120; pos += 20) {
    myservo.write(pos);
    delay(50);
    SoftRcPulseOut::refresh(NOW);
  }
  
  for (pos = 120; pos >= 0; pos -= 20) {
    myservo.write(pos);
    delay(50);
    SoftRcPulseOut::refresh(NOW);
  }
}


公司网站需要添加导出CSV文件的功能。一开始想用模板文件的方式导出,但是需要读取模板、分析需要替换的标识、格式化字符串、写文件等,太麻烦了。由于是web导出,就想用JSP实现。

首先,需要简单了解CSV文件规范:
1)可以采用UTF-8字符集编码,但是要带BOM,否则Excel打开后,Unicode字符会乱码。

2)一行一条数据,空行会当作一行空数据。同一行的各个数据之间用逗号分隔,每行的数据个数可以不相同。

3)每个数据用双引号括住,可以避免绝大部分的符号问题。比如在双引号里的换行符和逗号不会被解析。唯一需要转义的是双引号,转义符也是双引号。就是双引号里出现的每个双引号,要换成两个双引号。

示例代码:

<%@ page language="java" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" contentType="application/x-download"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<% 
    response.addHeader("Content-Disposition","attachment;filename=ShoppingCart.csv"); // 导出的文件名
    out.print("\ufeff"); // UTF-8的BOM
%>"Name","Sex","Score"
<c:forEach items="${students}" var="student">"${fn:replace(student.name, "\"", "\"\"")}","${student.sex}","${student.score}"
</c:forEach>

简单解析:
1)第一行的trimDirectiveWhitespaces="true",设置自动清除空行。即多个<% %>行,执行完毕后只保留最后一个%>后面的内容,包括换行符。这个清除空行是不完全的,但这样设置最简单。

2)第一行的contentType="application/x-download",设置浏览器可识别的文件类型。这个设置,浏览器打开链接后会自动下载。

3)taglib引入需要用到的标签,例如JSTL或自定义的标签。

4)response.addHeader("Content-Disposition","attachment;filename=ShoppingCart.csv");是设置导出的文件名。

5)out.print("\ufeff");输出UTF-8的BOM。

6)剩下的就是文件内容了。注意换行的问题就可以了。

年初,检查公司某个网站的后台日志,发现圣诞节前夕的登录出错日志暴涨。检查了一下,绝大部分是Email错误,然后断定是黑客拿着一堆Email和密码在撞库。

简单统计了一下,网站的日志,超过200万条登录错误数据,一秒大概7~10个请求。第一反映是图形验证码被破解了,而且大概10秒就分析出来。

幸好Nginx有Access Log。请求的行为是,先访问网站主页,再访问登录页,最后调用登录按钮的请求,检查Email密码是否正确。随机挑选了几个相关IP,都是泰国的。由于没有记录User Agent,所以不知道黑客是用什么程序或者爬虫,不能进一步分析并排除相关访问来源。另外,再细心分析,相关的撞库访问记录,都没有访问获取图形验证码。那就是,黑客根本就没有破解验证码。检查了代码,网站登录时,同一Email输入三次密码错误后才要求输入验证码。而黑客是利用一堆IP,输入不用的Email,所以,算是绕过了验证码。

最后,目前的处理是同一IP,登录错误3次后,就需要输入验证码。其实图形验证码也不是想象中那么容易破解,就可以防一防。但是,这不是一劳永逸的。

在V2上跟网友沟通了一下,总结一些方案:

1)升级验证码,采用更强更复杂的验证码。但是复杂的验证码(例如扭曲的字符串),对用户不友好的,所以Google推出了reCAPTCHA v3。reCAPTCHA v3无需用户进行任何操作,Google会分析其行为并打分,让网站程序自己根据评分进行处理。比如评分为0.9则认为是人类,0.4分则要进一步验证之类。

2)登录限制规则。比如过滤掉某些有问题的IP(通过第三方接口判断或网站本身记录),过滤有问题的来源(通过User Agent识别),登录错误若干次后进行限制等等。

3)采用二步验证。包括但不限于手机短信验证码、Email验证链接、Google的身份验证器、WebAuthn标准的方案等等。

近来迁移了自家的服务器,顺便记录一下Flask项目的部署。

这里采用Nginx + Supervisor + Python3 + uWSGI + Flask的方案。其中建议把uWSGI替换成Gunicorn,据说采用纯Python实现的Gunicorn,更方便打包为Dockor镜像,并且性能几乎跟Supervisor一样。这个留待以后再研(折)究(腾)。

新服务器采用Debian 9,部署过程参考以下文章:
Flask+uwsgi+Nginx部署应用
https://www.jianshu.com/p/84978157c785


1. 安装项目虚拟环境

假设项目文件夹为`/opt/flask_proj`,安装命令如下:

sudo apt update
sudo apt install python3 python3-pip
sudo pip3 install virtualenv
cd /opt/flask_proj
virtualenv venv
pip3 install -r requirements.txt

其中:
1)安装virtualenv需要用root用户,否则不会安装到/usr/local/bin/virtualenv,并且需要自行添加到path。
2)requirements.txt为flask项目所需的库。这个要看项目是否需要安装。
3)如果是迁移项目,可以在迁移前生成requirements.txt文件:pip3 freeze > requirements.txt


2. 安装uWSGI

如果requirements.txt里已包含uWSGI,则不用重复安装。安装命令如下:

pip3 install uwsgi

在项目文件夹下,新建uWSGI配置文件config.ini,参考内容如下:

[uwsgi]
master = true
home = venv
wsgi-file = manage.py
callable = app
socket = :5001
processes = 4
threads = 2
buffer-size = 32768


3. 安装supervisor

安装命令如下:

sudo apt-get install supervisor

在文件夹/etc/supervisor/conf.d下新建项目对应的配置文件,例如flask_proj.conf,参考内容如下:

[program:flask_proj]
# 启动命令入口
command=/opt/flask_proj/venv/bin/uwsgi /opt/flask_proj/config.ini
# 命令程序所在目录
directory=/opt/flask_proj
# 运行命令的用户名
user=user
autostart=true
autorestart=true
# 日志地址
stdout_logfile=/opt/flask_proj/logs/uwsgi_supervisor.log

启动supervisor服务:

sudo service supervisor start

4. 安装Nginx

一般安装系统自带的版本就够用了:

sudo apt install nginx

在文件夹/etc/nginx/sites-available下新建配置文件flask_proj,参考内容如下:

server {
    listen 443 ssl http2 default_server;
    #listen [::]:443 ssl;
    server_name www.abc.xyz;

    ssl_certificate /etc/letsencrypt/live/www.abc.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.abc.xyz/privkey.pem;

    index index.html index.htm;

    gzip            on;
    gzip_min_length 1k;
    gzip_buffers    4 16k;
    #gzip_proxied    expired no-cache no-store private auth;
    gzip_comp_level 5;
    gzip_types      text/html text/css text/javascript text/json text/plain text/xml application/javascript application/json application/soap+xml application/x-javascript application/x-www-form-urlencoded application/xhtml+xml application/xml;

    location / {
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:5001;
        uwsgi_param UWSGI_PYHOME /opt/flask_proj/venv;
        uwsgi_param UWSGI_CHDIR /opt/flask_proj;
        uwsgi_param UWSGI_SCRIPT manage:app;
        uwsgi_read_timeout 100;
    }  
}

注意:
1)这里只配置了https,相关的SSL证书,可以到https://letsencrypt.org/免费申请。
2)开启了gzip压缩。

最后添加链接文件,并重启Nginx:

sudo ln -s /etc/nginx/sites-available/flask_proj /etc/nginx/sites-enable/flask_proj
sudo service nginx restart



从G1时代开始,就了解到因为Android使用Linux内核,可以利用chroot运行大量Linux发行版。但是由于当时ARM CPU性能低下及内存不足,一般只能使用Terminal字符界面,或者ssh过去。然后升级过设备,并装上了“XServer XSDL”(Android上的Xserver)用来体验图形界面,但是手机屏幕太小,一弹出虚拟键盘就基本把桌面挡住了。后来想起X Window是基于客户端/服务器模式的,应该可以用PC电脑之类提供Xserver,显示手机上的Linux图形界面。最后终于弄明白了配置,出来的效果还是不错,至少可以用浏览器流畅播放视频了。

所需设备
S机,用于提供X Server的设备,最好是屏幕比较大的PC电脑(台式机或笔记本)。系统最好是Linux,装上X11。后来发现Windows也可以,因为有Xming。当然,Android也是可以,因为有XServer XSDL。这里只记录Linux的。

A机,Android设备(手机或平板),运行Linux发行版的X Client。Android上有很多装Linux的应用了,这里推荐Linux Deploy,因为这是github.com上的开源项目。装Linux的步骤不详述,这个应用已经做得很好了。

网络设置
只要两个设备在同一局域网内就可以了。下面列出几种方式:
1)使用有线/无线路由器组建局域网。路由器是性能瓶颈,特别是A机无线连接到路由。
2)A机分享无线网络,S机连过去。缺点是只能利用A机的移动网络上网,费钱……
3)S机分享无线网络,A机连过去。S机需要装个无线网卡,并成为性能瓶颈。
4)A机开启开发者模式,通过USB线连接S机。S机利用ADB命令的forward功能映射端口。ADB命令成为性能瓶颈。
5)A机分享有线网络,通过USB线连接S机。这个方案性能最佳,且使用设备最小,又不影响A机的网络。但A机可能会出现发热的情况。

X Server设置
这一步花了很多时间。理解后就是S机上的一个命令和一个配置文件,即如何启动X Server和设置验证。相关原理可查Google,这里设置X Server为:1.0(从0开始算,第2个服务),默认使用6001端口。
1)利用xhost设置可访问X Server的客户端IP。在S机上修改配置文件/etc/X1.hosts,若不存在则新建。把A机的IP地址填进去,并保存。
2)运行以下命令启动X Server:

sudo Xorg :1.0 -listen tcp

X Client设置
A机上需要装好桌面环境。推荐LXDE吧,轻量。启动前,连好网络,在Linux Deploy上设置:

图形界面 -> 勾选启用
图形子系统 -> 选X11
图形界面设置 -> 显示编号 -> 1.0
           -> X服务器地址 -> 填写S机的IP地址
           -> XServer XSDL -> 不要勾选
桌面环境 -> 选LXDE

然后启动Linux即可(实在太方便了)。对应的命令就是:

export DISPLAY=S机_IP:1.0
startlxde

总结
A机的性能越好,并且S机的性能越差,这个方案的实用性就越高。比起微软的“Continue on PC”,三星的DeX,Superbook等方案,便宜很多,并把旧电脑利用起来。使用过程中,不会影响A机的来电、通知等日常用途。但是目前来看,实用性确实不高。后面看看能不能用Raspberry Pi Zero + 显示屏 + 大容量移动电源来作为S机,以提高便携性。

CFO离职,捡了个烂电脑回来(CPU是i3-4000M,第4代酷睿,内存4G)。比10年前的酷睿T2450好太多了,而且是64位系统。前段时间有新闻报道说,Google的内部系统Goobuntu,准备抛弃Ubuntu,直接基于Debian。于是也想试试直接装Debian来用。其实用习惯了Lubuntu,只要桌面也装LXDE,应该用起来没什么区别。
第一次装Debian,发现居然连下载哪个ISO都不知道。一开始下个live CD的iso文件,发 现虽然启动界面有选项可以安装系统,但是按抓给你过程要检查CD-ROM。没用光盘的话,不能继续进行安装。后来才明白要用netinst的iso。

iso文件制作U盘启动盘很简单,只要两行命令。其中sdX是U盘的设备文件名。如果U盘已挂载,要先卸载,再执行

$ sudo cp debian-9.4.0-amd64-netinst.iso /dev/sdX
$ sudo sync

后面的安装步骤基本按着提示一步一步进行。有个文章写得挺好的,可以参考:
如何安装 Debian 9?
http://scottming.com/2017/08/06/how_to_install_debian9/

有两个步骤需要说明一下:
第一,需要找无线网卡的firmware,放在另一个U盘,再插到电脑上。安装程序会自动设别并安装。这是为了后面安装软件时连上网络。或者可以使用有线连接吧。

第二,到了分区那一步,不能按默认的整个硬盘分区。因为要保留原来的Windows系统,所以只能选择手动操作。而且第一次搞LVM分区,完全不懂操作。后来参考了这个文章才能顺利搞掂:
ubuntu 12.10 安装 LVM分区(图文)
http://blog.sina.com.cn/s/blog_56a70c0401018dki.html

简单来说,要注意几点:
1)由于GRUB Legacy不支持LVM,所以无法在LVM上创建/boot分区。也就是除了/boot分区,其它分区都可以在LVM上创建。
2)先把分区设置为LVM分区,再点LVM分区管理的选项进行详细设置。
3)LVM分区管理,先创建逻辑卷组,再创建逻辑卷,多个逻辑卷就等于是多个分区。
4)最后把逻辑卷设置对应的挂载点。

最后总算是顺利安装完毕。

后记
用了一段时间后,终于明白Ubuntu的存在价值了。Debian 9即使装上LXDE,还是有一些不顺手,例如屏幕亮度调节。后来还是换上Lubuntu。

搞了几天,终于弄懂了自建Git服务。正如绝大多数的教程所说,基于ssh来部署git服务,是最简单的。

1)服务器和客户端都安装Git。

sudo apt-get update
sudo apt-get install git

2)服务器上建立Git仓库。我比较懒,就用现有的用户,比如用户名为:user。用user登录后,执行命令。git init --bare是用于建立裸仓。执行成功后,会建立project.git文件夹,确保user对该文件夹有读写权限。

mkdir /path/git
cd /path/git
git init --bare project.git

3)客户端clone项目。ssh://是指,基于ssh的git服务。user就是用户名。server_ip就是服务器ip,也可以是域名。22是ssh服务的端口,根据部署的ssh服务而定。/path/git/project.git就是前面建立的项目路径。

git clone ssh://user@server_ip:22/path/git/project.git

然后就可以愉快地各种Git操作了。

至于后面增加安全性,可以新增一个git用户,设置其不能登录shell,并采用证书登录等等,可以参考网上的教程。例如:
廖雪峰的官方网站:搭建Git服务器

手上有个远古时代的上网本,华硕(ASUS) EeePC 900。删掉原来的Windows XP,装上Lubuntu 16.10。原电池废了,淘宝换了个新的,续航能达到5小时(大概吧)。但是一般用来远程ssh到raspberry pi,没必要开启图形界面(特别是发现用screen可以同一界面打开多个shell)。需要解决的问题是中文的显示和输入。Google过后,最终的解决方案是fbterm(完美显示中文) + fcitx-fbterm(实现中文输入)。

fbterm的安装参考这个(英文):
https://fcitx-im.org/wiki/Fbterm

中文版本的完整安装教程,参考这个:
http://ikuduku.com/blog/fbterm-display-and-input-Chinese-in-tty

关于中文输入法,我选择google拼音(fcitx-googlepinyin)。记录一下相关操作:

1)安装

#安装相关软件
sudo apt-get install fbterm fcitx fcitx-fbterm fcitx-googlepinyin fcitx-configtool
#让fbterm可以设置快捷键,主要是切换输入法
sudo setcap 'cap_sys_tty_config+ep' /usr/bin/fbterm
#把当前用户加入到video组
sudo gpasswd -a <your username> video

2)编辑当前用户的配置

vi ~/.fbtermrc

修改如下:

#设置字体
font-name=Mono
#设置字体大小
font-size=14
#设置输入法
input-method=fcitx-fbterm

3)设置开机启动

#设置开机进入shell界面,这是Ubuntu 16.04及以后版本的设置方法
sudo systemctl set-default multi-user.target
#设置进入图形界面的命令,然后用`sudo startx`命令即可进入图形界面
sudo sh -c "echo \#\!/bin/sh > /usr/local/bin/startx"
sudo sh -c "echo systemctl start lightdm >> /usr/local/bin/startx"
sudo chmod +x /usr/local/bin/startx
#设置进入shell后自动启动fbterm,而图形界面的shell则不会启动fbterm
printf '\nif [ $TERM = 'linux' ]; then\n  fcitx-fbterm-helper -l\nfi' >> ~/.bashrc

大概就这样了。使用过程中,还是有一点不习惯。例如快捷键的变换,Ctrl + Alt + F1 ~ F6 不能切换tty,而是切换字符编码,使用 Ctrl + Alt + 1 ~ 0 可以切换10个窗口。

一般一个月一次整理孩子的照片。但是老婆的手机和单反拍出来的照片,文件名都不统一。要统一采用拍照时间做为文件名的话,比较麻烦。本来想用Python3写个脚本处理的,但是官方没有提供获取EXIF信息的方法。找打其它包,也不怎么好用。但是今晚发现原来有个shell命令挺好用的,就是 EXIF。

于是就写了个shell脚本,如下:

#!/bin/bash

path=/home/fox/pics

i=$(ls $path/*.JPG)

for j in $i
do
  d=$(exif -t "DateTime" $j | grep Value)
  d=${d/  Value: /IMG_}
  d=${d//:/}
  d=${d// /_}
  $(mv $j ${d}.JPG)
done

这里采用的统一文件名格式是IMG_yyyyMMdd_HHmmss.jpg。由于采用字符串替换的语法,所以只能用bash运行。

某日发现了提供免费SSL证书的网站——www.sslforfree.com,立马手动申请了个证书来玩。部署以后,非常感动,重点是Chrome和FireFox都支持!曾在某知名网站申请过免费证书,总是申请失败。心灰意冷,本来准备买个廉价的付费版,幸好发现这网站!

免费虽好,但有效期只有3个月,而且到期后只能手动更新证书。不过找到提供该免费证书的网站——Let’s Encrypt(https://letsencrypt.org),以及该网站提供的自动更新证书的开源软件certbot(https://certbot.eff.org)。certbot首页就有安装教程,对应不同HTTP服务和操作系统。安装完毕后,执行以下命令,就可以获取证书了

#验证域名并获取SSL证书
sudo certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com

最后,设置个自动更新,就安枕无忧了。

PS.既然有了免费的证书,何不部署个HTTP 2.0?