关于如何把手机改装成无电池运行,已经研究好久了。当时的想法是,无电池运行的手机,可以长期使用外部供电而不怕内部电池损坏,从而实现很多的玩法。例如迷你服务器、录像监控等。但是实现后,发现还是那个经验教训:做得到不一定能做好,专业的事情还是交给专业的设备。Anyway,还是记录一下实现的过程。

首先,以下操作具有一定的危险性!锂电池包装破损的话,一接触到空气就会着火。

总的来说,实现方法有两个,但原理都是一样。具体可以参考这两个文章:

1)手机改装无电池工作(法拉电容替代电池)[教程]
https://www.luoji.men/2138.html

2)手机改装无电池工作(5V适配器替代电池)[教程]
https://www.luoji.men/2090.html

原理都是把手机电池(不管是可拆卸的或不可拆卸的),分解为电池保护板和锂电池两部分,然后用外部电源代替锂电池。注意接线的正负极不要接反。

方案一:用超级电容代替锂电池,手机充电口再接5V 2A以上的充电器。

所需电容: 法拉电容组合型5.5V 1.0F(可给手机亮屏续航4秒)
https://item.taobao.com/item.htm?id=622910347656

优点:电容可以在手机断电时,保持一定的亮屏时间。

缺点:每次开机前,手机要接上充电器,先给电容充电(起码30秒)。另外,手机充电口接上充电器,就不能接外部设备了(但是有的手机可以接扩展接头)。

方案二:用5V 2A以上的充电器代替锂电池。

优点:开机快,接电就能开机。而且手机充电口空出来,可以接其它设备。

缺点:充电器一定要接稳,线一松就会断电,手机关机。

最后,对于目前的手机,最好还是使用5V 3A以上的充电器充电,否则会导致不能开机,或者死机。

这个周末,把N年前买的,都是基于ESP8266的小板子(ESP-01和基于ESP-12E的NodeMCU)拿出来玩了下,发现还有可玩性。主要是刷上MicroPython,就带来更多玩法。以前写过相关的Blog文,现在看着,觉得没用,于是重写一篇。

1. ESP8266硬件

可以简单理解为支持2.4GHz WiFi的廉价、低功耗芯片。详细的特点,参考官方文档:
https://www.esp8266.com/wiki/doku.php?id=feature_set

官方Wiki,基本想了解的都可以在这里找到:
https://www.esp8266.com/wiki/doku.php?id=start

ESP8266的硬件家族介绍:
https://www.esp8266.com/wiki/doku.php?id=esp8266-module-family

有人做了个对比:
ESP8266 module comparison: ESP-01, ESP-05, ESP-12, ESP-201, Test Board and NodeMCU
https://blog.squix.org/2015/03/esp8266-module-comparison-esp-01-esp-05.html

简单来说,一般入门的,推荐购买NodeMCU或者D1 Mini这种带USB转TTL芯片的,接上电脑就可以开发了。像ESP-01这种简单且廉价的,最好作为硬件项目的模块来用,普通玩家不要像我这样贪便宜去购买。因为开发时,ESP-01需要USB转TTL来连电脑,实际使用时也需要3.3V的电源来启动。

NodeMCU开发板官网:
https://www.nodemcu.com/index_cn.html

D1 mini官方文档:
https://www.wemos.cc/en/latest/d1/d1_mini.html

2. 选择固件(Firmware)

还是先看官方WiKi的介绍:
Loading new firmware onto an ESP8266
https://www.esp8266.com/wiki/doku.php?id=loading_firmware

能玩的固件,有3个:

1)AT命令固件(AT-Command firmware),适合ESP-01这种作为模块嵌入到硬件项目。其它硬件使用AT命令操控ESP8266来使用WiFi联网。

2)NodeMCU固件,基于Lua语言的,NodeMCU官方网站有简单介绍。另外,其固件需要从定制网站选择模块,build完后,会发Email并提供下载。

NodeMCU固件定制网站
https://nodemcu-build.com/

3)MicroPython,基于Python3语法作为开发语言的固件。一般需要2MB以上空间,NodeMCU板基于ESP-12E,内部空间(flash)是4MB,可以刷最新版本。ESP-01内部空间只有1MB,能刷v1.12或以下的版本,或者刷定制针对1MB的定制版。但是ESP-01运行MicroPython不稳定,网上有说是硬件的问题。所以ESP-01最好不要刷MicroPython。

MicroPython官网:
https://micropython.org/

MicroPython源码:
https://github.com/micropython/micropython

MicroPython针对ESP8266的资料,包括刷机和开发文档:
MicroPython - Quick reference for the ESP8266
https://docs.micropython.org/en/latest/esp8266/quickref.html

MicroPython针对ESP8266的固件下载:
MicroPython Firmware for Generic ESP8266 module
https://micropython.org/download/esp8266/

3. 刷入固件

不管刷什么固件,都是一样的操作。先下载固件文件,再安装刷机工具。刷固件前,先擦除所有数据,再刷固件。网上有很多教程,包括不同操作系统,图形化工具或者命令行操作。下面以Ubuntu为例,默认连接在/dev/ttyUSB0,使用esptool.py作为工具操作。

sudo apt update
sudo apt install python3-pip
sudo pip3 install esptool #安装esptool.py
sudo esptool.py --port /dev/ttyUSB0 erase_flash #擦除所有数据
sudo esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect 0 firmware_file_name.bin #刷入固件

4. 调试系统

命令行调试的话,Windows上可以用PuTTY(需要装对应TTY驱动),Linux(例如Ubuntu)上可以用minicom或者screen命令连上去。

screen命令的格式是screen -S 名称 /dev/接口名称 波特率,其中名称是方便暂时断开后再进入,即如下:

sudo screen -S esp /dev/ttyUSB0 115200

minicom命令,可以设置参数,直接连上,如下:

sudo minicom -D /dev/ttyUSB0 -b 115200

也可以不设参数,直接使用sudo minicom,进去后设置。具体操作是
-> 按[Ctrl] + [A]
-> 按[Z]
-> 按[O],即选择“cOnfigure Minicom..O”
-> 选“Serial port setup”
-> 弹出菜单按如下设置:

+-----------------------------------------------------------------------+
| A -    Serial Device      : /dev/ttyUSB0                              |
| B - Lockfile Location     : /var/lock                                 |
| C -   Callin Program      :                                           |
| D -  Callout Program      :                                           |
| E -    Bps/Par/Bits       : 115200 8N1                                |
| F - Hardware Flow Control : No                                        |
| G - Software Flow Control : No                                        |
|                                                                       |
|    Change which setting?                                              |
+-----------------------------------------------------------------------+

-> 选“Save setup as dfl”,回车
-> 选“Exit”,回车
然后就进入控制台了。

5. 开发工具

一般推荐ESPlorer,基于Java的开源图形化开发工具。该工具比较全面,三个固件都支持,可以用来做调试,也可以做开发,上传文件。本来想写下一些基本测试代码,比如连上WiFi之类,但是该工具都提供了对应的按钮,啥都不用写了。

ESPlorer官网:
https://esp8266.ru/esplorer/

ESPlorer源码:
https://github.com/4refr0nt/ESPlorer

XDA论坛上看了很久,Android 11就只有crDroid有官方支持。加上Resurrection Remix OS用得真不爽,主要是自动杀进程,而且不分前后台。于是五一假期狠下心刷上crDroid,用了一段时间,很爽!流畅、不杀进程、支持国产支付app的指纹,续航还行。

注:目前此ROM处于无人维护状态!

1. 相关下载

1.1 crDroid ROM
官网不仅可以下载ROM,还提供小米官方固件(欧版MIUI)、Recovery、OpenGapps等的链接:
https://crdroid.net/picasso

1.2 欧版MIUI
目前最新版是12.1.8,据说今年6月会推出12.5适配。当然,这不是重点。下载地址:
xiaomi.eu_multi_HMK305G_V12.1.8.0.RGICNXM_v12-11.zip
https://sourceforge.net/projects/xiaomi-eu-multilang-miui-roms/files/xiaomi.eu/MIUI-STABLE-RELEASES/MIUIv12/

1.3 Recovery
crDroid推荐的Recovery是OrangeFox。界面跟Twrp非常不同,感觉更像是文件管理器,但功能还是一样。
OrangeFox-picasso-stable@R11.1_1.7.zip
https://orangefox.download/zh-CN/device/picasso

1.4 Margisk
目前几乎是获取root权限的唯一方案。下载地址:
https://github.com/topjohnwu/Magisk/releases

要注意,从Margisk 22开始,root安装包和root管理工具,都合并为一个apk文件。root系统时,只需用Recovery刷入该apk文件。

1.5 Gapps
由于OpenGapps的Android 11版还没正式发布(当前处于测试),所以我选用NikGApps。下载链接:
https://nikgapps.com/downloads.html#downloads

一般选在Core版就可以了。值得注意的是,如果要开启Google Assistant(Google助手),需要把Google Velvet(即Google搜索)安装为系统App。NikGApps提供了对应的Addons(文件名为:NikGapps-Addon-11-Velvet-signed.zip),可以通过Recovery直接刷入系统。

2. 刷机过程

2.1 解锁及刷Recovery
都是常识性操作。刷Recovery后,最好先备份当前系统。

2.2 备份数据
钛备份不再更新了,推荐使用OAndBackupX备份App及数据。可通过F-Droid安装:
https://f-droid.org/en/packages/com.machiav3lli.backup/

还有联系人和短信的备份,不再赘述。

2.3 刷机步骤
按刷机顺序排列,crDroid的方案是:OrangeFox -> 欧版MIUI -> crDroid -> Margisk -> OpenGapps。我的方案只是用NikGApps替换OpenGapps。另外,最好先“双清”再开刷。

3. 初始设置

3.1 去掉WiFi和信号出现“x”符号
需要使用adb执行以下命令:

adb shell settings put global captive_portal_mode 0
adb shell settings put global captive_portal_https_url https://www.google.cn/generate_204

命令执行成功后,手机开启飞行模式,然后关闭飞行模式,即可解决。
参考:https://www.uso.cn/post/view/47990

3.2 开启Google Assistant(可选)
初次进入系统后,不插入SIM卡,语言选English(US)。安装Google Assistant并设置开启。成功后,再插入SIM卡,并设语言为中文。

Eclipse虽老,但仍然好用,特别是对于老Java码农来说。

1. 版本选择

一般Java的Web开发,选择Eclipse IDE for Enterprise Java and Web Developers。官方下载链接如下:
https://www.eclipse.org/downloads/packages/

2. 实用的初始化设置

1)修改默认字符集为UTF-8

windows -> Preferences -> General -> Workspace -> Text file encoding -> 选UTF-8

2)修改默认字体,改善中文显示太小

windows -> Preferences -> General -> Appearance -> Colors and Fonts -> Basic -> Text Font -> Edit -> 选Courier New

3)代码自动提示功能失效的解决方法

Window -> Preferences -> Java -> Editor -> Content Assist -> Advanced -> Select the proposal kinds contained in the 'default' content assist list -> 勾选"Java Non-Type Proposals"、"Java Proposals"、"Java Type Proposals"三个选项

4)在输错方法后仍然出现代码自动提示

Window -> Preferences -> Java -> Editor -> Content Assist -> Auto Activation triggers for java -> 改为.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

5)禁用Language Servers,提高Eclipse性能

Window -> Preferences -> Language Servers -> 取消所有勾选

6)安装插件Quick Bookmarks plugin,快速的标记和访问书签

  • 安装:Help -> Eclipse Marketplace -> 搜“Quick Bookmarks plugin”并安装
  • 使用:Alt+[数字],标记书签。Alt+Shift+[数字],跳转到书签。

工作上遇到需要在浏览器展示PDF文件,于是找到了Mozilla开发的PDF.js。玩了一下,结论是,直接使用其自带的示例是简单的方法。

PDF.js的官网:https://mozilla.github.io/pdf.js/
github项目页面:https://github.com/mozilla/pdf.js

主要是没找到有用的开发文档,而自己也不会Node.js的开发。所以尝试自己写的页面,只能利用HTML 5的canvas展示PDF内容。官方自带的示例,会把PDF转换成HTML,并且有显示目录、缩略图、打印、等各种实用的功能,媲美很多完整的PDF阅读软件。

这里记下写过的页面,可以翻页、放大缩小:

<h4>显示PDF</h4>
<p>
    <button id="prePage">上一页</button> <input type="text" id="curPage" value="" readonly /> <button id="nextPage">下一页</button>
    <br />
    <button id="zoomIn">+</button> <input type="text" id="zoomScale" value="1" readonly /> <button id="zoomOut">-</button>
</p>
<p>
    <canvas id="the-canvas" style="border: 1px solid black; direction: ltr;"></canvas>
</p>

<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="/js/pdfjs/pdf.js"></script>
<script>
//加载PDF按钮
pdfjsLib.GlobalWorkerOptions.workerSrc = '/js/pdfjs/pdf.worker.js';//自己的路径

var loadingTask = pdfjsLib.getDocument('/pdfjs/docker.pdf'); 
var pdfDoc = null;
var totalpage = 0;
var curPage = $("#curPage");
var zoomScale = $("#zoomScale");

loadingTask.promise.then(function (pdf) {
    //加载指定界面(第一页)
    pdfDoc = pdf;
    totalpage = pdfDoc.numPages;
    getPage(1);
});

function getPage(pageNum) {
    if(pageNum < 1) {
        pageNum = 1;
    } else if(pageNum > totalpage) {
        pageNum = totalpage;
    }
    setCurPageNum(pageNum);
    pdfDoc.getPage(pageNum).then(function (page) {
        //var scale = 1;
        var viewport = page.getViewport({ scale: getZoomScale() });
        var canvas = document.getElementById('the-canvas');
        var context = canvas.getContext('2d');
        canvas.height = viewport.height;
        canvas.width = viewport.width;
        var renderContext = {
            canvasContext: context,
            viewport: viewport,
        };
        page.render(renderContext);
    });
}

function makeThumb(page) {
    // draw page to fit into 96x96 canvas
    var vp = page.getViewport(1);
    var canvas = document.createElement("canvas");
    canvas.width = canvas.height = 96;
    var scale = Math.min(canvas.width / vp.width, canvas.height / vp.height);
    return page.render({canvasContext: canvas.getContext("2d"), viewport: page.getViewport(scale)}).promise.then(function () {
        return canvas;
    });
}

function loadThumb() {
    var pages = []; while (pages.length < pdfDoc.numPages) pages.push(pages.length + 1);
    return Promise.all(pages.map(function (num) {
        // create a div for each page and build a small canvas for it
        var div = document.createElement("div");
        document.body.appendChild(div);
        return pdfDoc.getPage(num).then(makeThumb).then(function (canvas) {
            div.appendChild(canvas);
        });
    }));
}

function getCurPageNum() {
    return parseInt(curPage.val());
}
function setCurPageNum(pageNum) {
    return curPage.val(pageNum);
}
function getZoomScale() {
    return parseFloat(zoomScale.val());
}
function setZoomScale(scale) {
    return zoomScale.val(scale);
}
$("#prePage").click(function(){
    var pageNum = getCurPageNum() - 1;
    getPage(pageNum);
})
$("#nextPage").click(function(){
    var pageNum = getCurPageNum() + 1;
    getPage(pageNum);
})
$("#zoomIn").click(function(){
    var scale = getZoomScale();
    scale = Math.round((scale + 0.2) * 100) / 100;
    scale > 2 && (scale = 2);
    setZoomScale(scale);
    getPage(getCurPageNum());
})
$("#zoomOut").click(function(){
    var scale = getZoomScale();
    scale = Math.round((scale - 0.2) * 100) / 100;
    scale < 0.6 && (scale = 0.6);
    setZoomScale(scale);
    getPage(getCurPageNum());
})
</script>

最近接触了两款开源、跨平台、支持多种SQL数据库的数据库管理工具,值得记录一下。

DBeaver
官网:https://dbeaver.io/
2020年疫情期间,在家办公,想找个数据库管理工具,可以在Linux上访问SQL Server数据库,于是遇到DBeaver。界面像Eclipse,容易上手;基于Java,可以跨平台使用;使用JDBC,几乎支持所有数据库。在Linux上,几乎是万能的数据库管理工具了。

HeidiSQL
官网:https://www.heidisql.com/
在Windows上安装MariaDB 10.4.12时,发现自带了HeidiSQL数据库管理工具。界面及操作都跟MySQL Workbench相似,清晰明了,而且支持各种SQL数据。比较意外的是,其基于Delphi开发,所以Linux上需要利用Wine运行。

顺德双皮奶,作为代表家乡的特色甜品小吃,一直深受本人喜爱,只是会吃不会做。近来几乎每天都买水牛奶给孩子补充营养,于是尝试做一下,原来也很简单。主要是以前错误以为第一层奶皮需要技巧弄上去,实际是物理原理,脂肪会浮在水面上。所以,只要弄到水牛奶,再简单弄一下就能吃到双皮奶了。

材料(两碗份量):

  • 水牛奶:250g
  • 蛋清:50g(大概一只鸡蛋的蛋清)
  • 糖:12g(可根据口味增减)

做法:

  • 1,水牛奶煮开,平均倒入两个碗内。放凉,让第一层奶皮形成。
  • 2,轻轻拨开奶皮,倒出牛奶,并让奶皮留在碗里。
  • 3,鸡蛋分离出蛋清和蛋黄。倒出的牛奶,加入蛋清和糖,搅拌均匀至混合。
  • 4,牛奶和蛋清混合物,用筛网过滤一下(去除杂质,让口感更滑),再平均倒回两个碗里。
  • 5,碗上盖上小碟子(或者保鲜膜、锡纸之类,还是小碟子比较环保)。待蒸锅水沸后,放上去蒸7到10分钟(我是8分钟)。
  • 6,蒸好后,放凉到接近体温再食用。夏天可以放冰箱冷藏后食用。

Google Camera(简称gcam)自推出以来,一直是最喜爱的摄影应用(可惜后来变成Pixel机型专属应用)。幸好有开发者移植到其它机型,特别是像红米Note 4X那种渣拍照的手机,极大提升了拍照能力。近来在红米K30 5G上也装上了gcam,并默认开启了“动态照片(Motion Photo)”功能。本来没啥影响,只是拍出来一堆文件名以MVIMG开头或者扩展名前带有“.MP”的照片,昨天好奇研究了一下,才发现就是动态照片。

动态照片,简单来说,就是把拍照前几秒录下视频,并把视频与拍出来的照片整合在一起,生成一个jpg文件。目前发现两种格式:文件名后面带有“.MP”的,在EXIF信息里会说明mp4文件在整个jpg文件中位置;文件名以“MVIMG_”开头的,jpg文件里有个“ftypmp4”标识,该标识后面的数据就是mp4文件。

其实动态照片也没什么坏处,但是找了一圈也找不到可以直接浏览动态图片的Android应用。如果是看不到的视频,那保存在照片中,有什么意义?还浪费了存储空间,也不利于分享、传输。最后找了个工具,叫GoMoPho,把所有拍摄的动态照片都转为普通jpg文件。其实就是把动态文件切割为jpg和mp4两个文件,我保留了jpg并删除了mp4。GoMoPho的相关网址如下:

Google motion photos video extractor.
https://github.com/cliveontoast/GoMoPho

GoMoPho虽然只有命令界面,但是支持上面提到的两种动态照片,而且已移植到多种操作系统,还能支持批量处理文件夹的文件。

这是个简易葡挞食谱。网上的视频看到的,简易的地方是,利用比葡挞皮更容易买到的手抓饼来做。试着做了一下,并修改了材料份量,效果还不错。

1. 材料(做6个的份量)

  • 原味手抓饼 1块
  • 鸡蛋 1个
  • 牛奶 80克
  • 白糖 15克

2. 做法

  • 1)制作挞皮。手抓饼解冻,卷起来,平均切成6份。搓圆,摊在蛋挞模具上,做成碗状。
  • 2)制作挞液。鸡蛋打散,加入牛奶和白糖,搅拌至白糖融化。然后过筛,去除杂质,带来更细滑的口感。
  • 3)挞皮倒入挞液,8分满即可。挞液会在烤的时候涨起,太满会溢出,导致挞皮容易糊。
  • 4)烤箱预热10分钟,200摄氏度中层烤20分钟。挞皮金黄,挞液有焦糖色,即可出炉。

3. 总结

  • 1)鸡蛋尽量选小的,如果是比较大的鸡蛋,要减少牛奶的份量。
  • 2)手抓饼一定要选原味的。毕竟没人想吃带葱的葡挞吧。
  • 3)出炉后,要放凉一下才能吃。主要是怕烫嘴(笑)。

今天烧烤时,顺便烤了板栗,非常成功!记录一下经验吧。

1. 材料

  • 小板栗 120g左右(一人一次的份量)

2. 做法1

  • 1)小板栗洗干净,用刀切个开口,大概二分之一周长。一般用刀砍下去,板栗没有分成两半,而是粘在刀上,就可以了。砍板栗的教程,具体可以参考李子柒于2020-10-10发布关于中秋节的视频,在12:46处(笑)。
  • 2)开水里煮10分钟。
  • 3)从水中捞出放烤盘里,烤箱预热后,以190摄氏度烤10分钟。注意不要烤糊,烤到板栗外壳干脆即可,这样更容易剥开。
  • 4)这个做法,可以用微波炉代替烤箱。但是板栗处理不好的话,容易爆炸(喷得微波炉内壁到处都是板栗碎),不推荐。

3. 做法2

  • 1)小板栗洗干净,用锡纸包好。用竹签戳一个小洞,用于排气。
  • 2)放在碳炉(烧烤炉)上,大概烤半个小时到1个小时。
  • 3)由于锡纸把板栗里的水分都锁住,所以做出来不会变得太硬,非常好吃。

4. 注意

  • 1)选用个头小的小板栗,是因为更容易熟,做起来少费点时间。
  • 2)板栗一次不能多吃。其淀粉含量比较高,多吃容易导致消化不良(例如放屁量增多)。