TLoghttps://techie-s.work/2023-06-09T00:00:00+08:00用 Embassy 开发 Xinput 协议手柄2023-04-17T00:00:00+08:002023-04-17T12:16:07+08:00tag:techie-s.work,2023-04-17:/posts/2023/04/develop-xinput-controller-with-embassy/
<p>Project repository: <a href="https://github.com/hyx0329/em-usb-pad">GitHub repo</a></p>
<h2 id="road-map">Road Map<a class="headerlink" href="#road-map" title="Permanent link">¶</a></h2>
<ul class="task-list">
<li class="task-list-item"><input checked="" disabled="" type="checkbox"/> proof of concept<ul class="task-list">
<li class="task-list-item"><input checked="" disabled="" type="checkbox"/> implement xinput on stm32f103</li>
<li class="task-list-item"><input checked="" disabled="" type="checkbox"/> implement keypad scan on stm32f103<ul class="task-list">
<li class="task-list-item"><input checked="" disabled="" type="checkbox"/> now use existing <code>keypad</code> from crates.io</li>
</ul>
</li>
<li class="task-list-item"><input checked="" disabled="" type="checkbox"/> act like a gamepad</li>
</ul>
</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> implement keymap on stm32f103</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> port to nrf52840</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> implement ble (different modes)<ul class="task-list">
<li class="task-list-item"><input disabled="" type="checkbox"/> hid keyboard & mouse</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> gamepad</li>
</ul>
</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> improve overall structure</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> implement mode switch</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> board abstraction</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> package/lib?</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> custom board (prototype)<ul class="task-list">
<li class="task-list-item"><input disabled="" type="checkbox"/> NFC</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> battery</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> rumble</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> LED</li>
</ul>
</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> and custom shell</li>
<li class="task-list-item"><input disabled="" type="checkbox"/> production design</li>
</ul>
<p>Important thing:</p>
<ul class="task-list">
<li class="task-list-item"><input disabled="" type="checkbox"/> what about the marketing?</li>
</ul>
<h2 id="about-bluetooth">about bluetooth<a class="headerlink" href="#about-bluetooth" title="Permanent link">¶</a></h2>
<p>https://en.wikipedia.org/wiki/Xbox_Wireless_Controller#Summary</p>
<ul>
<li>Classic Bluetooth HID profile </li>
<li>HID over GATT Profile</li>
</ul>
<p>I wonder if they are mostly the same protocol on a higher level, because there are evidences showing the existence of Bluetooth LE XInput compatible controller.</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:usb-nutshell">
<p><a href="https://www.beyondlogic.org/usbnutshell/usb1.shtml">USB in a nutshell</a> <a class="footnote-backref" href="#fnref:usb-nutshell" title="回到 #1">↩</a></p>
</li>
<li id="fn:xbox360-usb-protocol">
<p><a href="https://www.partsnotincluded.com/understanding-the-xbox-360-wired-controllers-usb-data/">Understanding the Xbox 360 Wired Controller’s USB Data</a> <a class="footnote-backref" href="#fnref:xbox360-usb-protocol" title="回到 #2">↩</a></p>
</li>
<li id="fn:gamepad-tester">
<p><a href="https://gamepad-tester.com/">Gamepad Tester and Debugger
</a> <a class="footnote-backref" href="#fnref:gamepad-tester" title="回到 #3">↩</a></p>
</li>
<li id="fn:arduinoxinput-avr">
<p><a href="https://github.com/dmadison/ArduinoXInput_AVR">ArduinoXInput-AVR</a> <a class="footnote-backref" href="#fnref:arduinoxinput-avr" title="回到 #4">↩</a></p>
</li>
<li id="fn:arduinoxinput-lib">
<p><a href="https://github.com/dmadison/ArduinoXInput">ArduinoXInput-Library</a> <a class="footnote-backref" href="#fnref:arduinoxinput-lib" title="回到 #5">↩</a></p>
</li>
</ol>
</div>Embassy——易用的嵌入式Rust异步框架2023-01-25T00:00:00+08:002023-01-25T00:00:00+08:00tag:techie-s.work,2023-01-25:/posts/2023/01/embassy-an-easy-to-use-async-framework-for-embedded-rust/
<p>有些「小制作」需要用到USB、蓝牙、WiFi,我又想用 Rust 进行编程,但很多 Crates 提供的方法很有限。目前我粗略接触过的 <code>esp-idf-sys</code> 、 <code>Tock OS</code> 之类,都缺少了一些我计划中需要的关键功能(这么看感觉 C 那边生态优势很明显,瞧瞧 <code>Zephyr RTOS</code> 的成熟度)。最近在搜索 Rust 可用的蓝牙协议栈时,看到了 <code>Embassy</code> 这个框架,第一感觉是帮我简化了很多 MCU 外设管理的冗杂操作,顿时留下不错的印象。下面是一些个人学习记录。</p>
<p><em>之前觉得是给自己留的坑,写了一半没继续,如今看来还能有点用处,先挂出来吧。写得不咋地,还可能有错,见谅。</em></p>
<p><em>Embassy版本更新会带来一些API变更,建议从官方master分支的example开始建立代码工作区。本文不一定适用。</em></p>
<h2 id="embassy-点灯">Embassy 点灯<a class="headerlink" href="#embassy-点灯" title="Permanent link">¶</a></h2>
<p>废话不多说,先点个灯,好有个直观感受。嘛,还是得先配置好开发环境。先简单列出我的 …</p>
<p>有些「小制作」需要用到USB、蓝牙、WiFi,我又想用 Rust 进行编程,但很多 Crates 提供的方法很有限。目前我粗略接触过的 <code>esp-idf-sys</code> 、 <code>Tock OS</code> 之类,都缺少了一些我计划中需要的关键功能(这么看感觉 C 那边生态优势很明显,瞧瞧 <code>Zephyr RTOS</code> 的成熟度)。最近在搜索 Rust 可用的蓝牙协议栈时,看到了 <code>Embassy</code> 这个框架,第一感觉是帮我简化了很多 MCU 外设管理的冗杂操作,顿时留下不错的印象。下面是一些个人学习记录。</p>
<p><em>之前觉得是给自己留的坑,写了一半没继续,如今看来还能有点用处,先挂出来吧。写得不咋地,还可能有错,见谅。</em></p>
<p><em>Embassy版本更新会带来一些API变更,建议从官方master分支的example开始建立代码工作区。本文不一定适用。</em></p>
<h2 id="embassy-点灯">Embassy 点灯<a class="headerlink" href="#embassy-点灯" title="Permanent link">¶</a></h2>
<p>废话不多说,先点个灯,好有个直观感受。嘛,还是得先配置好开发环境。先简单列出我的开发资源配置。</p>
<ul>
<li>笔记本电脑,操作系统为 Arch Linux</li>
<li>开发板:WeAct BluePill Plus(STM32F103CBT6)</li>
<li>调试器:Muse Lab 高速 DAPLink(兼容公版 ATSAM3U2C DAPLink)</li>
</ul>
<p>首先准备好 Rust 工具链,这个可以参考 <code>https://rustup.rs</code> 提供的信息进行安装,此处不再详叙。</p>
<p>由于我们的目标板 CPU 架构为 ARM Cortex-M,需要额外安装对应的 <em>target</em> 。不过幸运的是,embassy 项目空间内已经写好了工具链配置(<code>rust-toolchain.toml</code>),待会跑 blinky 的时候 rustup 会自动处理工具链依赖。</p>
<p>这边编程也 <strong>不用</strong> 多数人熟悉的 OpenOCD 了(尽管也可以),使用的是基于 Rust Crate <code>probe-rs</code> 的一系列工具。这里主要使用的是 <em>probe-run</em> <sup id="fnref:probe-run-on-crates"><a class="footnote-ref" href="#fn:probe-run-on-crates">1</a></sup> (这样可以直接跑官方例程)。使用下面的指令安装 probe-run。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 安装 probe-run</span>
cargo<span class="w"> </span>install<span class="w"> </span>probe-run
</code></pre></div>
<p>关于 probe-run 的配置,待进入示例项目再看。</p>
<div class="admonition note">
<p class="admonition-title">如果 rustup、cargo 下载的速度过慢</p>
<p>可以尝试一下配置使用其它镜像或加速站点 <sup id="fnref:ustc-rs-tc"><a class="footnote-ref" href="#fn:ustc-rs-tc">2</a></sup> <sup id="fnref:ustc-rs-crates"><a class="footnote-ref" href="#fn:ustc-rs-crates">3</a></sup> <sup id="fnref:rsproxy"><a class="footnote-ref" href="#fn:rsproxy">4</a></sup>。需要额外注意的是,如果自己的 git 配置文件(<code>~/.gitconfig</code>)中配置了代理,建议在操作 rustup 和 cargo 的过程中删除代理相关配置。</p>
</div>
<div class="admonition tip">
<p class="admonition-title">开发板选择参考</p>
<p>目前 Embassy 对 STM32、NRF52 的支持情况足够好,资源丰富,所以选相关开发板能省不少力。不过要是有余力,也可以去看看 Xtensa(ESP32)或者 RISC-V(ESP32-C3)架构的支持,截至2023-01-26,Embassy 已经有对它们的初步支持,而相关工具(probe-run)也支持了 ESP32-C3 内置的 JTAG 适配器。</p>
</div>
<p>准备运行示例项目,先准备一份源代码,操作如下:</p>
<div class="highlight"><pre><span></span><code>git<span class="w"> </span>clone<span class="w"> </span>--depth<span class="w"> </span><span class="m">1</span><span class="w"> </span>--recursive<span class="w"> </span>https://github.com/embassy-rs/embassy
</code></pre></div>
<p>随后进入适用于自己开发板 MCU 的示例项目文件夹。</p>
<div class="highlight"><pre><span></span><code><span class="nb">cd</span><span class="w"> </span>embassy/examples/stm32f1
</code></pre></div>
<p>看一下示例项目内有哪些材料:</p>
<div class="highlight"><pre><span></span><code>><span class="w"> </span>tree<span class="w"> </span>-a
.
├──<span class="w"> </span>build.rs<span class="w"> </span><span class="c1"># 最早编译并运行的 build script</span>
├──<span class="w"> </span>.cargo
│<span class="w"> </span>└──<span class="w"> </span>config.toml<span class="w"> </span><span class="c1"># cargo 配置,关于 probe-run 的配置也在这里</span>
├──<span class="w"> </span>Cargo.lock<span class="w"> </span><span class="c1"># 锁,锁的是 Crates 版本</span>
├──<span class="w"> </span>Cargo.toml<span class="w"> </span><span class="c1"># 项目配置</span>
└──<span class="w"> </span>src<span class="w"> </span><span class="c1"># 这里有 4 个示例程序</span>
<span class="w"> </span>└──<span class="w"> </span>bin
<span class="w"> </span>├──<span class="w"> </span>adc.rs
<span class="w"> </span>├──<span class="w"> </span>blinky.rs
<span class="w"> </span>├──<span class="w"> </span>hello.rs
<span class="w"> </span>└──<span class="w"> </span>usb_serial.rs
</code></pre></div>
<p>这次就跑个 blinky(<code>blinky.rs</code>),检查下代码适不适合我的板子,一看,就 LED 引脚不一样。那就简单修改一下。</p>
<div class="highlight"><pre><span></span><code><span class="gd">- let mut led = Output::new(p.PC13, Level::High, Speed::Low);</span>
<span class="gi">+ let mut led = Output::new(p.PB2, Level::High, Speed::Low);</span>
</code></pre></div>
<p>再检查一下 probe-run 的配置(在 <code>.cargo/config.toml</code>)和我的 MCU 是否一致。一看,倒也不用改。</p>
<div class="highlight"><pre><span></span><code><span class="w">[target.'cfg(all(target_arch = "arm", target_os = "none"))']</span>
<span class="w"># replace STM32F103C8 with your chip as listed in `probe-run --list-chips`</span>
<span class="gd">- runner = "probe-run --chip STM32F103C8"</span>
<span class="gi">+ runner = "probe-run --chip STM32F103CB"</span>
<span class="w">[build]</span>
<span class="w">target = "thumbv7m-none-eabi"</span>
<span class="w">[env]</span>
<span class="w">DEFMT_LOG = "trace"</span>
</code></pre></div>
<p>接下来就连接好开发板,执行下面的指令编译、刷入固件。</p>
<div class="highlight"><pre><span></span><code>cargo<span class="w"> </span>run<span class="w"> </span>--bin<span class="w"> </span>blinky
</code></pre></div>
<p>等编译、刷写完毕,应该就能看到板子上的 LED 在闪烁了。</p>
<div class="admonition tip">
<p class="admonition-title">关于 LED 闪烁时间准确性</p>
<p>你可能会觉得闪烁时间和程序里写的 300 ms 差别有点大,我也不知道为什么。Debug 日志输出使用了RTT,应该影响不大;由此来看很可能是因为 Embassy 的非抢占式调度。谈起调度,可以了解下 <em>RTIC</em> ,Embassy 和 RTIC 的关系可以说是各自的重点不同 <sup id="fnref:embassy-rtic"><a class="footnote-ref" href="#fn:embassy-rtic">5</a></sup> 。</p>
</div>
<h2 id="embassy-设计初探-6">Embassy 设计初探 <sup id="fnref:embassy-by-layer"><a class="footnote-ref" href="#fn:embassy-by-layer">6</a></sup><a class="headerlink" href="#embassy-设计初探-6" title="Permanent link">¶</a></h2>
<p>Embassy 以一个 Executor 为核心。所有的任务用一个宏标记。以 <code>blinky.rs</code> 为例:</p>
<div class="highlight"><pre><span></span><code><span class="cp">#![no_std]</span>
<span class="cp">#![no_main]</span>
<span class="cp">#![feature(type_alias_impl_trait)]</span>
<span class="k">use</span><span class="w"> </span><span class="n">defmt</span>::<span class="o">*</span><span class="p">;</span>
<span class="k">use</span><span class="w"> </span><span class="n">embassy_executor</span>::<span class="n">Spawner</span><span class="p">;</span>
<span class="k">use</span><span class="w"> </span><span class="n">embassy_stm32</span>::<span class="n">gpio</span>::<span class="p">{</span><span class="n">Level</span><span class="p">,</span><span class="w"> </span><span class="n">Output</span><span class="p">,</span><span class="w"> </span><span class="n">Speed</span><span class="p">};</span>
<span class="k">use</span><span class="w"> </span><span class="n">embassy_time</span>::<span class="p">{</span><span class="n">Duration</span><span class="p">,</span><span class="w"> </span><span class="n">Timer</span><span class="p">};</span>
<span class="k">use</span><span class="w"> </span><span class="p">{</span><span class="n">defmt_rtt</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">_</span><span class="p">,</span><span class="w"> </span><span class="n">panic_probe</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">_</span><span class="p">};</span>
<span class="cp">#[embassy_executor::main]</span>
<span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">main</span><span class="p">(</span><span class="n">_spawner</span>: <span class="nc">Spawner</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">embassy_stm32</span>::<span class="n">init</span><span class="p">(</span><span class="nb">Default</span>::<span class="n">default</span><span class="p">());</span>
<span class="w"> </span><span class="n">info</span><span class="o">!</span><span class="p">(</span><span class="s">"Hello World!"</span><span class="p">);</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">led</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Output</span>::<span class="n">new</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">PB2</span><span class="p">,</span><span class="w"> </span><span class="n">Level</span>::<span class="n">High</span><span class="p">,</span><span class="w"> </span><span class="n">Speed</span>::<span class="n">Low</span><span class="p">);</span>
<span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">info</span><span class="o">!</span><span class="p">(</span><span class="s">"high"</span><span class="p">);</span>
<span class="w"> </span><span class="n">led</span><span class="p">.</span><span class="n">set_high</span><span class="p">();</span>
<span class="w"> </span><span class="n">Timer</span>::<span class="n">after</span><span class="p">(</span><span class="n">Duration</span>::<span class="n">from_millis</span><span class="p">(</span><span class="mi">300</span><span class="p">)).</span><span class="k">await</span><span class="p">;</span>
<span class="w"> </span><span class="n">info</span><span class="o">!</span><span class="p">(</span><span class="s">"low"</span><span class="p">);</span>
<span class="w"> </span><span class="n">led</span><span class="p">.</span><span class="n">set_low</span><span class="p">();</span>
<span class="w"> </span><span class="n">Timer</span>::<span class="n">after</span><span class="p">(</span><span class="n">Duration</span>::<span class="n">from_millis</span><span class="p">(</span><span class="mi">300</span><span class="p">)).</span><span class="k">await</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>在 blinky 的例子中,只是用宏标记了一个 <code>main</code> 入口点(此处已简化许多),Embassy 在完成一定初始化后会从这里开始执行主程序。但是这样好像同时只有一个任务在运行?也确实如此,毕竟还有一个 <code>Task</code> 没有引入。来看官方提供的另一个例子。</p>
<div class="highlight"><pre><span></span><code><span class="cp">#![feature(type_alias_impl_trait)]</span>
<span class="k">use</span><span class="w"> </span><span class="n">embassy_executor</span>::<span class="n">Spawner</span><span class="p">;</span>
<span class="k">use</span><span class="w"> </span><span class="n">embassy_time</span>::<span class="p">{</span><span class="n">Duration</span><span class="p">,</span><span class="w"> </span><span class="n">Timer</span><span class="p">};</span>
<span class="k">use</span><span class="w"> </span><span class="n">log</span>::<span class="o">*</span><span class="p">;</span>
<span class="cp">#[embassy_executor::task]</span>
<span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">run</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">info</span><span class="o">!</span><span class="p">(</span><span class="s">"tick"</span><span class="p">);</span>
<span class="w"> </span><span class="n">Timer</span>::<span class="n">after</span><span class="p">(</span><span class="n">Duration</span>::<span class="n">from_secs</span><span class="p">(</span><span class="mi">1</span><span class="p">)).</span><span class="k">await</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
<span class="cp">#[embassy_executor::main]</span>
<span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">main</span><span class="p">(</span><span class="n">spawner</span>: <span class="nc">Spawner</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">env_logger</span>::<span class="n">builder</span><span class="p">()</span>
<span class="w"> </span><span class="p">.</span><span class="n">filter_level</span><span class="p">(</span><span class="n">log</span>::<span class="n">LevelFilter</span>::<span class="n">Debug</span><span class="p">)</span>
<span class="w"> </span><span class="p">.</span><span class="n">format_timestamp_nanos</span><span class="p">()</span>
<span class="w"> </span><span class="p">.</span><span class="n">init</span><span class="p">();</span>
<span class="w"> </span><span class="n">spawner</span><span class="p">.</span><span class="n">spawn</span><span class="p">(</span><span class="n">run</span><span class="p">()).</span><span class="n">unwrap</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div>
<p>此处便可以看到,通过 Embassy 提供的宏,创建了一个 Task,而通过调用这个 Task,便可以得到一个 task 实例(实际上和 coroutine 差不多)。有过异步编程经验的人应该很快就能认出这个简单的创建多个任务的模式(个人感觉和 CircuitPython 的使用逻辑很接近)。</p>
<p>回到之前所说的,Embassy 的核心是一个 Executor。根据官方文档 <sup id="fnref:embassy-executor"><a class="footnote-ref" href="#fn:embassy-executor">7</a></sup> 的介绍:</p>
<blockquote>
<p>executor 维护一个存储需要轮询(poll)的任务队列。当一个任务(task)创建时,它就被检视(poll)一次。这个任务会一直执行到它被阻塞。这可能发生在任何等待(await) async 函数返回结果的时候。而当前述事件发生时,对应任务会暂停执行(yield)并返回 <code>Poll:Pending</code> 。随后,executor 将该任务放在任务队列尾部,去检视下一个任务。如果一个任务完成或者取消了,它就不再入队。</p>
</blockquote>
<p>当然,这也有个前提:<strong>没有哪个任务会一直阻塞、不提供切换任务的机会。</strong> 不然 executor 将永远无法切换任务,整个系统也退化成了单道程序处理机。</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:probe-run-on-crates">
<p><a href="https://crates.io/crates/probe-run">Crates.io 上的 probe-run</a> <a class="footnote-backref" href="#fnref:probe-run-on-crates" title="回到 #1">↩</a></p>
</li>
<li id="fn:ustc-rs-tc">
<p><a href="https://mirrors.ustc.edu.cn/help/rust-static.html">USTC Rust 工具链反向代理配置说明</a> <a class="footnote-backref" href="#fnref:ustc-rs-tc" title="回到 #2">↩</a></p>
</li>
<li id="fn:ustc-rs-crates">
<p><a href="https://mirrors.ustc.edu.cn/help/crates.io-index.html">USTC Crates 源使用帮助</a> <a class="footnote-backref" href="#fnref:ustc-rs-crates" title="回到 #3">↩</a></p>
</li>
<li id="fn:rsproxy">
<p><a href="https://rsproxy.cn/">Rsproxy 主页</a> <a class="footnote-backref" href="#fnref:rsproxy" title="回到 #4">↩</a></p>
</li>
<li id="fn:embassy-rtic">
<p><a href="https://cohost.org/jamesmunns/post/67555-should-i-use-embassy">Should I use Embassy or RTIC or both (for embedded rust)? – James Munns</a> <a class="footnote-backref" href="#fnref:embassy-rtic" title="回到 #5">↩</a></p>
</li>
<li id="fn:embassy-by-layer">
<p><a href="https://embassy.dev/dev/layer_by_layer.html">Embassy layer by layer</a> <a class="footnote-backref" href="#fnref:embassy-by-layer" title="回到 #6">↩</a></p>
</li>
<li id="fn:embassy-executor">
<p><a href="https://embassy.dev/dev/runtime.html">Embassy executor</a> <a class="footnote-backref" href="#fnref:embassy-executor" title="回到 #7">↩</a></p>
</li>
</ol>
</div>蓝牙 SBC 双通道高清音频模式2023-01-22T00:00:00+08:002023-01-22T00:00:00+08:00tag:techie-s.work,2023-01-22:/posts/2023/01/lineage-bluetooth-sbc-xq/
<p><em>本文是原Lineage官网列出的“Bluetooth SBC Dual Channel HD audio mode”<sup id="fnref:original-bluetooth-sbc-xq"><a class="footnote-ref" href="#fn:original-bluetooth-sbc-xq">1</a></sup>的个人翻译版本,供各位学习交流使用。译文中的图片均直接转载自原文。原文作者是 Valdikss,发布时间为2019年7月6日,引用时间2023年1月22日。</em></p>
<hr/>
<p>LineageOS 15.1 和 16.0 为原有的蓝牙 SBC 音频编码带来了一个独特的音质提升。它们引入了带有 eXtreme quality profile (SBC XQ) 的 SBC 双通道模式,将这个老旧编码的音频质量提升到了与高通私有编码——aptX HD——相近的高度。</p>
<p>只要用户的设备运行的是在2019年3月31日或之后编译的 LineageOS 15.1、或是在2019年5月13日或之后编译的 LineageOS 16.0,一个新的“HD Audio: SBC”复选框就会出现在用户的蓝牙音频设备设置界面上。激活这项设定将启用双通道模式(Dual Channel mode),这提供了一个经过提升、比原版 SBC 编码高得多的比特率(bitrate)。新的比特率(452 或 551 kbps)依赖于蓝牙设备的最大 …</p>
<p><em>本文是原Lineage官网列出的“Bluetooth SBC Dual Channel HD audio mode”<sup id="fnref:original-bluetooth-sbc-xq"><a class="footnote-ref" href="#fn:original-bluetooth-sbc-xq">1</a></sup>的个人翻译版本,供各位学习交流使用。译文中的图片均直接转载自原文。原文作者是 Valdikss,发布时间为2019年7月6日,引用时间2023年1月22日。</em></p>
<hr/>
<p>LineageOS 15.1 和 16.0 为原有的蓝牙 SBC 音频编码带来了一个独特的音质提升。它们引入了带有 eXtreme quality profile (SBC XQ) 的 SBC 双通道模式,将这个老旧编码的音频质量提升到了与高通私有编码——aptX HD——相近的高度。</p>
<p>只要用户的设备运行的是在2019年3月31日或之后编译的 LineageOS 15.1、或是在2019年5月13日或之后编译的 LineageOS 16.0,一个新的“HD Audio: SBC”复选框就会出现在用户的蓝牙音频设备设置界面上。激活这项设定将启用双通道模式(Dual Channel mode),这提供了一个经过提升、比原版 SBC 编码高得多的比特率(bitrate)。新的比特率(452 或 551 kbps)依赖于蓝牙设备的最大增强数据率(Enhanced Data Rate)并且在使用联合立体声模式(Joint Stereo mode)时相比于原版 328 kbps 是一个明显的提升。</p>
<p>这个特性搭载在所有受 LineageOS 支持的设备上,并且可以和现存的绝大多数蓝牙播放设备一同使用。这个特性最适合没有额外编码支持——aptX、aptX HD、AAC 或是 LDAC——的廉价音频设备。</p>
<h2 id="sbc-编码协议">SBC 编码(协议)<a class="headerlink" href="#sbc-编码协议" title="Permanent link">¶</a></h2>
<p>SBC 有许多在连接过程中协商的不同参数:</p>
<ul>
<li>音频通道/音轨(类型和数量):联合立体声,立体声,双通道,单声道;</li>
<li>频带数量:4 或 8;</li>
<li>单个音频数据帧中音频数据块的数量:4,8,12,16;</li>
<li>量化位分配算法:响度,信噪比(SNR);</li>
<li>量化过程中用到比特池(bit pool)的容量范围:通常为 2 到 53;</li>
</ul>
<p>解码器(播放设备)应当支持上述参数的任何可能组合,但编码器(智能手机)可以只实现一部分。</p>
<p>现在的蓝牙协议栈通常用下面一组参数进行协商(这一组参数也被称作 profile):联合立体声(Joint Stereo),8 个频带,16 块,响度,以及一个 2 到 53 位的比特池大小。这个 profile 将用 328 kbps 的比特率为 44.1 kHz 的立体声音频编码。</p>
<p>在所提到的参数中,比特池因其对比特率的直接影响处于格外重要的地位:它的大小越大,比特率就越大,也就提高了音频传输质量。当 profile 中的其他参数不改变时,一个特定的比特池大小将决定一个对应的比特率数值。</p>
<p>比特率同样被其它参数显著影响:音轨类型、频带数量以及音频数据块数量。你可以通过协商非常规的 profiles 来间接提高比特率,而不用调整比特池大小。需要着重注意的是一个更高的比特率并不总是带来更好的音质。例如,将单个音频数据帧中的音频数据块数量从 16 下调到 4,当使用联合立体声模式和 53 位大小的比特池时,将会把比特率从 328 kbps 提升到 441 kbps,但这也将减少很多计算时延,而这样会让动态量化过程变得更糟糕。上述例子不会带来除比特率增长之外的任何好处。</p>
<div class="math">$$
bitrate = \frac{8 \times frame\_length \times sample\_rate}{subbands \times blocks}
$$</div>
<!-- ![bitrate计算公式]({attach}content_bluetooth_sbc_xq_0.png) -->
<p>固定的比特池大小与比特率的值均来自原版编码为高品质音频推荐的 profile。这个推荐值应该不构成我们去限制实际参数值的原因,正如下面所做的那样。</p>
<p><img alt="几种 profile 的参数对比" src="https://techie-s.work/posts/2023/01/lineage-bluetooth-sbc-xq/content_bluetooth_sbc_xq_2.png"/></p>
<p>生效自 2007 年至 2015 年的 A2DP 规范 v1.2(译注:节4.3.2.6)要求所有解码设备都能正确处理最高达 512 kbps 的比特率:</p>
<blockquote>
<p>The decoder of the SNK shall support all possible bitpool values that do not result in excess of the maximum bit rate. This profile limits the available maximum bit rate to 320kb/s for mono, and 512kb/s for two-channel modes.</p>
<p>输入端(SNK,sink)的解码器必须支持所有不让比特率超过最大限定值的比特池大小。该 profile 将最大比特率限制为单声道模式下的 320kb/s 和双声道模式下的 512kb/s。</p>
</blockquote>
<p>而新版本的规范并未声明任何比特率限制。据推测,2015年之后发布的支持 EDR(Enhanced Data Rate)的现代耳机最高能支持 1000 kbps 的比特率。</p>
<h2 id="双通道模式">双通道模式<a class="headerlink" href="#双通道模式" title="Permanent link">¶</a></h2>
<p>双通道模式分别对左右声道进行编码,每个声道都用单独的比特池大小,这不像立体声或联合立体声那样对两个声道用同一个比特池大小。将设备运行模式从联合立体声模式切换到双通道模式将几乎会让比特率翻倍,达到 617 kbps,这将在不改变比特池大小(也就是53)的情况下带来显著的音质提升。我个人觉得比特池大小应该是一个内部变量。我推测,由于 A2DP 规范自身的设计失误导致了比特池大小并非由其它变量决定,而成了一个独立可协商的参数。</p>
<h2 id="551-和-452-kbps-是哪来的">551 和 452 kbps 是哪来的?<a class="headerlink" href="#551-和-452-kbps-是哪来的" title="Permanent link">¶</a></h2>
<p>蓝牙时分技术在设计上是能高效传输固定大小的大数据包的。数据传输以 <strong>槽</strong> (slot) 为单位,而每次最多可传输 5 个槽。也有些传输模式使用 1 或 3 个槽,但从来没有用 2 或 4 的。在链接速率为 2 Mbps 的情况下,你可以用 5 个槽传输多达 679 字节数据,而在 3Mbps 的情况下可以传输至多 1021 字节的数据。当使用 3 个槽时,上述情况下的单次传输数据量分别为 367 和 552 字节。</p>
<p><img alt="使用 5 slots 在 2 Mbps 情况下传输数据示意图" src="https://techie-s.work/posts/2023/01/lineage-bluetooth-sbc-xq/content_bluetooth_sbc_xq_3.png"/></p>
<p>如果我们向传输少于 679 或 1021 字节但多于 367 或 552 字节的数据,单次传输依然要占用 5 个槽,而且传输会占用同样长的时间,也就降低了传输效率。</p>
<p><img alt="44.1kHz 音频在 SBC 编码下的传输数据示意图" src="https://techie-s.work/posts/2023/01/lineage-bluetooth-sbc-xq/content_bluetooth_sbc_xq_4.png"/></p>
<p>44100 Hz 的音频经由 SBC 在双通道模式、比特池大小 38、每帧 16 块、8 频带下编码将生成长度为 164 字节的音频数据帧,相应的比特率为 452 kb/s。</p>
<p><img alt="帧长度计算公式" src="https://techie-s.work/posts/2023/01/lineage-bluetooth-sbc-xq/content_bluetooth_sbc_xq_1.png"/></p>
<p>一个用尽 5 个槽的传输可以包含至多 4 个音频数据帧:</p>
<p><strong>679 (EDR 2 mbit/s DH5) - 4 (L2CAP) - 12 (AVDTP/RTP) - 1 (SBC header) - (164*4) = 6</strong></p>
<p>单个数据包能至多传输 11.7 毫秒时长的音频数据,而这些数据将在 3.75 毫秒内传输完成,而我们还有 6 字节没用上。如果你稍稍提高一点比特池的大小,4 个音频数据帧将再也不能挤到单次传输之中。你不得不每次发送 3 帧,也就降低了传输效率、单次传输中的音频数据量,还会增加无线环境较差情况下的音频卡顿的概率。</p>
<p>EDR 3 Mbps 情况下的 551 kbps 比特率也是用类似的方法选出的:在比特池大小为 47、每帧 16 块、8 个频带的情况下,帧大小为 200 字节,而比特率为 551 kbps。单词传输也就能用 5 个音频数据帧传输至多 14.6 毫秒的音乐。</p>
<p>计算所有 SBC 可用参数的算法还挺复杂的,增加了出错的可能。如果你想自己全算一遍,建议使用这个 <a href="https://btcodecs.valdikss.org.ru/sbc-bitrate-calculator">交互式计算器</a>。</p>
<h2 id="我们可以做得更好吗">我们可以做得更好吗?<a class="headerlink" href="#我们可以做得更好吗" title="Permanent link">¶</a></h2>
<p>安卓的补丁包有一个额外选项可以增加 EDR 2Mbps 设备的比特率、超过它们既有的限制。你可以把比特率从 452 kbps 增加到 595 kbps,不过代价是当你的设备遇到拥塞的无线网络环境时传输稳定性会下降。</p>
<p>为了启用上述功能,在安卓 root shell 中执行下面的命令:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># setprop persist.bluetooth.sbc_hd_higher_bitrate 1</span>
</code></pre></div>
<h2 id="这个改变真的有什么好处吗">这个改变真的有什么好处吗?<a class="headerlink" href="#这个改变真的有什么好处吗" title="Permanent link">¶</a></h2>
<p>SoundExpert 的 Serge Smirnoff 做了一次 <a href="http://soundexpert.org/articles/-/blogs/audio-quality-of-sbc-xq-bluetooth-audio-codec">蓝牙SBC XQ profile 测试</a>。他的测试表明 SBC XQ 452 kbps 的音频比 aptX 的失真更少,而 551 kbps 的版本甚至可以和 aptX HD 比。</p>
<p>想自己听听差异吗?试试这个实时 <a href="https://btcodecs.valdikss.org.ru/sbc-encoder/">将音频转码为 SBC 的在线服务</a>(也包括 aptX 和 aptX HD),就运行在浏览器里。你可以比较不同 SBC profile 和其它编码的不同,而不用真的去用蓝牙传输音频,只要用手边的有线耳机、音箱,然后上传你最喜欢的音乐就好。你也可以在播放过程中直接调整那些参数。</p>
<h2 id="结论">结论<a class="headerlink" href="#结论" title="Permanent link">¶</a></h2>
<p>LineageOS 用户现在可以通过在他们的蓝牙设备设定里为“HD Audio: SBC”复选框打上勾,就能增强他们的蓝牙音频质量。这个特性应该能帮人们改变认为 SBC 低质量、应急用编码的这种偏见。当选择一个新的蓝牙耳机或音箱时,用户不再需要考虑那些私有编码了。</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:original-bluetooth-sbc-xq">
<p>https://www.lineageos.org/engineering/Bluetooth-SBC-XQ/ <a class="footnote-backref" href="#fnref:original-bluetooth-sbc-xq" title="回到 #1">↩</a></p>
</li>
</ol>
</div>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>HomeAssistant与ESP322023-01-12T00:00:00+08:002023-01-12T00:00:00+08:00tag:techie-s.work,2023-01-12:/posts/2023/01/homeassistant-working-with-esp32/
<h2 id="前言">前言<a class="headerlink" href="#前言" title="Permanent link">¶</a></h2>
<p>因为突然对智能家居很感兴趣, <del>稍微</del> 折腾了一下</p>
<h2 id="home-assistant">Home Assistant<a class="headerlink" href="#home-assistant" title="Permanent link">¶</a></h2>
<p>Home Assistant是一个用Python实现的开源智能家居平台,着眼于不依赖于云服务的离线智能家居。有赖于社区动力,
主流智能家居供应商的产品大多可接入Home Assistant管理。许多开源家居智能化方案也接入了Home Assistant。
就不过多介绍了。</p>
<h2 id="tasmota">Tasmota<a class="headerlink" href="#tasmota" title="Permanent link">¶</a></h2>
<h3 id="简介">简介<a class="headerlink" href="#简介" title="Permanent link">¶</a></h3>
<p>Tasmota是一个ESP32/ESP8266平台的开源固件,通过MQTT或HTTP API进行数据交互。
它将各个平台抽象为一系列模板,用统一的固件+硬件模板配置适配了各种各样的商业产品。
比如我手上的小米米家台灯1S,就可以刷成Tasmota固件。</p>
<p>通过其内置的三种脚本系统(rules,scripts,berry scripts)可以实现很多复杂的功能,例如
联机自动注册设备、自动上报数据、自动触发某一长时间运行的流程。</p>
<p>想在Home Assistant Core中接入Tasmota,只要自己搭建好MQTT服务器、启用MQTT和Tasmota集成,
再配置好Tasmota设备接入即可。</p>
<h3 id="实例tasmota人体传感器hass">实例:Tasmota+人体传感器+HASS<a class="headerlink" href="#实例tasmota人体传感器hass" title="Permanent link">¶</a></h3>
<p>鉴于很多 …</p>
<h2 id="前言">前言<a class="headerlink" href="#前言" title="Permanent link">¶</a></h2>
<p>因为突然对智能家居很感兴趣, <del>稍微</del> 折腾了一下</p>
<h2 id="home-assistant">Home Assistant<a class="headerlink" href="#home-assistant" title="Permanent link">¶</a></h2>
<p>Home Assistant是一个用Python实现的开源智能家居平台,着眼于不依赖于云服务的离线智能家居。有赖于社区动力,
主流智能家居供应商的产品大多可接入Home Assistant管理。许多开源家居智能化方案也接入了Home Assistant。
就不过多介绍了。</p>
<h2 id="tasmota">Tasmota<a class="headerlink" href="#tasmota" title="Permanent link">¶</a></h2>
<h3 id="简介">简介<a class="headerlink" href="#简介" title="Permanent link">¶</a></h3>
<p>Tasmota是一个ESP32/ESP8266平台的开源固件,通过MQTT或HTTP API进行数据交互。
它将各个平台抽象为一系列模板,用统一的固件+硬件模板配置适配了各种各样的商业产品。
比如我手上的小米米家台灯1S,就可以刷成Tasmota固件。</p>
<p>通过其内置的三种脚本系统(rules,scripts,berry scripts)可以实现很多复杂的功能,例如
联机自动注册设备、自动上报数据、自动触发某一长时间运行的流程。</p>
<p>想在Home Assistant Core中接入Tasmota,只要自己搭建好MQTT服务器、启用MQTT和Tasmota集成,
再配置好Tasmota设备接入即可。</p>
<h3 id="实例tasmota人体传感器hass">实例:Tasmota+人体传感器+HASS<a class="headerlink" href="#实例tasmota人体传感器hass" title="Permanent link">¶</a></h3>
<p>鉴于很多传感器都不需要多少配置就能无缝整合入Home Assistant,这里介绍一个需要一点操作的案例。</p>
<p>人体传感器,简单版,参考下列资料</p>
<ul>
<li>https://tasmota.github.io/docs/PIR-Motion-Sensors/</li>
<li>https://tasmota.github.io/docs/Rules/</li>
<li>https://www.home-assistant.io/integrations/mqtt</li>
</ul>
<p>步骤概要:</p>
<ol>
<li>配置Tasmota中检测信号的GPIO</li>
<li>配置Tasmota中对应的自动化规则</li>
<li>在Home Assistant中配置相关设备集成</li>
</ol>
<p>首先是人体传感器信号检测。这里使用HLK-LD2410传感器,其支持已加入Tasmota 12.3.1,不过下面做的其实和所谓支持关系不大。
LD2410提供了一个OUT信号脚,在检测到人体时是高电平,一段时间未检测到人体后就变为低电平。将OUT与ESP32的某个GPIO
(下称GPIOx)相连,在Tasmota配置页面进入 <code>Configuration</code> → <code>Configure Module</code> ,将对应的GPIOx设置为Switch,并分配一个闲置ID。
下面假设这个Switch的ID是1。</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Switch的ID无所谓,只要选空闲的就好。后面的指令需要将对应命令、Switch1等字串末尾的1改成对应ID。默认情况下,Switch会控制ID相同的灯。</p>
</div>
<p>进入 <code>Consoles</code> → <code>Console</code> ,设置对应Switch模式,关闭其开关相同编号设备的作用,同时关闭开关对应的MQTT消息。</p>
<div class="highlight"><pre><span></span><code>SwitchMode1 1
SwitchTopic 0
</code></pre></div>
<p>给Tasmota配置一个自动化规则,在信号发生变化的时候发送MQTT消息,在连接到MQTT服务时向Home Assistant注册该传感器。</p>
<div class="highlight"><pre><span></span><code><span class="nv">rule1</span>
<span class="w"> </span><span class="nv">on</span><span class="w"> </span><span class="nv">Switch1</span>#<span class="nv">state</span><span class="o">=</span><span class="mi">1</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="nv">publish</span><span class="w"> </span><span class="nv">stat</span><span class="o">/%</span><span class="nv">topic</span><span class="o">%/</span><span class="nv">PIR1</span><span class="w"> </span><span class="nv">ON</span><span class="w"> </span><span class="nv">endon</span>
<span class="w"> </span><span class="nv">on</span><span class="w"> </span><span class="nv">Switch1</span>#<span class="nv">state</span><span class="o">=</span><span class="mi">0</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="nv">publish</span><span class="w"> </span><span class="nv">stat</span><span class="o">/%</span><span class="nv">topic</span><span class="o">%/</span><span class="nv">PIR1</span><span class="w"> </span><span class="nv">OFF</span><span class="w"> </span><span class="nv">endon</span>
<span class="w"> </span><span class="nv">on</span><span class="w"> </span><span class="nv">mqtt</span>#<span class="nv">connected</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="nv">publish2</span><span class="w"> </span><span class="nv">homeassistant</span><span class="o">/</span><span class="nv">binary_sensor</span><span class="o">/</span><span class="nv">presence_</span><span class="o">%</span><span class="nv">deviceid</span><span class="o">%/</span><span class="nv">config</span><span class="w"> </span>{<span class="w"> </span><span class="s2">"name"</span>:<span class="w"> </span><span class="s2">"Presence Sensor"</span>,<span class="w"> </span><span class="s2">"object_id"</span>:<span class="w"> </span><span class="s2">"presence_%deviceid%"</span>,<span class="w"> </span><span class="s2">"device_class"</span>:<span class="w"> </span><span class="s2">"presence"</span>,<span class="w"> </span><span class="s2">"state_topic"</span>:<span class="w"> </span><span class="s2">"stat/%topic%/PIR1"</span>}<span class="w"> </span><span class="nv">endon</span>
</code></pre></div>
<div class="admonition note">
<p class="admonition-title">rule说明</p>
<p>rule有三个存储空间,分别为rule1、rule2、rule3。其中每个空间至少可以存储1000字节的指令,考虑到压缩,可能会更多。
rule1后面的1 <strong>不用</strong> 和对应开关的ID一致!</p>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>在终端输入上述指令时注意合并成一行!</p>
</div>
<p>下面提供另外一种写法,不过根据配置不同,数据刷新可能不够及时</p>
<div class="highlight"><pre><span></span><code><span class="x">rule1</span>
<span class="x"> on mqtt#connected do publish2 homeassistant/binary_sensor/presence_%deviceid%/config { "name": "Presence Sensor", "object_id": "presence_%deviceid%", "device_class": "presence", "state_topic": "tele/%topic%/SENSOR", "value_template": "</span><span class="cp">{{</span> <span class="nv">value_json.Switch1</span> <span class="cp">}}</span><span class="x">"} endon</span>
</code></pre></div>
<p>接下来启用rule1,保存配置并重启</p>
<div class="highlight"><pre><span></span><code>rule1 1
restart 1
</code></pre></div>
<p>最后便可以在Home Assistant的实体注册表找到名为“Presence Sensor”的人体存在检测设备了。
通过复杂一点的脚本,还能实现人体动态与静态的检测,当然这就需要启用LD2410传感器对应的支持以利用串口数据了。</p>
<h2 id="esphome">ESPHome<a class="headerlink" href="#esphome" title="Permanent link">¶</a></h2>
<h3 id="架构介绍">架构介绍<a class="headerlink" href="#架构介绍" title="Permanent link">¶</a></h3>
<p>就我目前所知,ESPHome的设计大致如下:</p>
<ul>
<li>所有ESPHome设备的固件由一组ESPHome程序(Dashboard或CLI)根据给出的设备配置(YAML)生成<ul>
<li>算是一揽子服务(配合PlatformIO CLI)</li>
<li>初见比较难hack</li>
<li>YAML解析有一些特殊语法</li>
</ul>
</li>
<li>每个ESPHome设备可以独立运行,不依赖面板/Dashboard<ul>
<li>面板只有UI/UX变了</li>
<li>CLI有提供指引系统,但是支持的开发板数量较少,如果一开始就要使用不支持的开发板会比较困难</li>
<li>固件可以通过USB安装,HTTPS连接dashboard的情况下可以用Web USB给ESP32编程</li>
</ul>
</li>
<li>Home Assistant通过约定的API接口与ESPHome设备交互数据,需要在设备配置YAML中写一行 <code>api:</code> 开启API</li>
</ul>
<p>(不得不说这个项目的文档很不适合我,很难检索到所需的资料)</p>
<p>也就是说,ESPHome包括编译系统和边缘设备两部分。</p>
<h3 id="使用python虚拟环境准备esphome部署环境">使用Python虚拟环境准备ESPHome部署环境<a class="headerlink" href="#使用python虚拟环境准备esphome部署环境" title="Permanent link">¶</a></h3>
<p>鉴于官网已经花费了大量篇幅从一开始就为Home Assistant和Docker用户介绍了对应的使用方法,这里将
完整地介绍一遍不使用Docker、Home Assistant插件来使用ESPHome的流程。</p>
<p>前面说过,ESPHome把相关工作都包揽了,这里只需要构建一个虚拟环境作为工作空间就好。
构建虚拟环境可以使用下面的指令。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 创建虚拟环境</span>
python3<span class="w"> </span>-m<span class="w"> </span>venv<span class="w"> </span>esphome-venv
<span class="c1"># 激活虚拟环境</span>
<span class="nb">source</span><span class="w"> </span>esphome-venv/bin/activate
</code></pre></div>
<div class="admonition note">
<p class="admonition-title">虚拟环境的pypi镜像与代理配置</p>
<p>可以直接在虚拟环境文件夹(上例中是 <code>esphome-venv</code>)内新建一个pip.conf,在其中添加对应配置,例如使用阿里云的pypi源:</p>
<div class="highlight"><pre><span></span><code><span class="k">[global]</span>
<span class="na">index-url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">https://mirrors.aliyun.com/pypi/simple/</span>
<span class="k">[install]</span>
<span class="na">trusted-host</span><span class="o">=</span><span class="s">mirrors.aliyun.com</span>
</code></pre></div>
</div>
<p>接下来随意创建一个保存YAML的文件夹作为工作空间便可以了。</p>
<h3 id="实例使用luatos-core-esp32c3开发板制作低功耗蓝牙ble网关">实例:使用LuatOS-CORE-ESP32C3开发板制作低功耗蓝牙(BLE)网关<a class="headerlink" href="#实例使用luatos-core-esp32c3开发板制作低功耗蓝牙ble网关" title="Permanent link">¶</a></h3>
<p>参考了下列资料:</p>
<ul>
<li>https://digiblur.com/wiki/ha/esphome-bluetooth-proxy-esp32c3/</li>
<li>https://esphome.io/components/display/index.html</li>
<li>https://esphome.io/components/display/st7735.html</li>
</ul>
<p>激活虚拟环境,在你的配置保存文件夹内编写如下YAML,保存为 <code>ble-gateway.yaml</code>。</p>
<div class="highlight"><pre><span></span><code><span class="nt">substitutions</span><span class="p">:</span>
<span class="w"> </span><span class="nt">display_name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">core-esp32c3-btproxy</span>
<span class="nt">esphome</span><span class="p">:</span>
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">${display_name}</span>
<span class="w"> </span><span class="nt">platformio_options</span><span class="p">:</span>
<span class="w"> </span><span class="nt">board_build.mcu</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">esp32c3</span>
<span class="w"> </span><span class="nt">board_build.variant</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">esp32c3</span><span class="w"> </span>
<span class="nt">esp32</span><span class="p">:</span>
<span class="w"> </span><span class="nt">variant</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ESP32C3</span>
<span class="w"> </span><span class="nt">board</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">esp32dev</span>
<span class="w"> </span><span class="nt">framework</span><span class="p">:</span>
<span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">esp-idf</span>
<span class="w"> </span><span class="nt">sdkconfig_options</span><span class="p">:</span>
<span class="w"> </span><span class="nt">CONFIG_BT_BLE_50_FEATURES_SUPPORTED</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">y</span>
<span class="w"> </span><span class="nt">CONFIG_BT_BLE_42_FEATURES_SUPPORTED</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">y</span>
<span class="w"> </span><span class="nt">CONFIG_ESP_TASK_WDT_TIMEOUT_S</span><span class="p">:</span><span class="w"> </span><span class="s">"10"</span><span class="w"> </span>
<span class="nt">logger</span><span class="p">:</span>
<span class="nt">api</span><span class="p">:</span>
<span class="nt">ota</span><span class="p">:</span>
<span class="nt">button</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">platform</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">safe_mode</span>
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">${display_name} (Safe Mode)</span>
<span class="c1"># 记得改WiFi和密码</span>
<span class="nt">wifi</span><span class="p">:</span>
<span class="w"> </span><span class="nt">ssid</span><span class="p">:</span><span class="w"> </span><span class="s">"Your</span><span class="nv"> </span><span class="s">SSID"</span>
<span class="w"> </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="s">"Your</span><span class="nv"> </span><span class="s">secret</span><span class="nv"> </span><span class="s">password"</span>
<span class="w"> </span><span class="c1">#manual_ip:</span>
<span class="w"> </span><span class="c1"># static_ip: !secret ip_esp32c3_btproxy</span>
<span class="w"> </span><span class="c1"># gateway: !secret ip_gateway</span>
<span class="w"> </span><span class="c1"># subnet: !secret ip_subnet</span>
<span class="w"> </span><span class="c1"># dns1: !secret ip_dns1</span>
<span class="nt">esp32_ble_tracker</span><span class="p">:</span>
<span class="w"> </span><span class="nt">scan_parameters</span><span class="p">:</span>
<span class="c1"># 可能需要调参</span>
<span class="c1"># interval: 1100ms</span>
<span class="c1"># window: 1100ms</span>
<span class="w"> </span><span class="nt">active</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="nt">bluetooth_proxy</span><span class="p">:</span>
<span class="w"> </span><span class="nt">active</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="c1">#### 下面是给显示器模块配置的 ###</span>
<span class="c1"># 五个按钮</span>
<span class="c1"># 由于 ESPHome 把 GPIO12、GPIO13强制占用了(原本控制Flash的),所以有一个按钮不能用。</span>
<span class="nt">binary_sensor</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">platform</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gpio</span>
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">"Up</span><span class="nv"> </span><span class="s">Button"</span>
<span class="w"> </span><span class="nt">pin</span><span class="p">:</span>
<span class="w"> </span><span class="nt">number</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO9</span>
<span class="w"> </span><span class="nt">inverted</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">mode</span><span class="p">:</span>
<span class="w"> </span><span class="nt">input</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">pullup</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">platform</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gpio</span>
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">"Down</span><span class="nv"> </span><span class="s">Button"</span>
<span class="w"> </span><span class="nt">pin</span><span class="p">:</span>
<span class="w"> </span><span class="nt">number</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO5</span>
<span class="w"> </span><span class="nt">inverted</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">mode</span><span class="p">:</span>
<span class="w"> </span><span class="nt">input</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">pullup</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w w-Error"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">platform</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gpio</span>
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">"Left</span><span class="nv"> </span><span class="s">Button"</span>
<span class="w"> </span><span class="nt">pin</span><span class="p">:</span>
<span class="w"> </span><span class="nt">number</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO8</span>
<span class="w"> </span><span class="nt">inverted</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">mode</span><span class="p">:</span>
<span class="w"> </span><span class="nt">input</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">pullup</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="c1"># - platform: gpio</span>
<span class="c1"># name: "Right Button"</span>
<span class="c1"># pin:</span>
<span class="c1"># number: GPIO13 # this pin is spared on CORE-ESP32C3</span>
<span class="c1"># inverted: true</span>
<span class="c1"># mode:</span>
<span class="c1"># input: true</span>
<span class="c1"># pullup: true</span>
<span class="w"> </span><span class="w w-Error"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">platform</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gpio</span>
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">"Center</span><span class="nv"> </span><span class="s">Button"</span>
<span class="w"> </span><span class="nt">pin</span><span class="p">:</span>
<span class="w"> </span><span class="nt">number</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO4</span>
<span class="w"> </span><span class="nt">inverted</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">mode</span><span class="p">:</span>
<span class="w"> </span><span class="nt">input</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">pullup</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="c1"># 显示器SPI</span>
<span class="nt">spi</span><span class="p">:</span>
<span class="w"> </span><span class="nt">clk_pin</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO2</span>
<span class="w"> </span><span class="nt">mosi_pin</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO3</span>
<span class="c1"># 来点显示的东西</span>
<span class="c1"># 可惜写这个的时间貌似还没支持</span>
<span class="c1">#time:</span>
<span class="c1"># - platform: sntp</span>
<span class="c1"># id: sntp_time</span>
<span class="nt">font</span><span class="p">:</span><span class="w"> </span><span class="c1"># 字体需要自己拷贝一份哦,下面是相对于该配置文件的位置</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">file</span><span class="p">:</span><span class="w"> </span><span class="s">"fonts/iosevka-regular.ttf"</span>
<span class="w"> </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">my_font</span>
<span class="c1"># display ST7735</span>
<span class="nt">display</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">platform</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">st7735</span>
<span class="w"> </span><span class="nt">model</span><span class="p">:</span><span class="w"> </span><span class="s">"INITR_MINI160X80"</span>
<span class="w"> </span><span class="nt">reset_pin</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO10</span>
<span class="w"> </span><span class="nt">cs_pin</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO7</span>
<span class="w"> </span><span class="nt">dc_pin</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">GPIO6</span>
<span class="w"> </span><span class="nt">device_width</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">80</span>
<span class="w"> </span><span class="nt">device_height</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">160</span>
<span class="w"> </span><span class="nt">col_start</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">0</span>
<span class="w"> </span><span class="nt">row_start</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">0</span>
<span class="w"> </span><span class="nt">use_bgr</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="w"> </span><span class="nt">lambda</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">|-</span>
<span class="w"> </span><span class="no">it.print(0, 1, id(my_font), "Hello");</span>
<span class="w"> </span><span class="no">it.print(0, 22, id(my_font), "World!");</span>
<span class="w"> </span><span class="no">// it.strftime(0, 35, id(my_font), "%Y-%m-%d %H:%M", id(sntp_time).now());</span>
</code></pre></div>
<p>接上设备,执行下面的命令刷机,就可以在Home Assistant里配置了。</p>
<div class="highlight"><pre><span></span><code>esphome<span class="w"> </span>run<span class="w"> </span>ble-gateway.yaml<span class="w"> </span>--device<span class="o">=</span>/dev/ttyACM0
</code></pre></div>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>记得先激活虚拟环境!</p>
</div>为CircuitPython编写专用的C功能模块2022-12-03T00:00:00+08:002022-12-03T00:00:00+08:00tag:techie-s.work,2022-12-03:/posts/2022/12/write-a-c-module-for-circuitpython/
<h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>自己的机械键盘需要一个高效、无阻塞的扫键模块,Makerdiary的既有实现不能满足我的需求。</p>
<h2 id="结构">结构<a class="headerlink" href="#结构" title="Permanent link">¶</a></h2>
<p>通过参考 <code>keypad</code>、<code>digitalio</code> 以及Makerdiary的 <code>matrix</code> 模块,我发现对于CircuitPython 7.x 有如下的代码结构安排:</p>
<ul>
<li><code>shared-bindings</code>:存放模块Python接口的定义,和为Python开发C模块很像;</li>
<li><code>shared-module</code>:实现与平台无关的模块存放其具体实现的地方;</li>
<li><code>ports/*/common-hal</code>:实现与平台无关的模块存放其具体实现的地方;</li>
<li><code>ports/*/boards</code>:某类平台的板极定义,可以用来调整特定板子的编译配置;</li>
<li><code>py/circuitpy_defns.mk</code>:大概是整体编译配置,通过添加内容让编译工具识别到添加的代码;</li>
<li><code>py/circuitpy_mpconfig.mk</code>:大概是整体编译选项配置,通过添加内容可以添加所需的选项开关;</li>
</ul>
<p>那么实现某个模块的流程大致如下:</p>
<ol>
<li>确认Python接口,并将初步定义在 <code>shared-bindings</code> 中创建好;</li>
<li>确认该模块是否与平台强相关:<ol>
<li>若只需CircuitPython标准接口或其它模块暴露的接口,可以将实现放在 …</li></ol></li></ol>
<h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>自己的机械键盘需要一个高效、无阻塞的扫键模块,Makerdiary的既有实现不能满足我的需求。</p>
<h2 id="结构">结构<a class="headerlink" href="#结构" title="Permanent link">¶</a></h2>
<p>通过参考 <code>keypad</code>、<code>digitalio</code> 以及Makerdiary的 <code>matrix</code> 模块,我发现对于CircuitPython 7.x 有如下的代码结构安排:</p>
<ul>
<li><code>shared-bindings</code>:存放模块Python接口的定义,和为Python开发C模块很像;</li>
<li><code>shared-module</code>:实现与平台无关的模块存放其具体实现的地方;</li>
<li><code>ports/*/common-hal</code>:实现与平台无关的模块存放其具体实现的地方;</li>
<li><code>ports/*/boards</code>:某类平台的板极定义,可以用来调整特定板子的编译配置;</li>
<li><code>py/circuitpy_defns.mk</code>:大概是整体编译配置,通过添加内容让编译工具识别到添加的代码;</li>
<li><code>py/circuitpy_mpconfig.mk</code>:大概是整体编译选项配置,通过添加内容可以添加所需的选项开关;</li>
</ul>
<p>那么实现某个模块的流程大致如下:</p>
<ol>
<li>确认Python接口,并将初步定义在 <code>shared-bindings</code> 中创建好;</li>
<li>确认该模块是否与平台强相关:<ol>
<li>若只需CircuitPython标准接口或其它模块暴露的接口,可以将实现放在 <code>shared-module</code> 中;</li>
<li>若确认实现与平台相关、用到了部分HAL提供的特殊方法,可以将实现放在对应的 <code>ports/*/common-hal</code> 中;</li>
</ol>
</li>
<li>实现模块具体细节;</li>
<li>修改 <code>py/circuitpy_defns.mk</code> 和 <code>py/circuitpy_mpconfig.mk</code> 以添加对应模块的配置信息;</li>
<li>修改 <code>ports/*/boards</code> 中对应开发板的配置,开启所需模块;</li>
<li>编译、测试与后续工作;</li>
</ol>Tinker Board 2S 折腾2022-11-08T00:00:00+08:002022-11-08T00:00:00+08:00tag:techie-s.work,2022-11-08:/posts/2022/11/hands-on-tinker-board-2s/
<p><em><strong>WIP</strong></em></p>
<h2 id="tinkerboard2">TinkerBoard2<a class="headerlink" href="#tinkerboard2" title="Permanent link">¶</a></h2>
<p>这是出于某种原因从网友手上收来的一块板子,很漂亮,看着也很强悍。但是吧,官方支持的内核、RootFS什么的都挺老的(opinioned),实在满足不了我折腾的需求。</p>
<h2 id="相关文档与代码资料">相关文档与代码资料<a class="headerlink" href="#相关文档与代码资料" title="Permanent link">¶</a></h2>
<p>除了使用说明 <sup id="fnref:tb2-manual"><a class="footnote-ref" href="#fn:tb2-manual">1</a></sup>,还可以看一下 Schematics <sup id="fnref:tb2-schematics"><a class="footnote-ref" href="#fn:tb2-schematics">2</a></sup>。Armbian <sup id="fnref:armbian-build"><a class="footnote-ref" href="#fn:armbian-build">3</a></sup> 最近(2022-11-08)也有开发者在投入资源适配。</p>
<p>这个板子的调试串口藏得真好……我直接忽略了 (藏在USB之间) 。</p>
<h2 id="研究路径">研究路径<a class="headerlink" href="#研究路径" title="Permanent link">¶</a></h2>
<ul>
<li>rk3399 启动流程 (官方wiki其它地方也能看看) <ul>
<li>http://opensource.rock-chips.com/wiki_Boot_option</li>
<li>不能指望 Asus 能做什么, 除了他们那个系统 (TinkerOS) , 不过我是不用的</li>
</ul>
</li>
<li>一个不错的研究笔记<ul>
<li><a href="https://github.com/lanseyujie/tn3399_v3">TN3399_V3</a></li>
<li>好歹都是 RK3399 的板子</li>
</ul>
</li>
</ul>
<h2 id="原版-tinkeros-说明">原版 TinkerOS 说明<a class="headerlink" href="#原版-tinkeros-说明" title="Permanent link">¶</a></h2>
<h3 id="u-boot">U-Boot<a class="headerlink" href="#u-boot" title="Permanent link">¶</a></h3>
<p>Arm64 启动时涉及到 ATF (Arm Trusted Firmware), 这里先提一嘴, 也就是要注意下启动流程</p>
<p>Tinker Board 2S 的 U-Boot 改自 Rockchip 维护的版本, <strong>默认调试接口的波特率是 115200</strong></p>
<p>RK3399 优先从 eMMC 启动, 因此为了更方 …</p>
<p><em><strong>WIP</strong></em></p>
<h2 id="tinkerboard2">TinkerBoard2<a class="headerlink" href="#tinkerboard2" title="Permanent link">¶</a></h2>
<p>这是出于某种原因从网友手上收来的一块板子,很漂亮,看着也很强悍。但是吧,官方支持的内核、RootFS什么的都挺老的(opinioned),实在满足不了我折腾的需求。</p>
<h2 id="相关文档与代码资料">相关文档与代码资料<a class="headerlink" href="#相关文档与代码资料" title="Permanent link">¶</a></h2>
<p>除了使用说明 <sup id="fnref:tb2-manual"><a class="footnote-ref" href="#fn:tb2-manual">1</a></sup>,还可以看一下 Schematics <sup id="fnref:tb2-schematics"><a class="footnote-ref" href="#fn:tb2-schematics">2</a></sup>。Armbian <sup id="fnref:armbian-build"><a class="footnote-ref" href="#fn:armbian-build">3</a></sup> 最近(2022-11-08)也有开发者在投入资源适配。</p>
<p>这个板子的调试串口藏得真好……我直接忽略了 (藏在USB之间) 。</p>
<h2 id="研究路径">研究路径<a class="headerlink" href="#研究路径" title="Permanent link">¶</a></h2>
<ul>
<li>rk3399 启动流程 (官方wiki其它地方也能看看) <ul>
<li>http://opensource.rock-chips.com/wiki_Boot_option</li>
<li>不能指望 Asus 能做什么, 除了他们那个系统 (TinkerOS) , 不过我是不用的</li>
</ul>
</li>
<li>一个不错的研究笔记<ul>
<li><a href="https://github.com/lanseyujie/tn3399_v3">TN3399_V3</a></li>
<li>好歹都是 RK3399 的板子</li>
</ul>
</li>
</ul>
<h2 id="原版-tinkeros-说明">原版 TinkerOS 说明<a class="headerlink" href="#原版-tinkeros-说明" title="Permanent link">¶</a></h2>
<h3 id="u-boot">U-Boot<a class="headerlink" href="#u-boot" title="Permanent link">¶</a></h3>
<p>Arm64 启动时涉及到 ATF (Arm Trusted Firmware), 这里先提一嘴, 也就是要注意下启动流程</p>
<p>Tinker Board 2S 的 U-Boot 改自 Rockchip 维护的版本, <strong>默认调试接口的波特率是 115200</strong></p>
<p>RK3399 优先从 eMMC 启动, 因此为了更方便地测试 uboot, 必须把 eMMC 上的 uboot 破
坏掉</p>
<p>所以我在 uboot 里执行了下面的指令, 然后我就能愉快地从 SD 卡启动了。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 0x40 是 SPL/TPL(idbloader.img)的起址</span>
mmc<span class="w"> </span>erase<span class="w"> </span>0x40<span class="w"> </span><span class="m">2</span>
</code></pre></div>
<h3 id="kernel">Kernel<a class="headerlink" href="#kernel" title="Permanent link">¶</a></h3>
<p>版本 4.4 没得说, 毕竟 BSP 给的就是 4.4, <s>晚点给它干上去</s>我做不到</p>
<h2 id="自行构建-u-bootkernelrootfs">自行构建 u-boot、kernel、rootfs<a class="headerlink" href="#自行构建-u-bootkernelrootfs" title="Permanent link">¶</a></h2>
<h3 id="atftf-a">ATF(TF-A)<a class="headerlink" href="#atftf-a" title="Permanent link">¶</a></h3>
<p>一个初始化设备和EL2的程序(大概吧,不太懂)。</p>
<div class="highlight"><pre><span></span><code>git clone https://github.com/ARM-software/arm-trusted-firmware.git
cd arm-trusted-firmware
make realclean
make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3399
</code></pre></div>
<h3 id="u-boot_1">U-Boot<a class="headerlink" href="#u-boot_1" title="Permanent link">¶</a></h3>
<p>需要适配(写设备树、调整驱动)。看了下 Armbian 最近(2022-11-08)的commit,适配工作正在有序进行(而我只是勉强能明白他们的补丁想干嘛)。
u-boot版本为2022.07。</p>
<p>目前(2022-11-08)看来,HDMI、USB都有点问题。通过修改配置,可以轻松地使用 SPI NOR Flash 存放 u-Boot,这样就不依赖
外置存储器了。</p>
<p>如何配置暂时不记录。编译成功后生成适用于 flashrom 的脚本如下:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">subprocess</span>
<span class="c1"># gen spi image</span>
<span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">([</span><span class="s2">"tools/mkimage"</span><span class="p">,</span> <span class="s2">"-n"</span><span class="p">,</span> <span class="s2">"rk3399"</span><span class="p">,</span> <span class="s1">'-T'</span><span class="p">,</span> <span class="s1">'rkspi'</span><span class="p">,</span> <span class="s1">'-d'</span><span class="p">,</span> <span class="s1">'tpl/u-boot-tpl.bin:spl/u-boot-spl.bin'</span><span class="p">,</span> <span class="s1">'idbloader-spi.img'</span><span class="p">])</span>
<span class="c1"># LOAD</span>
<span class="c1">## load spi tpl/spl</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'idbloader-spi.img'</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">tplspl</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="c1">## load itb</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'u-boot.itb'</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">uboot</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="c1">## CONCATENATE</span>
<span class="c1">### offset in KB</span>
<span class="n">FIT_OFFSET</span> <span class="o">=</span> <span class="mi">512</span> <span class="c1"># 这里请根据设备树配置项目 `u-boot,spl-payload-offset' 和 `CONFIG_SYS_SPI_FLASH_U_BOOT_OFFS' 等配置进行修改</span>
<span class="n">FLASH_SIZE</span> <span class="o">=</span> <span class="mi">8192</span> <span class="c1"># Flash 大小,只提供rom数据的情况下,flashrom要求rom数据和rom大小一致。</span>
<span class="n">FIT_OFFSET_BYTES</span> <span class="o">=</span> <span class="n">FIT_OFFSET</span> <span class="o">*</span> <span class="mi">1024</span>
<span class="n">FLASH_SIZE_BYTES</span> <span class="o">=</span> <span class="n">FLASH_SIZE</span> <span class="o">*</span> <span class="mi">1024</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">tplspl</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0xff</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">FIT_OFFSET_BYTES</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">tplspl</span><span class="p">))])</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">data</span> <span class="o">+</span> <span class="n">uboot</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0xff</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">FLASH_SIZE_BYTES</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">uboot</span><span class="p">))])</span>
<span class="c1">## WRITE</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"spi-nor.bin"</span><span class="p">,</span> <span class="s1">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="c1">## FLASH, check your permission</span>
<span class="c1"># subprocess.run(['flashrom', '-p', 'ch341a_spi', '-c', "MX25L6406E/MX25L6408E" '-w', 'spi-nor.bin'])</span>
</code></pre></div>
<h3 id="rootfs">rootfs<a class="headerlink" href="#rootfs" title="Permanent link">¶</a></h3>
<p>没啥难度,对于Debian/Ubuntu 有 debootstrap,对于 ArchLinux 有 ArchLinuxARM,不同发行版的RootFS有很多对应制作工具。略。</p>
<h3 id="kernel_1">kernel<a class="headerlink" href="#kernel_1" title="Permanent link">¶</a></h3>
<p>没搞定,旧的能用继续用旧的,略。</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:tb2-manual">
<p>https://tinker-board.asus.com/images/doc/download/E17169_Tinker_Board_2_2S_UM_WEB.pdf <a class="footnote-backref" href="#fnref:tb2-manual" title="回到 #1">↩</a></p>
</li>
<li id="fn:tb2-schematics">
<p>https://tinker-board.asus.com/images/doc/download/Tinker_Board_2_Schematics_20201214.pdf <a class="footnote-backref" href="#fnref:tb2-schematics" title="回到 #2">↩</a></p>
</li>
<li id="fn:armbian-build">
<p>https://github.com/armbian/build <a class="footnote-backref" href="#fnref:armbian-build" title="回到 #3">↩</a></p>
</li>
</ol>
</div>3D打印机折腾 - XYZPrinting da Vinci 1.02022-10-30T00:00:00+08:002022-11-10T00:00:00+08:00tag:techie-s.work,2022-10-30:/posts/2022/10/hack-on-xyzprinting-davinci-10/
<p><img alt="最终修改完成的机器" src="https://techie-s.work/posts/2022/10/hack-on-xyzprinting-davinci-10/PIC-01-final-result.jpg"/></p>
<h2 id="前言">前言<a class="headerlink" href="#前言" title="Permanent link">¶</a></h2>
<p>因为某种缘分,我上了一个库存3D打印机的车。买到的打印机是 XYZPrinting 出品的 da Vinci 系列,版本1.0,下面简称达芬奇。现在这个系列都加上了前缀“original”。</p>
<p>该打印机用的是私有协议,而我不喜欢被私有上位机控制的感觉(GNU/Linux上操作、配合其它软件时会很麻烦)。于是折腾了“一下下”。</p>
<p>幸运的是我拿到的版本比较老,可以更换电机驱动器。</p>
<h2 id="机器上手">机器上手<a class="headerlink" href="#机器上手" title="Permanent link">¶</a></h2>
<p>这个设备真的比想象的大不少……明明工作空间只有<code>200*200*200</code>的说……占地差不多 <code>500*600</code>,高度 <code>500</code>,单位都是毫米。</p>
<p>比较可惜的是,设备到我手上时已经因运输不当损坏了多个部件,例如柜门转轴、上翻盖转轴、热床支架、热床玻璃、Z轴底座等等。总之先尝试调平、打印了内置测试件,感觉还行。</p>
<p><img alt="打印机内部" src="https://techie-s.work/posts/2022/10/hack-on-xyzprinting-davinci-10/PIC-01-inside-shell.jpg"/></p>
<p>可惜这个调平不是长久之计,网 …</p>
<p><img alt="最终修改完成的机器" src="https://techie-s.work/posts/2022/10/hack-on-xyzprinting-davinci-10/PIC-01-final-result.jpg"/></p>
<h2 id="前言">前言<a class="headerlink" href="#前言" title="Permanent link">¶</a></h2>
<p>因为某种缘分,我上了一个库存3D打印机的车。买到的打印机是 XYZPrinting 出品的 da Vinci 系列,版本1.0,下面简称达芬奇。现在这个系列都加上了前缀“original”。</p>
<p>该打印机用的是私有协议,而我不喜欢被私有上位机控制的感觉(GNU/Linux上操作、配合其它软件时会很麻烦)。于是折腾了“一下下”。</p>
<p>幸运的是我拿到的版本比较老,可以更换电机驱动器。</p>
<h2 id="机器上手">机器上手<a class="headerlink" href="#机器上手" title="Permanent link">¶</a></h2>
<p>这个设备真的比想象的大不少……明明工作空间只有<code>200*200*200</code>的说……占地差不多 <code>500*600</code>,高度 <code>500</code>,单位都是毫米。</p>
<p>比较可惜的是,设备到我手上时已经因运输不当损坏了多个部件,例如柜门转轴、上翻盖转轴、热床支架、热床玻璃、Z轴底座等等。总之先尝试调平、打印了内置测试件,感觉还行。</p>
<p><img alt="打印机内部" src="https://techie-s.work/posts/2022/10/hack-on-xyzprinting-davinci-10/PIC-01-inside-shell.jpg"/></p>
<p>可惜这个调平不是长久之计,网上搜索后没有找到合适的热床组件,找商家补发了一个备件。在此期间先研究一下通信协议、耗材限制吧。</p>
<p>在此先记录一下我对于3D打印机到手后的操作建议:</p>
<ol>
<li>检查机械结构</li>
<li>上电检查状况</li>
<li>检查是否校准并视情况调整(例如使用自动校准功能)</li>
<li>装料</li>
<li>打印测试</li>
</ol>
<h2 id="耗材锁与上位机协议">耗材锁与上位机协议<a class="headerlink" href="#耗材锁与上位机协议" title="Permanent link">¶</a></h2>
<p>我机器随附光盘的程序没法正常运行……网上找到的官方程序也没法成功地在虚拟机里打印…… <del>可惜了</del> 那正好,必须折腾起来。好在网友留下了大量研究资料<sup id="fnref:filament-reseter"><a class="footnote-ref" href="#fn:filament-reseter">1</a></sup> <sup id="fnref:minimover-doc"><a class="footnote-ref" href="#fn:minimover-doc">2</a></sup>(相关论坛资料主要发布于2016年前后,很久远,说明这机器年岁也很高)。</p>
<p>“简单”看了一下,就是用特定串口命令上传文件呗,用Py快速实现了一个小工具<sup id="fnref:hyx0329-davinci-utils"><a class="footnote-ref" href="#fn:hyx0329-davinci-utils">3</a></sup>。
第一次上传gcode时一个不小心让打印头撞了一下平台……这好像也给后面打印立方体测试时埋下了隐患,导致模型某个侧面疯狂突出。</p>
<p>用这个方法打印模型需要用USB连接串口。需要注意的是,没调整好的话床很容易撞喷头,具体我就不详细写了。</p>
<h2 id="刷机-repetier-固件">刷机 - Repetier 固件<a class="headerlink" href="#刷机-repetier-固件" title="Permanent link">¶</a></h2>
<p><img alt="主板照片,2013年末的板子" src="https://techie-s.work/posts/2022/10/hack-on-xyzprinting-davinci-10/PIC-01-mainboard.jpg"/></p>
<p>工具写到一半发现有个现成的开源固件。刷机也很方便,直接短接、清除原固件后就可以直接用 Arduino IDE 或 PlatformIO IDE 上传。但是在使用过程中会遇到一些小问题。
中间虽然尝试了备份原固件,但发现JTAG接口并不能用=_=,算了反正原装固件是枷锁。</p>
<p>我的刷机过程如下,其它设备会略有不同,请参考固件说明。</p>
<ol>
<li>检查型号兼容性</li>
<li>断电->短接JP1->上电->过几秒断电->断开JP1</li>
<li>Clone并打开项目文件夹,修改 <code>src/ArduinoDUE/Repetier/Configuration.h</code> 中的配置</li>
<li>PlatformIO IDE 编译、上传,随后进行设备校准与检测</li>
<li>根据实际设备情况调整参数</li>
<li>重新编译、上传,校准与检测</li>
<li>终于测试通过了,正常使用</li>
</ol>
<p>我的挤出头边上的探针(probe)长度与测试点位置的位置好像和程序预定义的有点不同,在使用过程中发现了一些问题。不得不修改了一下代码。</p>
<h2 id="加装wifi模块">加装WiFi模块<a class="headerlink" href="#加装wifi模块" title="Permanent link">¶</a></h2>
<p>ESP3D这个东西还是有点意思的。哦,装了这个玩意儿可能会让USB串口不正常。</p>
<p>以 ESP12F 为例,2.1.x 固件的使用注意点:</p>
<ul>
<li><del>供电得足……</del></li>
<li>在 <code>esp3d/config.h</code> 中对你需要的功能进行调整</li>
<li>刷机前最好用esptool把模块的存储器擦除一遍,以清除所有会影响到的配置</li>
<li>刷机后上机工作不太正常是正常的,需要简单配置一下<ul>
<li>连接热点后上传index.html.gz,找不到可以fzf/find一下</li>
<li>走一遍tour,配置工作模式</li>
</ul>
</li>
<li>2.x成熟一点,指引直观易用;3.0更先进,但是不知道什么时候才有稳定版,没什么指引不太方便</li>
<li>打印机固件也要开启WiFi功能哦</li>
<li>Repetier for davinci 最大波特率只有 500000,如果你调高了,ESP3D和打印机固件会自动将波特率降下来</li>
</ul>
<p>硬件连接参考如下(达芬奇1.0 跳线JP1版),可以先看看原项目的示意图<sup id="fnref:esp3d-wiring-davinci"><a class="footnote-ref" href="#fn:esp3d-wiring-davinci">5</a></sup>。<strong>原项目建议串口加一对分压电阻把RX电平降下来</strong>。
该串口的波特率和 USB 串口的波特率(打印机设置的)一样。</p>
<div class="highlight"><pre><span></span><code>+---------+ +---------------+
| GND |___ ___|1 to RX |
| | \/ | |
| TXD |_ /\ _|2 to VCC(3.3V) |
| ESP | \/ \/ | |
| RXD |_/\ /\_|3 to GND |
| | \/ | |
| VCC |___/\___|4 to TX |
+---------+ +---------------+
</code></pre></div>
<p>测试的时候要同时面对好几个疑问真的头疼死了……总之装上去了!</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>后续升级达芬奇固件时,一定把ESP3D取下来!不然会有影响!</p>
</div>
<p><img alt="将ESP12F自制小板安装在达芬奇内部" src="https://techie-s.work/posts/2022/10/hack-on-xyzprinting-davinci-10/PIC-01-install-wifi-module.jpg"/></p>
<div class="admonition note">
<p class="admonition-title">关于项目仓库提供的连接示意图</p>
<p>ESP3D项目地址中达芬奇1.0/2.0的连接示意图标注的是连接方法,不是引脚定义。即RX接RX,TX接TX。</p>
</div>
<div class="admonition attention">
<p class="admonition-title">达芬奇装WiFi模块供电</p>
<p>不要用LDO(或者说不要用低效率的LDO),原本供电刚好 <strong>3.3V</strong> 且足够ESP8266开热点,过一个LDO可能就不够用了。如果版型和我的一样,串口可以直接对接,电平都是3.3V不会烧。</p>
</div>
<h2 id="换电机驱动">换电机驱动<a class="headerlink" href="#换电机驱动" title="Permanent link">¶</a></h2>
<p>想用TMS2209换掉原来的A4988。达芬奇的电机驱动板排针间距大概7个单位距离,比常见的5个单位距离宽一些(1单位距离为2.54mm,排针间距),需要改造。
目前想到的办法有魔改双层转接器和自制转接板。结果咧,转接器(双层排母但是两层一样)都做好了,但是怕操作失误把板子弄坏,还是放弃了(网友称其‘fragile’)。
<del>以后有机会用自制转接板改吧,感觉稳妥些。</del> 事实证明 <del>直接直连转接的话需要改一下电机插头线序</del> 只是方向相反而已,另外做的转接板线接错了直接报废。
另外说一句,我搞不太懂这些步进电机的1A2B什么的编号,好像有两种标准很坑人(一种字母对应线圈/相、数字对应正负,另一种反过来)。
只要知道接对了电机会动,接错了电机会原地抖动,估计就没啥问题了。</p>
<p><img alt="魔改的双排母,其实差一点就很合适了" src="https://techie-s.work/posts/2022/10/hack-on-xyzprinting-davinci-10/PIC-01-custom-connector.jpg"/></p>
<p><del>嘉立创免费打样了一组转接板,配合长排针,把原来的母座改了,同时兼容原版和常见的TMC2209</del> 总之就是直接报废。电机驱动根据soliforum的网友提供的信息<sup id="fnref:step-motor-info"><a class="footnote-ref" href="#fn:step-motor-info">6</a></sup>来看,
驱动电流不应该超过1A,大概在0.8A左右合适。100mΩ的情况下调整电压差不多1V应该就行了。</p>
<p>提前预留了活动空间并测试了一下电机驱动方向,发现恰好相反,那么稍微修改一下固件把它矫正过来~</p>
<div class="highlight"><pre><span></span><code><span class="c1">// 需要修改的内容如下,给出的是使用TMC2209时的值</span>
<span class="cp">#define INVERT_X_DIR false</span>
<span class="cp">#define INVERT_Y_DIR true</span>
<span class="cp">#define INVERT_Z_DIR false</span>
</code></pre></div>
<p>继续用原来的长排针,发现单位移动距离好像对,可能是微步设置出现问题,不管三七二十一先重新做了“可靠的”转换器,又实验了好几次,非常不稳定,中间还不小心插反了一个模块(-11CNY (ಥ_ಥ)),只剩3个可用了……
一筹莫展之际,想到了板子MS1、MS2、MS3是没有隔离的,而且TMC2209的MS3是个TX(输出模式的IO),说不定影响到了微步设置?嗯,咱把对应的引脚剪掉吧!还好只是剪转接器的引脚。oh,还真的是这样!一切终于正常了。</p>
<p><img alt="电机驱动模块与转接器及引脚裁剪示意图" src="https://techie-s.work/posts/2022/10/hack-on-xyzprinting-davinci-10/PIC-01-tmc2209-module.jpg"/></p>
<p>用TMC2209替换A4988的感受嘛,声音确实小了很多,还是挺明显的。在打印精度上基本没有多少提升=_=。总体上确实有提升就是啦。</p>
<h2 id="关于校准">关于校准<a class="headerlink" href="#关于校准" title="Permanent link">¶</a></h2>
<p>平台平衡校准,配合纸片手动调平比根据自动测量数据调整要靠谱得多(说明自动测量是废物/当然也不完全是)。</p>
<p>我建议的校准流程:</p>
<ul>
<li>平台校准<ol>
<li>清理平台和打印头,这一步在手动校准程序启动时会有个选项;</li>
<li>手动校准(Manual leveling),如果觉得不放心可以多来两次练练手(按照固件提示操作走完流程就行);</li>
<li>重新检测Zmin(Zmin evaluation);</li>
<li>打印测试件验证校准状况。</li>
</ol>
</li>
<li>挤出机校准<ol>
<li>在耗材上做好长度标记,然后按程序挤出一段耗材(比如100mm),测量实际挤出长度;</li>
<li>然后嘛就是一个比例计算, <span class="math">\(S_{new} = S_{old} \frac{E_{program}}{E_{actual}}\)</span>;</li>
<li>也可以参考一下网上资料 <sup id="fnref:steps-per-mm-calculator"><a class="footnote-ref" href="#fn:steps-per-mm-calculator">7</a></sup>。</li>
</ol>
</li>
</ul>
<h2 id="挤出头与热端更换">挤出头与热端更换<a class="headerlink" href="#挤出头与热端更换" title="Permanent link">¶</a></h2>
<p>目前看到有网友换了 MK8 或 E3D 的热端<sup id="fnref:davinci-mk8-hotend"><a class="footnote-ref" href="#fn:davinci-mk8-hotend">8</a></sup> <sup id="fnref:davinci-hotend-discussions"><a class="footnote-ref" href="#fn:davinci-hotend-discussions">9</a></sup>。以后我得尝试一下。热端拆解得在网上找视频了。看起来以后可以直接换MK8热端。</p>
<h2 id="温度传感器更换">温度传感器更换<a class="headerlink" href="#温度传感器更换" title="Permanent link">¶</a></h2>
<p>参考开源固件讨论区<sup id="fnref:davinci-repetier-change-thermistor"><a class="footnote-ref" href="#fn:davinci-repetier-change-thermistor">10</a></sup>,有两种做法:一是改主板并更换传感器从而能用既有的通用数据,二是自己测对应的温度数据。</p>
<h2 id="其他人的改造">其他人的改造<a class="headerlink" href="#其他人的改造" title="Permanent link">¶</a></h2>
<p>发现有人直接把主板换掉魔改了一番<sup id="fnref:frankenvinci"><a class="footnote-ref" href="#fn:frankenvinci">11</a></sup>。我暂时没这个动力……先好好用用看吧,要换也先换热端和电机驱动了。</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:filament-reseter">
<p><a href="https://github.com/voltivo/davinci_filament_reset_arduino">耗材重置器(仅Arduino AVR测试可用)</a> <a class="footnote-backref" href="#fnref:filament-reseter" title="回到 #1">↩</a></p>
</li>
<li id="fn:minimover-doc">
<p><a href="https://github.com/reality-boy/miniMover/tree/master/docs">miniMover项目文档,包括部分协议文档</a> <a class="footnote-backref" href="#fnref:minimover-doc" title="回到 #2">↩</a></p>
</li>
<li id="fn:hyx0329-davinci-utils">
<p><a href="https://github.com/hyx0329/xyzdavinci-3d-printer-utils">达芬奇小工具(但是不会继续开发了)</a> <a class="footnote-backref" href="#fnref:hyx0329-davinci-utils" title="回到 #3">↩</a></p>
</li>
<li id="fn:repetier4davinci">
<p><a href="https://github.com/luc-github/Repetier-Firmware-4-Davinci">Repetier-Firmware-4-Davinci</a> <a class="footnote-backref" href="#fnref:repetier4davinci" title="回到 #4">↩</a></p>
</li>
<li id="fn:esp3d-wiring-davinci">
<p><a href="https://github.com/luc-github/ESP3D/wiki/Hardware-connection#davinci-1020-board">ESP3D硬件连接-davinci1.0/2.0</a> <a class="footnote-backref" href="#fnref:esp3d-wiring-davinci" title="回到 #5">↩</a></p>
</li>
<li id="fn:step-motor-info">
<p><a href="https://www.soliforum.com/post/121940/#p121940">步进电机型号信息来源</a> <a class="footnote-backref" href="#fnref:step-motor-info" title="回到 #6">↩</a></p>
</li>
<li id="fn:steps-per-mm-calculator">
<p>https://www.maxzprint.com.au/stepps-per-mm-calculator/ <a class="footnote-backref" href="#fnref:steps-per-mm-calculator" title="回到 #7">↩</a></p>
</li>
<li id="fn:davinci-mk8-hotend">
<p><a href="https://www.instructables.com/How-to-Modify-a-XYZ-Da-Vinci-3D-Printer-to-Work-Wi/">达芬奇改装MK8热端–Milen(Instructables)</a> <a class="footnote-backref" href="#fnref:davinci-mk8-hotend" title="回到 #8">↩</a></p>
</li>
<li id="fn:davinci-hotend-discussions">
<p><a href="https://www.thingiverse.com/groups/da-vinci/forums/general/topic:7799">一些关于达芬奇热端更换的讨论</a> <a class="footnote-backref" href="#fnref:davinci-hotend-discussions" title="回到 #9">↩</a></p>
</li>
<li id="fn:davinci-repetier-change-thermistor">
<p><a href="https://github.com/luc-github/Repetier-Firmware-4-Davinci/discussions/104">达芬奇使用第三方温度传感器</a> <a class="footnote-backref" href="#fnref:davinci-repetier-change-thermistor" title="回到 #10">↩</a></p>
</li>
<li id="fn:frankenvinci">
<p><a href="https://www.techmonkeybusiness.com/articles/Frankenvinci.html">弗兰肯芬奇(魔改达芬奇)–Frankenvinci</a> <a class="footnote-backref" href="#fnref:frankenvinci" title="回到 #11">↩</a></p>
</li>
</ol>
</div>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>芒果派RISCV:麻雀 MQ Pro 上手体验2022-09-11T00:00:00+08:002022-09-11T00:00:00+08:00tag:techie-s.work,2022-09-11:/posts/2022/09/riscv-mangopi-mq-pro/
<h2 id="芒果派-mq-pro-简介">芒果派 MQ Pro 简介<a class="headerlink" href="#芒果派-mq-pro-简介" title="Permanent link">¶</a></h2>
<p>这是一块以全志D1(RISCV)CPU为核心的迷你开发板,外观规格同树莓派0(不过USB全改
Type-C了,好评)。特性列出如下:</p>
<ul>
<li>CPU:Allwinner D1 (RISCV single core)</li>
<li>Form factor: Raspi-0-like</li>
<li>Peripheral ports: GPIO, I2C & SPI, SDIO, Audio port(record, play),
Video(touch, HDMI, DVP, DSI and LVDS), USB Host, OTG, etc. </li>
<li>Connectivity: WiFi/BT(RTL8723ds)</li>
<li>Memory: 512MB/1GB respectively for MPi-MQ1PL/MPi-MQ1PH</li>
</ul>
<p>粉色油墨、过孔盖油、小尺寸、RISCV、开源、该有的接口都有了,除了性能弱一些,其实
还不错。</p>
<h2 id="自制钥匙串">自制“钥匙串”<a class="headerlink" href="#自制钥匙串" title="Permanent link">¶</a></h2>
<p>嗯因为板子比较小也好看,不想焊接排针,于是非常具有装饰性,做个钥匙串不过分吧?
我用 FreeCAD 按树莓派0的规格绘制了简单的亚克力外壳,用不锈钢齐平螺母和平头螺
丝、尼龙隔离柱和垫片等将开发板夹在了两块亚克力板中间,亚克力板则是做成钥匙扣的
样子。</p>
<p><img alt="将开发板像三明治一样夹在两块亚克力板之间,做成钥匙扣,整体成长方形,但一条短边像弦一样被拉出形成一个三角形孔洞,另一条短边有一个恰好能用于取出MicroSD卡的椭圆形凹陷,钥匙扣四角用螺丝固定" src="https://techie-s.work/posts/2022/09/riscv-mangopi-mq-pro/riscv-mangopi-mq-pro-keychain.jpg"/></p>
<h2 id="简单玩玩安装-archlinux">简单玩玩,安装 ArchLinux<a class="headerlink" href="#简单玩玩安装-archlinux" title="Permanent link">¶</a></h2>
<h3 id="社区力量">社区力量<a class="headerlink" href="#社区力量" title="Permanent link">¶</a></h3>
<p>嗯?好像没有那么简单啊……不过感谢社区的力量<sup id="fnref:rv-img-builder"><a class="footnote-ref" href="#fn:rv-img-builder">1</a></sup>!我成功安装了 ArchLinux …</p>
<h2 id="芒果派-mq-pro-简介">芒果派 MQ Pro 简介<a class="headerlink" href="#芒果派-mq-pro-简介" title="Permanent link">¶</a></h2>
<p>这是一块以全志D1(RISCV)CPU为核心的迷你开发板,外观规格同树莓派0(不过USB全改
Type-C了,好评)。特性列出如下:</p>
<ul>
<li>CPU:Allwinner D1 (RISCV single core)</li>
<li>Form factor: Raspi-0-like</li>
<li>Peripheral ports: GPIO, I2C & SPI, SDIO, Audio port(record, play),
Video(touch, HDMI, DVP, DSI and LVDS), USB Host, OTG, etc. </li>
<li>Connectivity: WiFi/BT(RTL8723ds)</li>
<li>Memory: 512MB/1GB respectively for MPi-MQ1PL/MPi-MQ1PH</li>
</ul>
<p>粉色油墨、过孔盖油、小尺寸、RISCV、开源、该有的接口都有了,除了性能弱一些,其实
还不错。</p>
<h2 id="自制钥匙串">自制“钥匙串”<a class="headerlink" href="#自制钥匙串" title="Permanent link">¶</a></h2>
<p>嗯因为板子比较小也好看,不想焊接排针,于是非常具有装饰性,做个钥匙串不过分吧?
我用 FreeCAD 按树莓派0的规格绘制了简单的亚克力外壳,用不锈钢齐平螺母和平头螺
丝、尼龙隔离柱和垫片等将开发板夹在了两块亚克力板中间,亚克力板则是做成钥匙扣的
样子。</p>
<p><img alt="将开发板像三明治一样夹在两块亚克力板之间,做成钥匙扣,整体成长方形,但一条短边像弦一样被拉出形成一个三角形孔洞,另一条短边有一个恰好能用于取出MicroSD卡的椭圆形凹陷,钥匙扣四角用螺丝固定" src="https://techie-s.work/posts/2022/09/riscv-mangopi-mq-pro/riscv-mangopi-mq-pro-keychain.jpg"/></p>
<h2 id="简单玩玩安装-archlinux">简单玩玩,安装 ArchLinux<a class="headerlink" href="#简单玩玩安装-archlinux" title="Permanent link">¶</a></h2>
<h3 id="社区力量">社区力量<a class="headerlink" href="#社区力量" title="Permanent link">¶</a></h3>
<p>嗯?好像没有那么简单啊……不过感谢社区的力量<sup id="fnref:rv-img-builder"><a class="footnote-ref" href="#fn:rv-img-builder">1</a></sup>!我成功安装了 ArchLinux。</p>
<h3 id="进一步折腾">进一步折腾<a class="headerlink" href="#进一步折腾" title="Permanent link">¶</a></h3>
<p><del>闲着无聊</del> 将 sehraf 的脚本用 Makefile 重新实现了一遍<sup id="fnref:rv-img-builder-makefile"><a class="footnote-ref" href="#fn:rv-img-builder-makefile">2</a></sup>,顺便修了一些bug,都自
动化了。前前后后花了三四天吧。全当学 Makefile 了。</p>
<h2 id="接着玩">接着玩?<a class="headerlink" href="#接着玩" title="Permanent link">¶</a></h2>
<p>emm,暂时没有想法。不过它确实是我的 tiny-tiny-tiny card computer。</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:rv-img-builder">
<p><a href="https://github.com/sehraf/riscv-arch-image-builder">riscv-arch-image-builder by sehraf</a> <a class="footnote-backref" href="#fnref:rv-img-builder" title="回到 #1">↩</a></p>
</li>
<li id="fn:rv-img-builder-makefile">
<p><a href="https://github.com/hyx0329/riscv-archlinux-d1">Makefile 实现的 D1 平台 ArchLinux 镜像构建工具</a> <a class="footnote-backref" href="#fnref:rv-img-builder-makefile" title="回到 #2">↩</a></p>
</li>
</ol>
</div>在MSM8916随身WiFi上玩转Linux - part2 新设备2022-08-07T00:00:00+08:002022-09-11T00:00:00+08:00tag:techie-s.work,2022-08-07:/posts/2022/08/openstick-msm8916-part2-new-device/
<h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>哎呀,剁手了,又买了一个随身WiFi想刷机。不过这次不再是 <code>UFI001C_MB_V01</code> 而是
<code>UFI_16_V3</code>。刷机流程稍有不同。后面又整了台 <code>UFI003</code>,看过发现和 <code>UFI001C</code> 一样。</p>
<h2 id="刷机流程">刷机流程<a class="headerlink" href="#刷机流程" title="Permanent link">¶</a></h2>
<h3 id="备份">备份<a class="headerlink" href="#备份" title="Permanent link">¶</a></h3>
<p>首先是 <strong>备份备份备份</strong>!</p>
<p>比较友好的是,该款版型的系统原生开启高通调试接口,备份基带QCN无需ROOT。老规矩,上
<strong>QPST</strong>。</p>
<p>其次是全盘eMMC备份。这里使用 <strong>Qualcomm Premium Tool</strong> 中备份Block0的功能。</p>
<h3 id="fastboot刷机">fastboot刷机<a class="headerlink" href="#fastboot刷机" title="Permanent link">¶</a></h3>
<p>下面尝试进入fastboot刷机。哎?怎么刷不进去?</p>
<div class="highlight"><pre><span></span><code>% fastboot flash partition gpt_both0.bin
Sending 'partition' (33 KB) OKAY [ 0.005s]
Writing 'partition' FAILED (remote: 'failed to write partition,please contact vendor')
fastboot: error: Command failed
</code></pre></div>
<p>看样子fastboot做了限制加了锁。经过一番研究,发现在MSM8916平台上fastboot在 <code>aboot</code>(appsboot/lk1st) 分
区,而刷GNU/Linux正好也要更新这个分区,干脆直接用 <strong>Qualcomm Premium Tool</strong> 把
该写的 <code>aboot</code> 分区通过9008提前写入。看上去不错,fastboot被替换掉了。</p>
<p>然后看情况稍微修改一下 Debian 的刷机脚本,不过好像不改也行。总之就是自己准备好备份,然后相应刷就行。</p>
<p>接下来的 …</p>
<h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>哎呀,剁手了,又买了一个随身WiFi想刷机。不过这次不再是 <code>UFI001C_MB_V01</code> 而是
<code>UFI_16_V3</code>。刷机流程稍有不同。后面又整了台 <code>UFI003</code>,看过发现和 <code>UFI001C</code> 一样。</p>
<h2 id="刷机流程">刷机流程<a class="headerlink" href="#刷机流程" title="Permanent link">¶</a></h2>
<h3 id="备份">备份<a class="headerlink" href="#备份" title="Permanent link">¶</a></h3>
<p>首先是 <strong>备份备份备份</strong>!</p>
<p>比较友好的是,该款版型的系统原生开启高通调试接口,备份基带QCN无需ROOT。老规矩,上
<strong>QPST</strong>。</p>
<p>其次是全盘eMMC备份。这里使用 <strong>Qualcomm Premium Tool</strong> 中备份Block0的功能。</p>
<h3 id="fastboot刷机">fastboot刷机<a class="headerlink" href="#fastboot刷机" title="Permanent link">¶</a></h3>
<p>下面尝试进入fastboot刷机。哎?怎么刷不进去?</p>
<div class="highlight"><pre><span></span><code>% fastboot flash partition gpt_both0.bin
Sending 'partition' (33 KB) OKAY [ 0.005s]
Writing 'partition' FAILED (remote: 'failed to write partition,please contact vendor')
fastboot: error: Command failed
</code></pre></div>
<p>看样子fastboot做了限制加了锁。经过一番研究,发现在MSM8916平台上fastboot在 <code>aboot</code>(appsboot/lk1st) 分
区,而刷GNU/Linux正好也要更新这个分区,干脆直接用 <strong>Qualcomm Premium Tool</strong> 把
该写的 <code>aboot</code> 分区通过9008提前写入。看上去不错,fastboot被替换掉了。</p>
<p>然后看情况稍微修改一下 Debian 的刷机脚本,不过好像不改也行。总之就是自己准备好备份,然后相应刷就行。</p>
<p>接下来的步骤和前一篇文章基本一致。</p>
<h2 id="补充">补充<a class="headerlink" href="#补充" title="Permanent link">¶</a></h2>
<h3 id="led怎么不亮">LED怎么不亮<a class="headerlink" href="#led怎么不亮" title="Permanent link">¶</a></h3>
<p><del>emm这块板子的LED是用 <code>aw2013</code> 控制的,是 <code>i2c6</code> 下面挂的一颗LED控制器。需要相应地修改设备树。</del>
我完完全全地被骗了,这就是三个普通的GPIO LED,只不过其中有一个直接连到了PMIC
上。另外,这板子上的红灯居然是常亮的,只和电源是否接通有关系(看板子估计是因为用一个电阻替换了应有的三脚管);蓝色LED结果是个绿
的,我猜都是为了省经费。</p>
<h3 id="apt-key-的代替方案">apt-key 的代替方案<a class="headerlink" href="#apt-key-的代替方案" title="Permanent link">¶</a></h3>
<p>read https://www.linuxuprising.com/2021/01/apt-key-is-deprecated-how-to-add.html</p>
<h3 id="修改appsboot">修改appsboot<a class="headerlink" href="#修改appsboot" title="Permanent link">¶</a></h3>
<p>Clone lk2nd,修改相关配置并编译。原来的aboot是给 <code>UFI001C_MB_V01</code> 用的,这里hack一下给 <code>UFI_16_V3</code> 用。其实也就
修改了一下相关LED的引脚定义,顺带改一下product名称,没别的了。</p>
<h2 id="后记">后记<a class="headerlink" href="#后记" title="Permanent link">¶</a></h2>
<p>经过这次曲折的研究,我意外学习了一些设备树和驱动的概念。感觉设备树这个设计很神
奇啊,本来分散的硬件通过设备树的描述就可以当成一个整体了;驱动集成到内核也把硬
件细节封装了起来;原本的芯片像是通过外围设备组成了一个更大的芯片。以后看情况再
补充学习一下。</p>WeAct BlackPill 跑 CircuitPython 使用自选外部 Flash2022-08-05T00:00:00+08:002022-08-05T00:00:00+08:00tag:techie-s.work,2022-08-05:/posts/2022/08/weact-blackpill-run-circuitpython-with-custom-external-flash/<h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>就是想用便宜的Flash。另外之前在CircuitPython 6.x时代也修改过了,但是之前买的拆机
Flash 质量实在太差,当U盘很不稳,不得已从库存的新Flash中挑了一片 <code>MX25L6406EM2I</code>。
<del>才发现某创商城搞活动时卖的 Flash 是停产/EOL产品。</del></p>
<h2 id="修改">修改<a class="headerlink" href="#修改" title="Permanent link">¶</a></h2>
<p>这个很容易,关键是要知道修改哪里。在CPY 6.x时代,所有的Flash配置都在一个个
结构体中(存疑,可能记错了),驱动大部分是可以通用的。而在CPY 7.x时代,所有的Flash
都有一个自己的 <code>toml</code> 配置文件,定义了其中的各个参数。</p>
<p>准备一份源码。</p>
<div class="highlight"><pre><span></span><code>git<span class="w"> </span>clone<span class="w"> </span>https://github.com/adafruit/circuitpython.git
<span class="nb">cd</span><span class="w"> </span>circuitpython
make<span class="w"> </span>fetch-submodule
</code></pre></div>
<p>CPY 7.x的Flash配置文件在 <code>data/nvm.toml</code> 子模块下。以我的Flash为例,依葫芦
(<code>MX25L1606</code>,<code>MX25L3206E</code>)画瓢,新建
<code>data/nvm.toml/flash/macronix/MX25L6406E.toml</code>。另外我严重怀疑 <code>MX25L1606</code> 的最
大时钟频率配置错了,少了个0。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Settings for the Macronix MX25L6406E 8MiB SPI flash.</span>
<span class="c1"># Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/8630/MX25L6406E,%203V,%2064Mb,%20v1.9.pdf</span>
<span class="n">total_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="n">x800000</span><span class="w"> </span><span class="c1"># 8 MiB</span>
<span class="n">start_up_time_us</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5000</span>
<span class="n">manufacturer_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="n">xc2</span>
<span class="n">memory_type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="n">x20</span>
<span class="n">capacity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0 …</span></code></pre></div><h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>就是想用便宜的Flash。另外之前在CircuitPython 6.x时代也修改过了,但是之前买的拆机
Flash 质量实在太差,当U盘很不稳,不得已从库存的新Flash中挑了一片 <code>MX25L6406EM2I</code>。
<del>才发现某创商城搞活动时卖的 Flash 是停产/EOL产品。</del></p>
<h2 id="修改">修改<a class="headerlink" href="#修改" title="Permanent link">¶</a></h2>
<p>这个很容易,关键是要知道修改哪里。在CPY 6.x时代,所有的Flash配置都在一个个
结构体中(存疑,可能记错了),驱动大部分是可以通用的。而在CPY 7.x时代,所有的Flash
都有一个自己的 <code>toml</code> 配置文件,定义了其中的各个参数。</p>
<p>准备一份源码。</p>
<div class="highlight"><pre><span></span><code>git<span class="w"> </span>clone<span class="w"> </span>https://github.com/adafruit/circuitpython.git
<span class="nb">cd</span><span class="w"> </span>circuitpython
make<span class="w"> </span>fetch-submodule
</code></pre></div>
<p>CPY 7.x的Flash配置文件在 <code>data/nvm.toml</code> 子模块下。以我的Flash为例,依葫芦
(<code>MX25L1606</code>,<code>MX25L3206E</code>)画瓢,新建
<code>data/nvm.toml/flash/macronix/MX25L6406E.toml</code>。另外我严重怀疑 <code>MX25L1606</code> 的最
大时钟频率配置错了,少了个0。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Settings for the Macronix MX25L6406E 8MiB SPI flash.</span>
<span class="c1"># Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/8630/MX25L6406E,%203V,%2064Mb,%20v1.9.pdf</span>
<span class="n">total_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="n">x800000</span><span class="w"> </span><span class="c1"># 8 MiB</span>
<span class="n">start_up_time_us</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5000</span>
<span class="n">manufacturer_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="n">xc2</span>
<span class="n">memory_type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="n">x20</span>
<span class="n">capacity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="n">x17</span>
<span class="n">max_clock_speed_mhz</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">80</span>
<span class="n">quad_enable_bit_mask</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="n">x40</span>
<span class="n">has_sector_protection</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span>
</code></pre></div>
<p>然后修改对应板子的配置文件。</p>
<div class="highlight"><pre><span></span><code><span class="gh">diff --git a/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk b/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk</span>
<span class="gh">index 9da716374..bf048775e 100644</span>
<span class="gd">--- a/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk</span>
<span class="gi">+++ b/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk</span>
<span class="gu">@@ -5,7 +5,7 @@ USB_MANUFACTURER = "WeAct"</span>
<span class="w"> </span>SPI_FLASH_FILESYSTEM = 1
<span class="w"> </span>#See supervisor/shared/external_flash/devices.h for options
<span class="gd">-EXTERNAL_FLASH_DEVICES = GD25Q16C,W25Q64FV,W25Q32JVxQ,W25Q64JVxQ</span>
<span class="gi">+EXTERNAL_FLASH_DEVICES = GD25Q16C,W25Q64FV,W25Q32JVxQ,W25Q64JVxQ,MX25L6406E</span>
<span class="w"> </span>LONGINT_IMPL = MPZ
<span class="w"> </span>INTERNAL_FLASH_FILESYSTEM = 0
</code></pre></div>
<p>再之后编译。<del>不得不提这编译的速度貌似比NRF52快多了。</del></p>
<div class="highlight"><pre><span></span><code>make<span class="w"> </span>-C<span class="w"> </span>mpy-cross
<span class="nb">cd</span><span class="w"> </span>ports/stm
make<span class="w"> </span><span class="nv">BOARD</span><span class="o">=</span>stm32f411ce_blackpill_with_flash<span class="w"> </span>-j<span class="k">$(</span>nproc<span class="k">)</span>
</code></pre></div>
<p>最后刷固件。</p>
<div class="highlight"><pre><span></span><code>dfu-util<span class="w"> </span>-l<span class="w"> </span><span class="c1"># 列出当前的 DFU 设备</span>
dfu-util<span class="w"> </span>-a<span class="w"> </span><span class="m">0</span><span class="w"> </span>-D<span class="w"> </span>build_stm32f411ce_blackpill_with_flash/firmware.bin<span class="w"> </span>-s<span class="w"> </span>0x08000000:leave
</code></pre></div>
<p>Very easy,不是么~</p>
<h2 id="结语">结语<a class="headerlink" href="#结语" title="Permanent link">¶</a></h2>
<p>好像也没什么好说的。啊对,珍爱开发板,远离拆机Flash。</p>在高通骁龙410主控的USB网卡上玩 GNU/Linux2022-07-29T00:00:00+08:002022-09-13T00:00:00+08:00tag:techie-s.work,2022-07-29:/posts/2022/07/openstick-msm8916/
<h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>有段时间网上基于物联网卡的USB式移动数据终端(4G网卡)变多了,搞开车活动,设备很
便宜,价格10块上下,而且默认可以用 ADB 和 Fastboot。作为骁龙410,内核主线支持也
不错。是一个可玩性很高的小设备。我买了某品牌的设备,板号是 <code>UFI001C_MB_V01</code>,目前已经
有网友搞定了,可以跑主线内核的 Debian Bullseye。</p>
<p>后面又入了一个 <code>UFI003</code>,结构基本一致。</p>
<p><s> <em>这东西好像可以拿来做下载机、签到、旁路由、渗透工具</em> </s></p>
<h2 id="目前可以公开的信息">目前可以公开的信息<a class="headerlink" href="#目前可以公开的信息" title="Permanent link">¶</a></h2>
<ul>
<li>设备信息(<code>UFI001C_MB_V01</code>)<ul>
<li>CPU:MSM8916,Snapdragon 410,A53@4C,64bit</li>
<li>RAM/Storage:512M/4G,实际可用约382M/3.3G(因为Modem占用了一些内存)</li>
<li>无线:802.11b/g/n,4G LTE,Bluetooth 4.1,其它SoC支持的也不一定能
用,因为没有接外设出来</li>
<li>有线:USB(可模拟网卡设备,同时用于供电)</li>
<li>其它说明<ul>
<li>板上有一颗 eSIM,应该是互联网卡注册用的。</li>
<li>有一个 SIM 卡槽位置,但是没有焊接卡 …</li></ul></li></ul></li></ul>
<h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>有段时间网上基于物联网卡的USB式移动数据终端(4G网卡)变多了,搞开车活动,设备很
便宜,价格10块上下,而且默认可以用 ADB 和 Fastboot。作为骁龙410,内核主线支持也
不错。是一个可玩性很高的小设备。我买了某品牌的设备,板号是 <code>UFI001C_MB_V01</code>,目前已经
有网友搞定了,可以跑主线内核的 Debian Bullseye。</p>
<p>后面又入了一个 <code>UFI003</code>,结构基本一致。</p>
<p><s> <em>这东西好像可以拿来做下载机、签到、旁路由、渗透工具</em> </s></p>
<h2 id="目前可以公开的信息">目前可以公开的信息<a class="headerlink" href="#目前可以公开的信息" title="Permanent link">¶</a></h2>
<ul>
<li>设备信息(<code>UFI001C_MB_V01</code>)<ul>
<li>CPU:MSM8916,Snapdragon 410,A53@4C,64bit</li>
<li>RAM/Storage:512M/4G,实际可用约382M/3.3G(因为Modem占用了一些内存)</li>
<li>无线:802.11b/g/n,4G LTE,Bluetooth 4.1,其它SoC支持的也不一定能
用,因为没有接外设出来</li>
<li>有线:USB(可模拟网卡设备,同时用于供电)</li>
<li>其它说明<ul>
<li>板上有一颗 eSIM,应该是互联网卡注册用的。</li>
<li>有一个 SIM 卡槽位置,但是没有焊接卡座</li>
<li>有一颗按钮,按住它上电会进入 EDL/9008 模式。</li>
</ul>
</li>
</ul>
</li>
<li>操作系统<ul>
<li>原版系统:安卓(没看版本,盲猜魔改 AOSP</li>
<li>修改系统:Debian(融合部分Mobian)、OpenWrt,以及其它发行版</li>
</ul>
</li>
<li>刷机方式<ul>
<li>原版系统开放 ADB、Fastboot</li>
<li>9008工具亦可</li>
</ul>
</li>
</ul>
<h2 id="刷机流程">刷机流程<a class="headerlink" href="#刷机流程" title="Permanent link">¶</a></h2>
<h3 id="备份">备份<a class="headerlink" href="#备份" title="Permanent link">¶</a></h3>
<div class="admonition danger">
<p class="admonition-title">刷机警告</p>
<p>道路千万条, <strong>备份</strong> 第一条。<br/>
刷机太心急,锁网废机器。 </p>
</div>
<p><strong>先使用9008工具做全盘备份,ROOT之后用QPST备份基带(QCN)</strong>,然后再刷系统。</p>
<h4 id="9008-全盘备份">9008 全盘备份<a class="headerlink" href="#9008-全盘备份" title="Permanent link">¶</a></h4>
<p>9008刷机用 Windows 方便一些。为了能使用高通测试签名的驱动,需要使用命令行开启测试模式。</p>
<div class="highlight"><pre><span></span><code><span class="c"># 设置后需重启以应用</span>
<span class="n">bcdedit</span><span class="p">.</span><span class="n">exe</span> <span class="n">-set</span> <span class="n">testsigning</span> <span class="n">on</span>
<span class="c"># 刷完后用下面这条改回去</span>
<span class="n">bcdedit</span><span class="p">.</span><span class="n">exe</span> <span class="n">-set</span> <span class="n">testsigning</span> <span class="n">off</span>
</code></pre></div>
<p>使用 <strong>Qualcomm Premium Tool</strong> 做完整的 eMMC 备份。需要使用 Windows。
做全盘备份请按图上顺序点击,在出现分区选择时选择最后一个分区(这样备份从0到最后
一个分区),后续按提示选择合适的位置存放全盘镜像。只需要保留这一份全盘备份就
行,其它分区、分区表等信息可以从中提取。</p>
<p><img alt="Qualcomm Premium Tool 操作提示,先进入qualcomm分页下的partitions分页,再按Read(9008)" src="https://techie-s.work/posts/2022/07/openstick-msm8916/qcom-tool.PNG"/></p>
<h4 id="基带备份">基带备份<a class="headerlink" href="#基带备份" title="Permanent link">¶</a></h4>
<p>基带/QCN 备份,需要在安卓系统下开启高通的调试接口然后用 QPST 提取。基带芯片是一
个协处理器,其固件数据存储在一片 NVRAM 中,所以不能直接用 9008 模式读取。</p>
<p>Root有很多方法,在此不详叙,仅提一些操作要点。</p>
<ul>
<li>如果要用桌面投屏控制这个棒子,请先在投屏成功后再安装桌面应用并将其设置为默认
桌面,否则会一直黑;</li>
<li>好像没了</li>
</ul>
<p>开启测试端口的方法如下:</p>
<div class="highlight"><pre><span></span><code>adb<span class="w"> </span>shell
<span class="c1"># 接下来是在 adb shell 中操作</span>
su
<span class="c1"># 注意留住下面的 adb,不然adb就暂时用不了了</span>
setprop<span class="w"> </span>sys.usb.config<span class="w"> </span>diag,adb
</code></pre></div>
<p>接下来就可以使用 QPST 的 Software Download 程序,在 Backup 分页中操作并读取当前
设备的基带固件了(记得选好自己的设备)。</p>
<p>另外串号什么的最好也备份一下。我这次有点浮躁忍不了 Windows(甩锅)。</p>
<p><del>驱动的事Windows好像并不是全都比GNU/Linux那边方便,感觉 Linux 那边通常更方便。</del></p>
<h3 id="debian-安装与使用">Debian 安装与使用<a class="headerlink" href="#debian-安装与使用" title="Permanent link">¶</a></h3>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>OpenStick 项目及其主页提供的部分内容有误,且 RootFS 配置上有 Bug,导致会出
现 USB 网络和 WiFi 热点不能同时开启的现象。如果有能力,建议自行重建 RootFS。</p>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>刷 Debian 可以参考 OpenStick 的文档 <sup id="fnref:openstick-flash-debian"><a class="footnote-ref" href="#fn:openstick-flash-debian">3</a></sup>。此处略过。<br/>
不过 Modem 固件(运行时加载的固件)文档并没有写清,在一个 64MB 大小的分区里,所
有的固件都在一个 images 文件夹里。部分软件提取出来的分区镜像名是 <code>NON-HLOS.bin</code>。</p>
</div>
<p>emm OpenStick小队整的 Debian RootFS 好像缺了不少常用的包,而且配置好像有点点小问题。
记录一下操作。</p>
<ul>
<li>更新时间 <code>timedatectl set-ntp true</code></li>
<li>安装编辑器:vim</li>
<li>安装 curl/wget/ca-certificates 保证 https 连接</li>
<li>换源到 USTC (网易163源貌似缺包,<code>ftp.cn.debian.org</code> 又是直接重定向到 USTC 会有证书错误)</li>
<li>安装 Bluez 并加载相关模块而启用蓝牙</li>
<li>安装 Zsh/OhMyZsh 方便操作</li>
<li>安装 iproute2/iw 等网络工具</li>
<li>更新到 Debian Bookworm</li>
<li><del>安装 fake-hwclock 保证时间一致性</del> 不用了,可以用 systemd-timesyncd</li>
<li>写了个脚本做网络连接 failsafe,后面有写</li>
<li>改了设备树,让按钮可以用</li>
</ul>
<p>iperf3 测了一下速度,2.4G WiFi 速度有 33Mbps,USB 则有 112 Mbps。Modem 暂时没条
件测试。</p>
<h3 id="编译与修改内核">编译与修改内核<a class="headerlink" href="#编译与修改内核" title="Permanent link">¶</a></h3>
<p>本节内容有参考 OpenStick 文档<sup id="fnref:openstick-compile-kernel"><a class="footnote-ref" href="#fn:openstick-compile-kernel">4</a></sup>,并加入了自己的更新。</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>以下内容仅供参考,不保证准确可用,请灵活应变!</p>
</div>
<p>如果你用的是非 Debian 系的 GNU/Linux,建议创建一个 Chroot。BTW 我电脑用的是
Arch Linux,后面自行构建了一个 Debian bookworm RootFS,在 Chroot 内操作。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Chroot on arch - optional approach</span>
mkdir<span class="w"> </span>-p<span class="w"> </span>/home/user/CHROOT/bookworm-cache
<span class="nb">cd</span><span class="w"> </span>/home/user/CHROOT/
sudo<span class="w"> </span>debootstrap<span class="w"> </span>--arch<span class="o">=</span>amd64<span class="w"> </span>--cache-dir<span class="w"> </span>/home/user/CHROOT/bookworm-cache<span class="w"> </span>--foreign<span class="w"> </span>bookworm<span class="w"> </span>bookworm<span class="w"> </span>http://mirrors.ustc.edu.cn/debian/
sudo<span class="w"> </span>arch-chroot<span class="w"> </span>bookworm
/debootstrap/debootstrap<span class="w"> </span>--second-stage
</code></pre></div>
<p>然后准备环境、制作 <code>boot.img</code>。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Debian bookworm environment</span>
apt<span class="w"> </span>update<span class="w"> </span><span class="o">&&</span><span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>-y<span class="w"> </span>git<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>build-essential<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>gcc-aarch64-linux-gnu<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>bc<span class="w"> </span>bison<span class="w"> </span>cpio<span class="w"> </span>flex<span class="w"> </span>rsync<span class="w"> </span>kmod<span class="w"> </span>libssl-dev
<span class="c1"># build kernel</span>
git<span class="w"> </span>clone<span class="w"> </span>--depth<span class="w"> </span><span class="m">1</span><span class="w"> </span>git@github.com:OpenStick/linux.git<span class="w"> </span>linux-openstick
<span class="nb">cd</span><span class="w"> </span>linux-openstick
<span class="nb">export</span><span class="w"> </span><span class="nv">ARCH</span><span class="o">=</span>arm64
<span class="nb">export</span><span class="w"> </span><span class="nv">CROSS_COMPILE</span><span class="o">=</span>aarch64-linux-gnu-
make<span class="w"> </span>msm8916_defconfig
make<span class="w"> </span>deb-pkg<span class="w"> </span><span class="c1"># packages are generated in the parent directory</span>
<span class="c1">## prepare data for boot.img</span>
mkdir<span class="w"> </span>../android-boot
cp<span class="w"> </span>arch/arm64/boot/dts/qcom/msm8916-handsome-openstick-ufi001c.dtb<span class="w"> </span>../android-boot
cp<span class="w"> </span>arch/arm64/boot/Image.gz<span class="w"> </span>../android-boot
<span class="c1"># install kernel packages</span>
scp<span class="w"> </span>../linux-headers-5.15.0-handsomekernel_5.15.0-handsomekernel-1_arm64.deb<span class="w"> </span>user@openstick.local:
scp<span class="w"> </span>../linux-image-5.15.0-handsomekernel_5.15.0-handsomekernel-1_arm64.deb<span class="w"> </span>user@openstick.local:
ssh<span class="w"> </span>user@openstick.local<span class="w"> </span>sudo<span class="w"> </span>apt<span class="w"> </span>remove<span class="w"> </span><span class="s2">"linux-image-5.15.0-handsomekernel+"</span>
ssh<span class="w"> </span>user@openstick.local<span class="w"> </span>sudo<span class="w"> </span>apt<span class="w"> </span>remove<span class="w"> </span><span class="s2">"linux-headers-5.15.0-handsomekernel+"</span>
ssh<span class="w"> </span>user@openstick.local<span class="w"> </span>sudo<span class="w"> </span>dpkg<span class="w"> </span>-i<span class="w"> </span>linux-image-5.15.0-handsomekernel_5.15.0-handsomekernel-1_arm64.deb<span class="w"> </span>linux-headers-5.15.0-handsomekernel_5.15.0-handsomekernel-1_arm64.deb
<span class="c1"># get initrd.img</span>
scp<span class="w"> </span>user@openstick.local:/boot/initrd.img-5.15.0-handsomekernel<span class="w"> </span>../android-boot
<span class="c1"># generate boot.img</span>
<span class="nb">cd</span><span class="w"> </span>../android-boot
cat<span class="w"> </span>Image.gz<span class="w"> </span>msm8916-handsome-openstick-ufi001c.dtb<span class="w"> </span>><span class="w"> </span>kernel-dtb
mkbootimg<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--base<span class="w"> </span>0x80000000<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--kernel_offset<span class="w"> </span>0x00080000<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--ramdisk_offset<span class="w"> </span>0x02000000<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--tags_offset<span class="w"> </span>0x01e00000<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--pagesize<span class="w"> </span><span class="m">2048</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--second_offset<span class="w"> </span>0x00f00000<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--ramdisk<span class="w"> </span>initrd.img-5.15.0-handsomekernel<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--cmdline<span class="w"> </span><span class="s2">"earlycon root=PARTUUID=a7ab80e8-e9d1-e8cd-f157-93f69b1d141e console=ttyMSM0,115200 no_framebuffer=true rw"</span><span class="se">\</span>
<span class="w"> </span>--kernel<span class="w"> </span>kernel-dtb<span class="w"> </span>-o<span class="w"> </span>boot.img
<span class="c1"># flash boot.img</span>
adb<span class="w"> </span>reboot<span class="w"> </span>bootloader
fastboot<span class="w"> </span>flash<span class="w"> </span>boot<span class="w"> </span>boot.img
</code></pre></div>
<p>截止文章发布,该内核设备树的按钮电平配置还是反的,我已经改了,不多说了。</p>
<p>按钮修好后,可以配置 systemd-logind 设置 <code>HandleRebootKey</code> 和
<code>HandleRebootKeyLongPress</code> 为 <code>ignore</code>,然后用别的程序监控这个按钮发出别的操
作。不过也可以用 <code>systemd-inhibit</code> 去 <code>handle-reboot-key</code>。</p>
<h3 id="其它说明">其它说明<a class="headerlink" href="#其它说明" title="Permanent link">¶</a></h3>
<ul>
<li>这个设备 sim 卡热插拔不太稳的样子。所以尽量别热插拔了可能 Modem 会出现运行错
误;</li>
<li>内核默认配置是激活外部 eSIM,所以自己插卡后需要手动切卡(通过SysFs写点东
西),然后可能需要重启 ModemManager,之后大概就能获取到网络信息了。至少我获取
到了。</li>
<li><s>我也不知道全网通基带考不靠谱,虽然我刷了,但是没测试(这个测试起来太麻烦
了)</s>,后面感觉并不靠谱,没必要;</li>
<li><del>想用按钮,或许要自己改 device tree</del>;</li>
<li>这东西,Modem 发热严重,可以试试改用低功耗模式;另,使用不匹配的固件同样会造
成 Modem 发热严重。</li>
</ul>
<h2 id="有趣的应用">有趣的应用<a class="headerlink" href="#有趣的应用" title="Permanent link">¶</a></h2>
<h3 id="先让自己的设备断网也不怕">先让自己的设备断网也不怕<a class="headerlink" href="#先让自己的设备断网也不怕" title="Permanent link">¶</a></h3>
<p>虽说可以用串口终端,不过也不是什么时候都方便。所以我写了一些脚本<sup id="fnref:openstick-failsafe-guard"><a class="footnote-ref" href="#fn:openstick-failsafe-guard">5</a></sup>帮我根据情况恢复网络。</p>
<p>其中一个服务会在网络断开时尝试启用所有的候选网络,开启恢复用的接入点;另一个则
是监控仅有的 GPIO 按钮,并根据用户配置执行对应操作。</p>
<p>感谢来自下面这些链接中内容的作者们,他们给了我很多启发:</p>
<ul>
<li>https://github.com/beagleboard/linux/issues/20</li>
<li>https://unix.stackexchange.com/questions/25372/turn-off-buffering-in-pipe</li>
<li>https://unix.stackexchange.com/questions/428399/how-can-i-run-a-shell-script-on-input-device-event</li>
</ul>
<h3 id="添加-sim-卡槽">添加 SIM 卡槽<a class="headerlink" href="#添加-sim-卡槽" title="Permanent link">¶</a></h3>
<p>网上很多把原来的 eSIM 干掉的做法,emm 实际上没必要。焊接卡槽可以用精密的尖头烙
铁。我用 936 就把 SIM 卡槽焊接好了。如图所示。
<s>跳线是测试连通性时发现 GND 居然不通,所以额外跳了一根线到最近的测试点上(CPU
的金属罩子它不沾锡啊TvT)。</s>
实际上不用跳线,因为这个卡槽的GND就是板子设计的使能控制脚,接地可能就只能用卡槽
了,会影响esim切卡。后来我把这根线剪掉了。</p>
<p><img alt="添加 SIM 卡槽从而能插自己的卡" src="https://techie-s.work/posts/2022/07/openstick-msm8916/dongle-add-sim-holder.jpg"/></p>
<h3 id="切换-sim-卡">切换 SIM 卡<a class="headerlink" href="#切换-sim-卡" title="Permanent link">¶</a></h3>
<p>该设备的 SIM 卡使用 GPIO 作为切换开关,但切换后需要重启 modem 以应用配置。下面
列出操作示例。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 先设置激活你卡对应的led/gpio开关,比如说</span>
<span class="nb">echo</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>><span class="w"> </span>/sys/class/leds/esim0_select/brightness<span class="w"> </span><span class="c1"># 禁用 esim0</span>
<span class="nb">echo</span><span class="w"> </span><span class="m">1</span><span class="w"> </span>><span class="w"> </span>/sys/class/leds/sim0_select/brightness<span class="w"> </span><span class="c1"># 启用 sim0</span>
<span class="c1"># 重新加载内核模块并重启相关服务</span>
modprobe<span class="w"> </span>-r<span class="w"> </span>qcom-q6v5-mss
modprobe<span class="w"> </span>qcom-q6v5-mss
systemctl<span class="w"> </span>restart<span class="w"> </span>rmtfs
systemctl<span class="w"> </span>restart<span class="w"> </span>ModemManager
<span class="c1"># 注意,有些设备的modem在使用跨运营商卡的时候会主动崩溃一次</span>
<span class="c1"># 此时视情况需要重启ModemManager</span>
</code></pre></div>
<h3 id="迷你短信平台">迷你短信平台<a class="headerlink" href="#迷你短信平台" title="Permanent link">¶</a></h3>
<p>咕咕咕</p>
<h3 id="home-assistant">Home Assistant<a class="headerlink" href="#home-assistant" title="Permanent link">¶</a></h3>
<p>咕咕咕</p>
<p>测试了,太吃内存,就算禁用modem空内存出来还是不够用,还会令WiFi性能急剧下降。</p>
<h2 id="结语">结语<a class="headerlink" href="#结语" title="Permanent link">¶</a></h2>
<p>emm懒了,这里的内容晚点看情况随缘更新(咕咕咕)</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:open-stick-doc">
<p><a href="https://www.kancloud.cn/handsomehacker/openstick/2636505">关于 OpenStick - 概述</a> <a class="footnote-backref" href="#fnref:open-stick-doc" title="回到 #1">↩</a></p>
</li>
<li id="fn:universal-adb-drivers">
<p><a href="https://adb.clockworkmod.com/">Universal ADB Drivers - ClockWorkMod</a> <a class="footnote-backref" href="#fnref:universal-adb-drivers" title="回到 #2">↩</a></p>
</li>
<li id="fn:openstick-flash-debian">
<p><a href="https://www.kancloud.cn/handsomehacker/openstick/2636506">OpenStick - 开始折腾</a> <a class="footnote-backref" href="#fnref:openstick-flash-debian" title="回到 #3">↩</a></p>
</li>
<li id="fn:openstick-compile-kernel">
<p><a href="https://www.kancloud.cn/handsomehacker/openstick/2637565">OpenStick – 编译内核</a> <a class="footnote-backref" href="#fnref:openstick-compile-kernel" title="回到 #4">↩</a></p>
</li>
<li id="fn:openstick-failsafe-guard">
<p><a href="https://github.com/hyx0329/openstick-failsafe-guard">github/hyx0329 - Openstick Failsafe Guard</a> <a class="footnote-backref" href="#fnref:openstick-failsafe-guard" title="回到 #5">↩</a></p>
</li>
</ol>
</div>M60 Keyboard - 运行 Python 的机械键盘2022-07-03T00:00:00+08:002023-06-09T00:00:00+08:00tag:techie-s.work,2022-07-03:/posts/2022/07/circuitpython-notes-000/
<h2 id="背景">背景<a class="headerlink" href="#背景" title="Permanent link">¶</a></h2>
<p>折腾这个主要是因为我自己有用一个固件是 CircuitPython<sup id="fnref:circuitpython"><a class="footnote-ref" href="#fn:circuitpython">1</a></sup> <sup id="fnref:circuitpython-git"><a class="footnote-ref" href="#fn:circuitpython-git">2</a></sup> 的机械键盘<sup id="fnref:pykb-makerdiary"><a class="footnote-ref" href="#fn:pykb-makerdiary">3</a></sup>。
然后官方好像咕咕咕了很久没更新,我就忍不了了(也正好有空),直接自己上,把固件
大版本提了上来(6.0.0 -> 7.3.1),以便使用更多特性。</p>
<h2 id="定制-circuitpython">定制 CircuitPython<a class="headerlink" href="#定制-circuitpython" title="Permanent link">¶</a></h2>
<p>为什么不用官方发行并支持的二进制版本呢?emm主要是官方固件不接USB就会停机,也就不能无线使用了,只能
定制一下固件。</p>
<p>在 GitHub 上对比了 Makerdiary 和 Adafruit 官方版两个分支的不同
<sup id="fnref:compare-makerdiary-adafruit"><a class="footnote-ref" href="#fn:compare-makerdiary-adafruit">5</a></sup>,从中剥离了 <code>matrix</code> 模块和针对键盘的定制,调整
成了一组 Patch,调整后的补丁区分度更高,也方便后面继续升级版本。下面简单说一下
遇到的困难。</p>
<h3 id="未声明的函数">未声明的函数<a class="headerlink" href="#未声明的函数" title="Permanent link">¶</a></h3>
<p>对于 CircuitPython 7.x 的编译系统,所有用到的函数都需要声明。几乎没写过靠谱的C的我,在面对
这个问题的时候竟 …</p>
<h2 id="背景">背景<a class="headerlink" href="#背景" title="Permanent link">¶</a></h2>
<p>折腾这个主要是因为我自己有用一个固件是 CircuitPython<sup id="fnref:circuitpython"><a class="footnote-ref" href="#fn:circuitpython">1</a></sup> <sup id="fnref:circuitpython-git"><a class="footnote-ref" href="#fn:circuitpython-git">2</a></sup> 的机械键盘<sup id="fnref:pykb-makerdiary"><a class="footnote-ref" href="#fn:pykb-makerdiary">3</a></sup>。
然后官方好像咕咕咕了很久没更新,我就忍不了了(也正好有空),直接自己上,把固件
大版本提了上来(6.0.0 -> 7.3.1),以便使用更多特性。</p>
<h2 id="定制-circuitpython">定制 CircuitPython<a class="headerlink" href="#定制-circuitpython" title="Permanent link">¶</a></h2>
<p>为什么不用官方发行并支持的二进制版本呢?emm主要是官方固件不接USB就会停机,也就不能无线使用了,只能
定制一下固件。</p>
<p>在 GitHub 上对比了 Makerdiary 和 Adafruit 官方版两个分支的不同
<sup id="fnref:compare-makerdiary-adafruit"><a class="footnote-ref" href="#fn:compare-makerdiary-adafruit">5</a></sup>,从中剥离了 <code>matrix</code> 模块和针对键盘的定制,调整
成了一组 Patch,调整后的补丁区分度更高,也方便后面继续升级版本。下面简单说一下
遇到的困难。</p>
<h3 id="未声明的函数">未声明的函数<a class="headerlink" href="#未声明的函数" title="Permanent link">¶</a></h3>
<p>对于 CircuitPython 7.x 的编译系统,所有用到的函数都需要声明。几乎没写过靠谱的C的我,在面对
这个问题的时候竟然非常小心,总认为光声明没有用。</p>
<p>然而事实是,补全声明就行了。</p>
<h3 id="不匹配的类型">不匹配的类型<a class="headerlink" href="#不匹配的类型" title="Permanent link">¶</a></h3>
<p>CircuitPython 7.x 开始,跟上了 MicroPython 内置 C 模块的接口设计,不再用旧的那
套了。为此需要将旧的类型转换成新的、函数原型做对应调整。庆幸的是,C实现的
<code>matrix</code> 模块并不需要传入参数,逻辑不用作过多修改。</p>
<p>不过也可以看出这个 matrix 模块不够灵活,是针对 M60 键盘写死的,想迁移到别的设备还得动点脑筋。</p>
<h3 id="安全模式没了">安全模式没了<a class="headerlink" href="#安全模式没了" title="Permanent link">¶</a></h3>
<p>为什么我无法手动进入安全模式?安全模式下,不会自动重载代码,不会上电自动启动代
码,方便调试板上的 python 程序,同时也可以作为一种更新板上代码的方式。为了避免
键盘的虚拟U盘文件系统损坏,我会在 <code>boot.py</code> 中关闭存储设备接口或将存储设置为只
读。有时代码没写好,忘了加恢复的快捷键,就需要用安全模式避免代码上电执行。但现
在安全模式没了,就又得格式化存储区……</p>
<p>检查了 cherry-pick 的 commit,发现问题可能出在绕过 <code>soft device</code> 崩溃时的安全模
式重启上。reset 键盘时, <code>soft device</code> 可能会挂掉,而现在的状况是,手动进入安全
模式, <code>soft device</code> 也会挂掉,所以原来的代码顺带把进入安全模式的状态给清了……</p>
<p>解决方法,便是对当前重置条件做额外检查。运气很好,其它模块内有对应实现,搬过来稍微改改就好了。</p>
<h3 id="用-mpy-cross-提前将模块编译成字节码">用 mpy-cross 提前将模块编译成字节码<a class="headerlink" href="#用-mpy-cross-提前将模块编译成字节码" title="Permanent link">¶</a></h3>
<p>需要用与 CircuitPython 版本对应的 mpy-cross<sup id="fnref:mpy-cross-micropython"><a class="footnote-ref" href="#fn:mpy-cross-micropython">6</a></sup>,参数设定好。本来想继续
研究看看,但好像没什么用。</p>
<p>还想研究 C 写的模块来着,发现 CircuitPython 还不行……这边 MicroPython 更先进啊。</p>
<h2 id="修-bug">修 Bug<a class="headerlink" href="#修-bug" title="Permanent link">¶</a></h2>
<h3 id="让官方版本-circuitpython-支持无-usb-连接使用">让官方版本 CircuitPython 支持无 USB 连接使用<a class="headerlink" href="#让官方版本-circuitpython-支持无-usb-连接使用" title="Permanent link">¶</a></h3>
<p>其实很简单,回过头来看,只要把键盘的电源控制IO(P0.28)激活即可。Makerdiary在添加支持时并没有通过board暴露这个引脚,文档里也有点错误。总之搞定了。CPY 8.2.0+应该都能用了。</p>
<p>===</p>
<p>先这样写一点,之后应该不会再补充了。</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:circuitpython">
<p><a href="https://circuitpython.org">CircuitPython 主页</a> <a class="footnote-backref" href="#fnref:circuitpython" title="回到 #1">↩</a></p>
</li>
<li id="fn:circuitpython-git">
<p><a href="https://github.com/adafruit/circuitpython">CircuitPython 仓库</a> <a class="footnote-backref" href="#fnref:circuitpython-git" title="回到 #2">↩</a></p>
</li>
<li id="fn:pykb-makerdiary">
<p><a href="https://github.com/makerdiary/python-keyboard">Makerdiary 的 M60 键盘</a> <a class="footnote-backref" href="#fnref:pykb-makerdiary" title="回到 #3">↩</a></p>
</li>
<li id="fn:pykb-hyx0329">
<p><a href="https://github.com/hyx0329/python-keyboard">M60 键盘 - hyx0329 定制版固件</a> <a class="footnote-backref" href="#fnref:pykb-hyx0329" title="回到 #4">↩</a></p>
</li>
<li id="fn:compare-makerdiary-adafruit">
<p><a href="https://github.com/adafruit/circuitpython/compare/main...xiongyihui:circuitpython:m60">比较 Makerdiary 和 和 Adafruit 官方版两个分支的不同</a> <a class="footnote-backref" href="#fnref:compare-makerdiary-adafruit" title="回到 #5">↩</a></p>
</li>
<li id="fn:mpy-cross-micropython">
<p><a href="https://docs.micropython.org/en/latest/reference/mpyfiles.html">MicroPython 对 mpy、mpy-cross 的介绍</a> <a class="footnote-backref" href="#fnref:mpy-cross-micropython" title="回到 #6">↩</a></p>
</li>
<li id="fn:mpy-c-module">
<p><a href="https://docs.micropython.org/en/latest/develop/natmod.html">MicroPython 用 C 编写 native 模块</a> <a class="footnote-backref" href="#fnref:mpy-c-module" title="回到 #7">↩</a></p>
</li>
</ol>
</div>重新认识 Vim2022-06-04T00:00:00+08:002022-09-11T00:00:00+08:00tag:techie-s.work,2022-06-04:/posts/2022/06/vim-refresh/
<p>自我接触 GNU/Linux 已有 7 年,用了那么多次 Vim,却还停留在这个水平……感觉不太行,而且确实少了
点生产力。</p>
<p><strong>需要重新认识一下 Vim 了!</strong></p>
<p>(本文不定期更新)</p>
<p>一个我认为不错的 Vim primer:<a href="https://danielmiessler.com/study/vim/">Learn Vim For the Last Time: A Tutorial and Primer</a></p>
<h2 id="有关-vim-的一些问题">有关 Vim 的一些问题<a class="headerlink" href="#有关-vim-的一些问题" title="Permanent link">¶</a></h2>
<h3 id="vim-是什么">Vim 是什么?<a class="headerlink" href="#vim-是什么" title="Permanent link">¶</a></h3>
<p>hmm,简单说就是一个 TUI(Text-based User Interface)环境下的文本编辑器。
<s>和 Emacs 是某种意义上的竞争对手。</s></p>
<h3 id="为什么用-vim它有什么优势吗">为什么用 Vim?它有什么优势吗?<a class="headerlink" href="#为什么用-vim它有什么优势吗" title="Permanent link">¶</a></h3>
<p>我选用 Vim 没什么特别的理由。它的界面极其简洁,插件功能非常丰富,适合终端下使用。其它的嘛,其实
也不能就这样和别的编辑器比。</p>
<p>相比于现代编辑器,Vim 更依赖键盘快捷键,学习曲线较陡,而且在 GUI 环境下并不一定比其它编辑器更强。
不过我现在就基本在命令行环境下开发,学 …</p>
<p>自我接触 GNU/Linux 已有 7 年,用了那么多次 Vim,却还停留在这个水平……感觉不太行,而且确实少了
点生产力。</p>
<p><strong>需要重新认识一下 Vim 了!</strong></p>
<p>(本文不定期更新)</p>
<p>一个我认为不错的 Vim primer:<a href="https://danielmiessler.com/study/vim/">Learn Vim For the Last Time: A Tutorial and Primer</a></p>
<h2 id="有关-vim-的一些问题">有关 Vim 的一些问题<a class="headerlink" href="#有关-vim-的一些问题" title="Permanent link">¶</a></h2>
<h3 id="vim-是什么">Vim 是什么?<a class="headerlink" href="#vim-是什么" title="Permanent link">¶</a></h3>
<p>hmm,简单说就是一个 TUI(Text-based User Interface)环境下的文本编辑器。
<s>和 Emacs 是某种意义上的竞争对手。</s></p>
<h3 id="为什么用-vim它有什么优势吗">为什么用 Vim?它有什么优势吗?<a class="headerlink" href="#为什么用-vim它有什么优势吗" title="Permanent link">¶</a></h3>
<p>我选用 Vim 没什么特别的理由。它的界面极其简洁,插件功能非常丰富,适合终端下使用。其它的嘛,其实
也不能就这样和别的编辑器比。</p>
<p>相比于现代编辑器,Vim 更依赖键盘快捷键,学习曲线较陡,而且在 GUI 环境下并不一定比其它编辑器更强。
不过我现在就基本在命令行环境下开发,学点 Vim 貌似更合适,而且命令行下、围绕键盘的操作方式更适合
熟练人员、笔记本电脑用户。</p>
<p>当然这也只是我自己的感受。</p>
<h3 id="学习的思路为何">学习的思路为何?<a class="headerlink" href="#学习的思路为何" title="Permanent link">¶</a></h3>
<p>首先,接受 Vim 的设计特色。Vim、Emacs 等软件并非像常用 GUI 编辑器那样考虑到了很多鼠标操作,而
在认识到他们围绕键盘操作的特色后,也能理解为什么需要优先学习快捷键了。</p>
<p>其次,学习 Vim 中等价于其它编辑器的功能。这部分优先学会,才能让使用者实现用 Vim 代替原有的编辑
器,将整个环境替换过去。</p>
<p>之后就是喜闻乐见的进阶技巧了。宏、模块/插件之类是非常高效的功能。</p>
<h2 id="我的-vim-使用记录">我的 Vim 使用记录<a class="headerlink" href="#我的-vim-使用记录" title="Permanent link">¶</a></h2>
<h3 id="2022-09-11">2022-09-11<a class="headerlink" href="#2022-09-11" title="Permanent link">¶</a></h3>
<p>感觉这样记录不太行……中间都直接不记录了……</p>
<h3 id="2022-07-29">2022-07-29<a class="headerlink" href="#2022-07-29" title="Permanent link">¶</a></h3>
<p>到这个时候,我虽然还不能说熟练使用 Vim,但是一些基本功能也已经掌握了,而且通过插件的帮助改善了编
程体验。前面好多学习经历都没有记录。</p>
<p>目前学会的部分:</p>
<ul>
<li>基本的文件浏览<ul>
<li>上下左右滚屏</li>
<li>跳转</li>
</ul>
</li>
<li>基本的编辑<ul>
<li>verbs,noun,adverbs</li>
<li>重复</li>
<li>查找</li>
<li>替换</li>
<li>快速改写/删除</li>
</ul>
</li>
</ul>MicroPython 上手2022-05-11T00:00:00+08:002022-05-11T00:00:00+08:00tag:techie-s.work,2022-05-11:/posts/2022/05/micropython-001-hands-on/
<p>虽说是上手,但实际我有玩过一阵子了,而且这篇文更接近介绍。本系列博客(?)将尝试给有基础单片机概念的人快速了解 MicroPython。</p>
<h2 id="介绍">介绍<a class="headerlink" href="#介绍" title="Permanent link">¶</a></h2>
<p><a href="https://github.com/micropython/micropython">MicroPython</a> 是一款面向嵌入式设备的 Python 环境,运行于各种单片机上,
Adafruit fork 出的 <a href="https://github.com/adafruit/circuitpython">CircuitPython</a> 是一个对新手更友好的分支,并且是CPython的严格子集,不过也缺少了一部分高级特性,这个参考官方仓库说明。</p>
<p>MicroPython 主要通过串口实现 REPL(Read Eval Print Loop)的交互,也就是 Python Console。
此外在支持的设备上还能使用网络,从网页或者 Telnet 客户端进行操作。</p>
<p>MicroPython 的语法接近 CPython,许多常用标准库能代换,但相比之下精简了很多,并且添加了一部分独有的库,这些独有库通常以字母 <code>u</code> 开头。</p>
<h2 id="使用前言">使用前言<a class="headerlink" href="#使用前言" title="Permanent link">¶</a></h2>
<p>想使用 MicroPython?买一块兼容开发板,刷上固件,然后就能 Happy 啦!
主要 …</p>
<p>虽说是上手,但实际我有玩过一阵子了,而且这篇文更接近介绍。本系列博客(?)将尝试给有基础单片机概念的人快速了解 MicroPython。</p>
<h2 id="介绍">介绍<a class="headerlink" href="#介绍" title="Permanent link">¶</a></h2>
<p><a href="https://github.com/micropython/micropython">MicroPython</a> 是一款面向嵌入式设备的 Python 环境,运行于各种单片机上,
Adafruit fork 出的 <a href="https://github.com/adafruit/circuitpython">CircuitPython</a> 是一个对新手更友好的分支,并且是CPython的严格子集,不过也缺少了一部分高级特性,这个参考官方仓库说明。</p>
<p>MicroPython 主要通过串口实现 REPL(Read Eval Print Loop)的交互,也就是 Python Console。
此外在支持的设备上还能使用网络,从网页或者 Telnet 客户端进行操作。</p>
<p>MicroPython 的语法接近 CPython,许多常用标准库能代换,但相比之下精简了很多,并且添加了一部分独有的库,这些独有库通常以字母 <code>u</code> 开头。</p>
<h2 id="使用前言">使用前言<a class="headerlink" href="#使用前言" title="Permanent link">¶</a></h2>
<p>想使用 MicroPython?买一块兼容开发板,刷上固件,然后就能 Happy 啦!
主要支持的设备是部分基于 STM32、NRF52、ESP32 的开发板。具体支持哪些板子可以看<a href="https://micropython.org/download/">官方下载页面</a>。我个人比较推荐纯新手且仅打算使用 Py 编程的朋友选用基于 ESP32 系列 MCU 的开发板,比如 Node32、Lolin32、LuatOS Core。</p>
<p>我选择的是来自 LuatOS 团队的开发板,它使用 ESP32-C3 这款 RISCV 架构的芯片,支持蓝牙、802.11 b/g/n/e/i,我选购的时候它价格也不错。就是不在官方支持列表上XD。</p>
<p>官方的下载渠道在<a href="https://micropython.org/download/">这里</a>,根据不同的开发板,点进去后会有不同的安装说明。</p>
<h2 id="给-luatos-core-安装-micropython">给 LuatOS Core 安装 MicroPython<a class="headerlink" href="#给-luatos-core-安装-micropython" title="Permanent link">¶</a></h2>
<p>这里就写一下这款开发板的安装流程吧。ESP32 系列的其实都差不多,而且一般不需要调试器。</p>
<p>大概分这么几步:</p>
<ul>
<li>下载工具</li>
<li>下载固件</li>
<li>清空 Flash</li>
<li>写入固件</li>
</ul>
<h3 id="下载工具">下载工具<a class="headerlink" href="#下载工具" title="Permanent link">¶</a></h3>
<p>这里需要安装 <code>esptool.py</code> 或者乐鑫提供的 Windows 版刷写工具。工具之类我就不再介绍如何安装了,希望读者能自行搞定(尽管这个可能是大多数人最开始搞不定的地方)。</p>
<h3 id="下载固件-清空-flash-写入固件">下载固件 & 清空 Flash & 写入固件<a class="headerlink" href="#下载固件-清空-flash-写入固件" title="Permanent link">¶</a></h3>
<p>由于我这块板子没有进入官方支持列表,有两种选择:</p>
<ul>
<li>使用官方通用固件</li>
<li>自行补充开发板定义,并编译</li>
</ul>
<p>我实际上选择了第二种,从结果上看第一种也没差。这里按第一种方法。ESP32-C3 的官方固件下载和说明页面<a href="https://micropython.org/download/esp32c3/">点击这里</a>。</p>
<p>Windows 用户请自行解决工具问题。</p>
<p>对于 Linux 用户,打开命令行终端,使用下面的命令进行刷写。注意,清空 Flash 是为了避免残留数据干扰固件第一次建立文件系统,在第一次刷写时不可避免(当然空 Flash 除外)。</p>
<p>另外,我用的开发板只用了两条数据线读写 Flash,务必选用 DIO 模式。 <code>/dev/ttyACM0</code> 是串口的设备路径,请根据实际情况调整。</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 清空 Flash</span>
esptool.py<span class="w"> </span>--chip<span class="w"> </span>esp32c3<span class="w"> </span>--port<span class="w"> </span>/dev/ttyACM0<span class="w"> </span>erase_flash
<span class="c1"># 刷写固件,强制 DIO 模式,波特率460800,刷写固件,从0x0地址开始写入,固件名称 esp32c3-20220117-v1.18.bin</span>
esptool.py<span class="w"> </span>--chip<span class="w"> </span>esp32c3<span class="w"> </span>--port<span class="w"> </span>/dev/ttyACM0<span class="w"> </span>--flash_mode<span class="w"> </span>dio<span class="w"> </span>--baud<span class="w"> </span><span class="m">460800</span><span class="w"> </span>write_flash<span class="w"> </span>-z<span class="w"> </span>0x0<span class="w"> </span>esp32c3-20220117-v1.18.bin
</code></pre></div>
<p>这样固件就写入完成了。下篇开始正式玩XD。</p>
<h2 id="结语">结语<a class="headerlink" href="#结语" title="Permanent link">¶</a></h2>
<p>这样应该就算上手完了?不是吧 XP。嗯,实际上应该再写一点第一次使用时使用的 Demo,比如 Hello World、Blinky 什么的。
但对于希望小白能全看懂的我来说,或许更适合放在一篇单独的文章中。但实际写出来的好像也不是非常容易看懂啊……</p>MicroPython 交互终端2022-05-11T00:00:00+08:002022-05-11T00:00:00+08:00tag:techie-s.work,2022-05-11:/posts/2022/05/micropython-002-repl/
<h2 id="前言">前言<a class="headerlink" href="#前言" title="Permanent link">¶</a></h2>
<p>很多单片机没有 USB控制器,只有一个 UART/TTL/RS232 串口,所以在开发阶段的大部分交互依赖于单片机串口。
通常情况下,串口用来上传源代码文件或输出调试信息。</p>
<h2 id="串口交互软件">串口交互软件<a class="headerlink" href="#串口交互软件" title="Permanent link">¶</a></h2>
<p>串口需要使用串口软件方可交互。Windows 下有很多为人熟知的工具,不过我个人用得不多。GNU/Linux 用户可以使用 <code>screen</code>
当作临时的终端,但各个方面都差一点意思。这里我习惯了使用 <a href="https://github.com/makerdiary/terminal-s">terminal-s</a>,简单又好用。</p>
<p>不过上传文件的时候就都不怎么方便了。这里可以用 <code>adafruit-ampy</code> 进行文件操作。这个程序我就先不介绍了。</p>
<h2 id="通过串口输入程序">通过串口输入程序<a class="headerlink" href="#通过串口输入程序" title="Permanent link">¶</a></h2>
<p>下面给出了一个交互实例,开关 GPIO12 所连接的 led,最后打印了默认的帮助信息。</p>
<div class="highlight"><pre><span></span><code><span class="o">></span> <span class="n">terminal</span><span class="o">-</span><span class="n">s</span>
<span class="o">---</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyACM0</span> <span class="ow">is</span> <span class="n">connected</span><span class="o">.</span> <span class="n">Press</span> <span class="n">Ctrl</span><span class="o">+</span><span class="p">]</span> <span class="n">to</span> <span class="n">quit</span> <span class="o">---</span>
<span class="n">MPY</span><span class="p">:</span> <span class="n">soft</span> <span class="n">reboot</span>
<span class="n">MicroPython</span> <span class="n">v1</span><span class="mf">.18</span><span class="o">-</span><span class="mi">422</span><span class="o">-</span><span class="n">g7c0402338</span> <span class="n">on</span> <span class="mi">2022</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">11</span><span class="p">;</span> <span class="n">LuatOS</span> <span class="n">C3</span> <span class="n">CORE</span> <span class="k">with …</span></code></pre></div>
<h2 id="前言">前言<a class="headerlink" href="#前言" title="Permanent link">¶</a></h2>
<p>很多单片机没有 USB控制器,只有一个 UART/TTL/RS232 串口,所以在开发阶段的大部分交互依赖于单片机串口。
通常情况下,串口用来上传源代码文件或输出调试信息。</p>
<h2 id="串口交互软件">串口交互软件<a class="headerlink" href="#串口交互软件" title="Permanent link">¶</a></h2>
<p>串口需要使用串口软件方可交互。Windows 下有很多为人熟知的工具,不过我个人用得不多。GNU/Linux 用户可以使用 <code>screen</code>
当作临时的终端,但各个方面都差一点意思。这里我习惯了使用 <a href="https://github.com/makerdiary/terminal-s">terminal-s</a>,简单又好用。</p>
<p>不过上传文件的时候就都不怎么方便了。这里可以用 <code>adafruit-ampy</code> 进行文件操作。这个程序我就先不介绍了。</p>
<h2 id="通过串口输入程序">通过串口输入程序<a class="headerlink" href="#通过串口输入程序" title="Permanent link">¶</a></h2>
<p>下面给出了一个交互实例,开关 GPIO12 所连接的 led,最后打印了默认的帮助信息。</p>
<div class="highlight"><pre><span></span><code><span class="o">></span> <span class="n">terminal</span><span class="o">-</span><span class="n">s</span>
<span class="o">---</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyACM0</span> <span class="ow">is</span> <span class="n">connected</span><span class="o">.</span> <span class="n">Press</span> <span class="n">Ctrl</span><span class="o">+</span><span class="p">]</span> <span class="n">to</span> <span class="n">quit</span> <span class="o">---</span>
<span class="n">MPY</span><span class="p">:</span> <span class="n">soft</span> <span class="n">reboot</span>
<span class="n">MicroPython</span> <span class="n">v1</span><span class="mf">.18</span><span class="o">-</span><span class="mi">422</span><span class="o">-</span><span class="n">g7c0402338</span> <span class="n">on</span> <span class="mi">2022</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">11</span><span class="p">;</span> <span class="n">LuatOS</span> <span class="n">C3</span> <span class="n">CORE</span> <span class="k">with</span> <span class="n">ESP32C3</span>
<span class="n">Type</span> <span class="s2">"help()"</span> <span class="k">for</span> <span class="n">more</span> <span class="n">information</span><span class="o">.</span>
<span class="o">>>></span> <span class="kn">from</span> <span class="nn">machine</span> <span class="kn">import</span> <span class="n">Pin</span>
<span class="o">>>></span> <span class="n">led</span> <span class="o">=</span> <span class="n">Pin</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="n">Pin</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">led</span><span class="o">.</span><span class="n">on</span><span class="p">()</span>
<span class="o">>>></span> <span class="n">led</span><span class="o">.</span><span class="n">off</span><span class="p">()</span>
<span class="o">>>></span> <span class="n">help</span><span class="p">()</span>
<span class="n">Welcome</span> <span class="n">to</span> <span class="n">MicroPython</span> <span class="n">on</span> <span class="n">the</span> <span class="n">ESP32</span><span class="err">!</span>
<span class="n">For</span> <span class="n">generic</span> <span class="n">online</span> <span class="n">docs</span> <span class="n">please</span> <span class="n">visit</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">docs</span><span class="o">.</span><span class="n">micropython</span><span class="o">.</span><span class="n">org</span><span class="o">/</span>
<span class="n">For</span> <span class="n">access</span> <span class="n">to</span> <span class="n">the</span> <span class="n">hardware</span> <span class="n">use</span> <span class="n">the</span> <span class="s1">'machine'</span> <span class="n">module</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">machine</span>
<span class="n">pin12</span> <span class="o">=</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>
<span class="n">pin12</span><span class="o">.</span><span class="n">value</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">pin13</span> <span class="o">=</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="mi">13</span><span class="p">,</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="o">.</span><span class="n">PULL_UP</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">pin13</span><span class="o">.</span><span class="n">value</span><span class="p">())</span>
<span class="n">i2c</span> <span class="o">=</span> <span class="n">machine</span><span class="o">.</span><span class="n">I2C</span><span class="p">(</span><span class="n">scl</span><span class="o">=</span><span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="mi">21</span><span class="p">),</span> <span class="n">sda</span><span class="o">=</span><span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="mi">22</span><span class="p">))</span>
<span class="n">i2c</span><span class="o">.</span><span class="n">scan</span><span class="p">()</span>
<span class="n">i2c</span><span class="o">.</span><span class="n">writeto</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="sa">b</span><span class="s1">'1234'</span><span class="p">)</span>
<span class="n">i2c</span><span class="o">.</span><span class="n">readfrom</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="n">Basic</span> <span class="n">WiFi</span> <span class="n">configuration</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">network</span>
<span class="n">sta_if</span> <span class="o">=</span> <span class="n">network</span><span class="o">.</span><span class="n">WLAN</span><span class="p">(</span><span class="n">network</span><span class="o">.</span><span class="n">STA_IF</span><span class="p">);</span> <span class="n">sta_if</span><span class="o">.</span><span class="n">active</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span>
<span class="n">sta_if</span><span class="o">.</span><span class="n">scan</span><span class="p">()</span> <span class="c1"># Scan for available access points</span>
<span class="n">sta_if</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s2">"<AP_name>"</span><span class="p">,</span> <span class="s2">"<password>"</span><span class="p">)</span> <span class="c1"># Connect to an AP</span>
<span class="n">sta_if</span><span class="o">.</span><span class="n">isconnected</span><span class="p">()</span> <span class="c1"># Check for successful connection</span>
<span class="n">Control</span> <span class="n">commands</span><span class="p">:</span>
<span class="n">CTRL</span><span class="o">-</span><span class="n">A</span> <span class="o">--</span> <span class="n">on</span> <span class="n">a</span> <span class="n">blank</span> <span class="n">line</span><span class="p">,</span> <span class="n">enter</span> <span class="n">raw</span> <span class="n">REPL</span> <span class="n">mode</span>
<span class="n">CTRL</span><span class="o">-</span><span class="n">B</span> <span class="o">--</span> <span class="n">on</span> <span class="n">a</span> <span class="n">blank</span> <span class="n">line</span><span class="p">,</span> <span class="n">enter</span> <span class="n">normal</span> <span class="n">REPL</span> <span class="n">mode</span>
<span class="n">CTRL</span><span class="o">-</span><span class="n">C</span> <span class="o">--</span> <span class="n">interrupt</span> <span class="n">a</span> <span class="n">running</span> <span class="n">program</span>
<span class="n">CTRL</span><span class="o">-</span><span class="n">D</span> <span class="o">--</span> <span class="n">on</span> <span class="n">a</span> <span class="n">blank</span> <span class="n">line</span><span class="p">,</span> <span class="n">do</span> <span class="n">a</span> <span class="n">soft</span> <span class="n">reset</span> <span class="n">of</span> <span class="n">the</span> <span class="n">board</span>
<span class="n">CTRL</span><span class="o">-</span><span class="n">E</span> <span class="o">--</span> <span class="n">on</span> <span class="n">a</span> <span class="n">blank</span> <span class="n">line</span><span class="p">,</span> <span class="n">enter</span> <span class="n">paste</span> <span class="n">mode</span>
<span class="n">For</span> <span class="n">further</span> <span class="n">help</span> <span class="n">on</span> <span class="n">a</span> <span class="n">specific</span> <span class="nb">object</span><span class="p">,</span> <span class="nb">type</span> <span class="n">help</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="n">For</span> <span class="n">a</span> <span class="nb">list</span> <span class="n">of</span> <span class="n">available</span> <span class="n">modules</span><span class="p">,</span> <span class="nb">type</span> <span class="n">help</span><span class="p">(</span><span class="s1">'modules'</span><span class="p">)</span>
</code></pre></div>
<p>根据帮助信息,REPL 有几个快捷键:</p>
<ul>
<li>Ctrl+A:在空行的情况下输入,用于进入 <code>raw</code> 模式;</li>
<li>Ctrl+B:在空行的情况下输入,用于进入 <code>normal</code> 模式,也就是默认模式;</li>
<li>Ctrl+C:在任意情况下输入,用于中断当前运行的程序,即为 Python 程序员熟知的 KeyboardInterrupt;</li>
<li>Ctrl+D:在空行的情况下输入,软重启开发板,换言之 Python 解释器环境重置了;</li>
<li>Ctrl+E:在空行的情况下输入,进入粘贴模式,适合用来粘贴测试代码,原默认情况下会自动缩进,这个模式就没有了;</li>
</ul>
<h2 id="通过串口上传文件">通过串口上传文件<a class="headerlink" href="#通过串口上传文件" title="Permanent link">¶</a></h2>
<p>使用 <code>adafruit-ampy</code> 进行文件传输是比较常见的做法,此外还有使用 <code>WebREPL</code> 在网页上上传内容、使用编辑器插件等等多种方法。此处暂不详叙。BTW 不少编辑器插件其实也是 ampy 包装来的。</p>
<p>为了延长 Flash 整体寿命、增强稳定性,可以使用 LittleFS 取代 FAT,不过 v1 和 v2 版可能在特殊情况下不稳定。MicroPython 的文档在<a href="https://docs.micropython.org/en/latest/reference/filesystem.html#littlefs">这里</a></p>ThunderBird 配合 GnuPG 加密或签名电子邮件2022-04-13T00:00:00+08:002022-06-04T00:00:00+08:00tag:techie-s.work,2022-04-13:/posts/2022/04/thunerbird-use-gnupg-encrypt-sign-email/
<p>Well,都用上 PGP 了,电子邮件怎么签名还是得研究一下。</p>
<p>我将统称 <code>key</code> 为 「钥匙」,而非公钥、私钥、密钥</p>
<h2 id="目标">目标:<a class="headerlink" href="#目标" title="Permanent link">¶</a></h2>
<ul>
<li>ThunderBird 使用 PGP 加解密电子邮件</li>
<li>使用外部智能卡里的密钥</li>
</ul>
<h2 id="步骤">步骤<a class="headerlink" href="#步骤" title="Permanent link">¶</a></h2>
<p>参考 <a href="https://wiki.mozilla.org/Thunderbird:OpenPGP:Smartcards#Limitations_of_using_GnugPG">Moziilla 的 Wiki</a>。</p>
<ul>
<li>安装相关软件<ul>
<li>Thunderbird</li>
<li>GnuPG/GPGME</li>
</ul>
</li>
<li>在 Preference 页面最下面找到并点击 Config Editor, 在弹出的
高级配置页面中将 <code>mail.openpgp.allow_external_gnupg</code> 设置为 <code>true</code></li>
<li>重启 ThunderBird(为了让相关动态库能加载)</li>
<li>在账户配置中找到 <code>End-To-End Encryption</code>,针对每个帐号设置单独的密钥<ul>
<li>点击 <code>Add Key</code></li>
<li>选择 <code>Use your external key through GnuPG</code>,并点击下一步</li>
<li>输入你钥匙的 ID,一般为完整 ID 的后 16 个字符(16x4=64bit),并确认</li>
<li>至此成功为发信账户配置 PGP 钥匙</li>
<li>你还可以选择是否加密邮件主题</li>
</ul>
</li>
<li>为收件人配置 PGP 钥匙<ul>
<li>点击右上角的汉堡菜单按钮,在弹出的选项卡中找到「工具 …</li></ul></li></ul>
<p>Well,都用上 PGP 了,电子邮件怎么签名还是得研究一下。</p>
<p>我将统称 <code>key</code> 为 「钥匙」,而非公钥、私钥、密钥</p>
<h2 id="目标">目标:<a class="headerlink" href="#目标" title="Permanent link">¶</a></h2>
<ul>
<li>ThunderBird 使用 PGP 加解密电子邮件</li>
<li>使用外部智能卡里的密钥</li>
</ul>
<h2 id="步骤">步骤<a class="headerlink" href="#步骤" title="Permanent link">¶</a></h2>
<p>参考 <a href="https://wiki.mozilla.org/Thunderbird:OpenPGP:Smartcards#Limitations_of_using_GnugPG">Moziilla 的 Wiki</a>。</p>
<ul>
<li>安装相关软件<ul>
<li>Thunderbird</li>
<li>GnuPG/GPGME</li>
</ul>
</li>
<li>在 Preference 页面最下面找到并点击 Config Editor, 在弹出的
高级配置页面中将 <code>mail.openpgp.allow_external_gnupg</code> 设置为 <code>true</code></li>
<li>重启 ThunderBird(为了让相关动态库能加载)</li>
<li>在账户配置中找到 <code>End-To-End Encryption</code>,针对每个帐号设置单独的密钥<ul>
<li>点击 <code>Add Key</code></li>
<li>选择 <code>Use your external key through GnuPG</code>,并点击下一步</li>
<li>输入你钥匙的 ID,一般为完整 ID 的后 16 个字符(16x4=64bit),并确认</li>
<li>至此成功为发信账户配置 PGP 钥匙</li>
<li>你还可以选择是否加密邮件主题</li>
</ul>
</li>
<li>为收件人配置 PGP 钥匙<ul>
<li>点击右上角的汉堡菜单按钮,在弹出的选项卡中找到「工具」(Tools)-> <code>OpenPGP Key Manager</code></li>
<li>或者直接在账户配置中找到并点击 <code>OpenPGP Key Manager</code>,在新跳出的窗口中导入相关密钥</li>
<li>可以通过 <code>OpenPGP Key Manager</code> 菜单栏中的 <code>File</code> 从文件导入钥匙</li>
<li>可以通过 <code>OpenPGP Key Manager</code> 菜单栏中的 <code>Edit</code> 从剪切板或网址导入钥匙</li>
</ul>
</li>
</ul>
<h2 id="发邮件">发邮件<a class="headerlink" href="#发邮件" title="Permanent link">¶</a></h2>
<p>在新邮件编辑窗口,能找到一个标签为「security」的按钮,点击后便能明白如何对当前邮件启用加密了。</p>
<h2 id="注意点">注意点<a class="headerlink" href="#注意点" title="Permanent link">¶</a></h2>
<ul>
<li>有的邮件服务商会把加密邮件当作垃圾邮件,导致无法发送或接收;</li>
<li>有些客户端不支持加密的邮件主题;</li>
</ul>GNU/Linux 环境下 Calibre DeDRM 插件的使用2022-04-12T00:00:00+08:002022-06-04T00:00:00+08:00tag:techie-s.work,2022-04-12:/posts/2022/04/gnu-linux-calibre-dedrm/<h2 id="前言">前言<a class="headerlink" href="#前言" title="Permanent link">¶</a></h2>
<ul>
<li>2022-06-04:得知 Kindle 将退出中国市场,感觉还是略为可惜,各位赶快备份自己的电子书资料吧。</li>
</ul>
<p>之前都是在 Windows 上用的,但现在发现直接在 Linux 上也不是不行。</p>
<p>注:解除DRM的目的是在更多支持的设备上使用从正规渠道购买的数字资料,并保证数字资料可用性。</p>
<h2 id="目的">目的<a class="headerlink" href="#目的" title="Permanent link">¶</a></h2>
<ul>
<li>在 GNU/Linux 上使用 Kindle</li>
<li>在 GNU/Linux 上使用 Calibre 配合插件解除 AZW 的 DRM</li>
</ul>
<h2 id="安装-calibrekindlededrm-插件">安装 Calibre、Kindle、DeDRM 插件<a class="headerlink" href="#安装-calibrekindlededrm-插件" title="Permanent link">¶</a></h2>
<ul>
<li>安装 Calibre:使用包管理器;</li>
<li>安装 Kindle;<ul>
<li>装 wine(用你的包管理器);</li>
<li>用 wine 安装 Kindle for PC(直接用 wine 运行 exe);</li>
</ul>
</li>
<li>安装 <a href="https://github.com/apprenticeharper/DeDRM_tools">DeDRM</a> 插件;</li>
</ul>
<h3 id="注意点">注意点<a class="headerlink" href="#注意点" title="Permanent link">¶</a></h3>
<p>由于插件用到的部分脚本没有针对 Linux 改写,如果不想改代码的话,有几个注意点:</p>
<ul>
<li>wine 环境下 <strong>必须</strong> 安装 Python(含pip);<ul>
<li>需要额外安装 <code>pycryptodome</code> ;</li>
</ul>
</li>
<li>宿主 Linux 环境下,安装 python 和相关的库 <code>pycrypto</code> ;</li>
<li>如果使用了不同的 <code>WINEPREFIX</code> 安装 Kindle,请单独配置好插件的 <code>WINEPREFIX</code>;</li>
</ul>
<p>这样环境就配置好了。</p>
<h2 id="解除文档-drm">解除文档 …</h2><h2 id="前言">前言<a class="headerlink" href="#前言" title="Permanent link">¶</a></h2>
<ul>
<li>2022-06-04:得知 Kindle 将退出中国市场,感觉还是略为可惜,各位赶快备份自己的电子书资料吧。</li>
</ul>
<p>之前都是在 Windows 上用的,但现在发现直接在 Linux 上也不是不行。</p>
<p>注:解除DRM的目的是在更多支持的设备上使用从正规渠道购买的数字资料,并保证数字资料可用性。</p>
<h2 id="目的">目的<a class="headerlink" href="#目的" title="Permanent link">¶</a></h2>
<ul>
<li>在 GNU/Linux 上使用 Kindle</li>
<li>在 GNU/Linux 上使用 Calibre 配合插件解除 AZW 的 DRM</li>
</ul>
<h2 id="安装-calibrekindlededrm-插件">安装 Calibre、Kindle、DeDRM 插件<a class="headerlink" href="#安装-calibrekindlededrm-插件" title="Permanent link">¶</a></h2>
<ul>
<li>安装 Calibre:使用包管理器;</li>
<li>安装 Kindle;<ul>
<li>装 wine(用你的包管理器);</li>
<li>用 wine 安装 Kindle for PC(直接用 wine 运行 exe);</li>
</ul>
</li>
<li>安装 <a href="https://github.com/apprenticeharper/DeDRM_tools">DeDRM</a> 插件;</li>
</ul>
<h3 id="注意点">注意点<a class="headerlink" href="#注意点" title="Permanent link">¶</a></h3>
<p>由于插件用到的部分脚本没有针对 Linux 改写,如果不想改代码的话,有几个注意点:</p>
<ul>
<li>wine 环境下 <strong>必须</strong> 安装 Python(含pip);<ul>
<li>需要额外安装 <code>pycryptodome</code> ;</li>
</ul>
</li>
<li>宿主 Linux 环境下,安装 python 和相关的库 <code>pycrypto</code> ;</li>
<li>如果使用了不同的 <code>WINEPREFIX</code> 安装 Kindle,请单独配置好插件的 <code>WINEPREFIX</code>;</li>
</ul>
<p>这样环境就配置好了。</p>
<h2 id="解除文档-drm">解除文档 DRM<a class="headerlink" href="#解除文档-drm" title="Permanent link">¶</a></h2>
<ul>
<li>拖拽方式导入文档;</li>
<li>阅读/打开一下导入的文档;<ul>
<li>以确保文档已解锁;某些文档的密钥是专用的,不是通用密钥库的,要打开文档后插件才会解码。</li>
</ul>
</li>
</ul>luatos-core 开发板适配 MicroPython2022-03-26T00:00:00+08:002022-04-04T00:00:00+08:00tag:techie-s.work,2022-03-26:/posts/2022/03/micropython-add-luatos-core/<h2 id="适配添加开发板定义">适配(添加开发板定义)<a class="headerlink" href="#适配添加开发板定义" title="Permanent link">¶</a></h2>
<p>最近买了几块 ESP32C3 板子,LuatOS 团队出品,很漂亮,也很实惠。</p>
<p>这里说是适配,大部分工作已经由别人做好了,我只要把板子定义写好即可。不过官方的文档稍微有点散,make 很多参数
都挺难调整的(因为没办法轻松知道有哪些开关)。</p>
<h2 id="编译">编译<a class="headerlink" href="#编译" title="Permanent link">¶</a></h2>
<p>时间:2022-03-26, 16:57, GMT+8</p>
<p>hmm,release 版(1.18)的 MicroPython 好像还不支持 ESP32-C3,不过 Master 分支的可以(实验所得结论)。
编译的参数比较分散,这里集中写一下,略过环境准备过程,有疑问的可以先看看<a href="https://github.com/micropython/micropython/tree/master/ports/esp32#readme">这里</a></p>
<div class="highlight"><pre><span></span><code><span class="c1"># 下载 esp-idf, 版本需不低于 4.3</span>
<span class="c1"># 请参考这里:https://github.com/espressif/esp-idf/releases/tag/v4.4</span>
<span class="c1"># 编译 MicroPython 的过程参考这里</span>
<span class="c1"># https://github.com/micropython/micropython/tree/master/ports/esp32#readme</span>
<span class="c1">#source ../esp-idf-v4.4/export.sh</span>
git<span class="w"> </span>clone<span class="w"> </span>https://github.com/micropython/micropython
make<span class="w"> </span>-C<span class="w"> </span>mpy-cross
<span class="nb">cd</span><span class="w"> </span>ports/esp32
<span class="nb">export</span><span class="w"> </span><span class="nv">BOARD</span><span class="o">=</span>LUATOS_C3_CORE
<span class="nb">export</span><span class="w"> </span><span class="nv">PORT</span><span class="o">=</span>/dev/ttyACM0
<span class="nb">export</span><span class="w"> </span><span class="nv">FROZEN_MANIFEST</span><span class="o">=</span><span class="sb">`</span>readlink<span class="w"> </span>-f<span class="w"> </span>boards/LUATOS_C3_CORE/manifest.py<span class="sb">`</span>
make<span class="w"> </span>submodules …</code></pre></div><h2 id="适配添加开发板定义">适配(添加开发板定义)<a class="headerlink" href="#适配添加开发板定义" title="Permanent link">¶</a></h2>
<p>最近买了几块 ESP32C3 板子,LuatOS 团队出品,很漂亮,也很实惠。</p>
<p>这里说是适配,大部分工作已经由别人做好了,我只要把板子定义写好即可。不过官方的文档稍微有点散,make 很多参数
都挺难调整的(因为没办法轻松知道有哪些开关)。</p>
<h2 id="编译">编译<a class="headerlink" href="#编译" title="Permanent link">¶</a></h2>
<p>时间:2022-03-26, 16:57, GMT+8</p>
<p>hmm,release 版(1.18)的 MicroPython 好像还不支持 ESP32-C3,不过 Master 分支的可以(实验所得结论)。
编译的参数比较分散,这里集中写一下,略过环境准备过程,有疑问的可以先看看<a href="https://github.com/micropython/micropython/tree/master/ports/esp32#readme">这里</a></p>
<div class="highlight"><pre><span></span><code><span class="c1"># 下载 esp-idf, 版本需不低于 4.3</span>
<span class="c1"># 请参考这里:https://github.com/espressif/esp-idf/releases/tag/v4.4</span>
<span class="c1"># 编译 MicroPython 的过程参考这里</span>
<span class="c1"># https://github.com/micropython/micropython/tree/master/ports/esp32#readme</span>
<span class="c1">#source ../esp-idf-v4.4/export.sh</span>
git<span class="w"> </span>clone<span class="w"> </span>https://github.com/micropython/micropython
make<span class="w"> </span>-C<span class="w"> </span>mpy-cross
<span class="nb">cd</span><span class="w"> </span>ports/esp32
<span class="nb">export</span><span class="w"> </span><span class="nv">BOARD</span><span class="o">=</span>LUATOS_C3_CORE
<span class="nb">export</span><span class="w"> </span><span class="nv">PORT</span><span class="o">=</span>/dev/ttyACM0
<span class="nb">export</span><span class="w"> </span><span class="nv">FROZEN_MANIFEST</span><span class="o">=</span><span class="sb">`</span>readlink<span class="w"> </span>-f<span class="w"> </span>boards/LUATOS_C3_CORE/manifest.py<span class="sb">`</span>
make<span class="w"> </span>submodules
make
<span class="c1"># 刷固件</span>
make<span class="w"> </span>deploy
</code></pre></div>让Linux的Firefox使用硬件加速2022-03-12T00:00:00+08:002022-09-17T00:00:00+08:00tag:techie-s.work,2022-03-12:/posts/2022/03/run-firefox-with-hardware-acceleration-on-linux/<h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>硬件加速不好吗, 硬件加速它省电啊!</p>
<h2 id="坑">坑<a class="headerlink" href="#坑" title="Permanent link">¶</a></h2>
<ul>
<li><del>Firefox 对笔记本双显卡不太友好, 检测会出问题</del> 貌似在102版本之后修好了,会
自动选用合适的。</li>
</ul>
<h2 id="操作">操作<a class="headerlink" href="#操作" title="Permanent link">¶</a></h2>
<p>按照下面的表格修改 <code>about:config</code> 里面的值</p>
<table>
<thead>
<tr>
<th style="text-align: left;">键</th>
<th style="text-align: left;">值</th>
<th style="text-align: left;">解释</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">gfx.webrender.all</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">让 WebRender 能在 PRIME 环境下能用核心显卡的硬件加速</td>
</tr>
<tr>
<td style="text-align: left;">gfx.webrender.enabled</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">开启 WebRender, VAAPI必须</td>
</tr>
<tr>
<td style="text-align: left;">gfx.x11-egl.force-enabled</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">若 FFver < 94, 强制在 X11 启用 EGL 接口渲染</td>
</tr>
<tr>
<td style="text-align: left;">media.ffmpeg.vaapi.enabled</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">开启 VAAPI</td>
</tr>
<tr>
<td style="text-align: left;">media.ffvpx.enabled</td>
<td style="text-align: left;">false</td>
<td style="text-align: left;">禁用内置vp8/9解码器, 可能有优先级bug, 参考 ArchWiki:Firefox(版本102及以上不需要)</td>
</tr>
<tr>
<td style="text-align: left;">media.navigator.mediadatadecoder_vpx_enabled</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">WebRTC 硬件解码</td>
</tr>
<tr>
<td style="text-align: left;">media.rdd-vpx.enabled</td>
<td style="text-align: left;">false</td>
<td style="text-align: left;">把部分解码器线程挪出 RDD (沙盒), 避免找不到库, 这样不用完全关闭 RDD (版本102及以上不需要)</td>
</tr>
</tbody>
</table>
<p>我这样操作起来就开启了所有的硬件加速</p>
<div class="admonition tip">
<p class="admonition-title">如何判断浏览器正在使用 VAAPI 硬件解码</p>
<p>在命令行设置环境变量 <code>MOZ_LOG="PlatformDecoderModule:5"</code> 并启动 Firefox, 比如这样<code>env …</code></p></div><h2 id="动机">动机<a class="headerlink" href="#动机" title="Permanent link">¶</a></h2>
<p>硬件加速不好吗, 硬件加速它省电啊!</p>
<h2 id="坑">坑<a class="headerlink" href="#坑" title="Permanent link">¶</a></h2>
<ul>
<li><del>Firefox 对笔记本双显卡不太友好, 检测会出问题</del> 貌似在102版本之后修好了,会
自动选用合适的。</li>
</ul>
<h2 id="操作">操作<a class="headerlink" href="#操作" title="Permanent link">¶</a></h2>
<p>按照下面的表格修改 <code>about:config</code> 里面的值</p>
<table>
<thead>
<tr>
<th style="text-align: left;">键</th>
<th style="text-align: left;">值</th>
<th style="text-align: left;">解释</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">gfx.webrender.all</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">让 WebRender 能在 PRIME 环境下能用核心显卡的硬件加速</td>
</tr>
<tr>
<td style="text-align: left;">gfx.webrender.enabled</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">开启 WebRender, VAAPI必须</td>
</tr>
<tr>
<td style="text-align: left;">gfx.x11-egl.force-enabled</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">若 FFver < 94, 强制在 X11 启用 EGL 接口渲染</td>
</tr>
<tr>
<td style="text-align: left;">media.ffmpeg.vaapi.enabled</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">开启 VAAPI</td>
</tr>
<tr>
<td style="text-align: left;">media.ffvpx.enabled</td>
<td style="text-align: left;">false</td>
<td style="text-align: left;">禁用内置vp8/9解码器, 可能有优先级bug, 参考 ArchWiki:Firefox(版本102及以上不需要)</td>
</tr>
<tr>
<td style="text-align: left;">media.navigator.mediadatadecoder_vpx_enabled</td>
<td style="text-align: left;">true</td>
<td style="text-align: left;">WebRTC 硬件解码</td>
</tr>
<tr>
<td style="text-align: left;">media.rdd-vpx.enabled</td>
<td style="text-align: left;">false</td>
<td style="text-align: left;">把部分解码器线程挪出 RDD (沙盒), 避免找不到库, 这样不用完全关闭 RDD (版本102及以上不需要)</td>
</tr>
</tbody>
</table>
<p>我这样操作起来就开启了所有的硬件加速</p>
<div class="admonition tip">
<p class="admonition-title">如何判断浏览器正在使用 VAAPI 硬件解码</p>
<p>在命令行设置环境变量 <code>MOZ_LOG="PlatformDecoderModule:5"</code> 并启动 Firefox, 比如这样<code>env MOZ_LOG="PlatformDecoderModule:5" firefox</code>
然后打开一个测试视频, 看看日志里有关 <code>VA-API</code> 的输出</p>
</div>
<h2 id="补充">补充<a class="headerlink" href="#补充" title="Permanent link">¶</a></h2>
<p>之前有个操作刻意没有写。因为 Firefox 的一些问题,上面的参数仍不能开启旧版Firefox的
<code>vaapi</code>视频解码。如果有需要,需要完全关闭 RDD 沙箱。用下面的方法运行 Firefox:</p>
<div class="highlight"><pre><span></span><code>env<span class="w"> </span><span class="nv">MOZ_DISABLE_RDD_SANDBOX</span><span class="o">=</span><span class="m">1</span><span class="w"> </span>firefox
</code></pre></div>
<div class="admonition warning">
<p class="admonition-title">安全警告</p>
<p>使用上述方法将带来一定安全隐患,请务必确认自己的需求、注意计算机系统安全!</p>
</div>Pelican 使用 Elegant 主题的手记2022-02-06T00:00:00+08:002022-02-06T00:00:00+08:00tag:techie-s.work,2022-02-06:/posts/2022/02/pelican-blogging-with-theme-elegant/
<h2 id="目的">目的<a class="headerlink" href="#目的" title="Permanent link">¶</a></h2>
<p>记录一下各种形式的内容格式, 备忘</p>
<h2 id="meta信息示例">meta信息示例<a class="headerlink" href="#meta信息示例" title="Permanent link">¶</a></h2>
<p>这是 Markdown meta 信息的完全版, 放在每个 MD 的开头(无空行). 部分功能用到了插件.</p>
<div class="highlight"><pre><span></span><code>---
title: My super title
date: 2010-12-03
modified: 2010-12-05 19:30
category: Python
tags: pelican, publishing
slug: my-post-template
authors: nobody, no one
summary: a template for blog articles
lang: en
translation: false
status: draft
template: article
save_as: index.md
url: whatever
---
</code></pre></div>
<h2 id="内容书写示例">内容书写示例<a class="headerlink" href="#内容书写示例" title="Permanent link">¶</a></h2>
<p>这是普通文字</p>
<div class="highlight"><pre><span></span><code>这是代码块
</code></pre></div>
<p>段落间的 <code>代码</code> 两边最好加上空格, 其它 <strong>粗体</strong> <em>斜体</em> 也是类似.</p>
<p>删除线用 HTML 代码块 <del><code><del>被划线内容1</del></code></del> <s><code><s>被划线内容2</s></code></s> 来包裹最方便, 不易出错</p>
<p><em><del>用符号是不行嘀</del></em></p>
<ul>
<li>这是列表</li>
<li>前后要空行</li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: left;">Key1</th>
<th style="text-align: center;">Key2</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">这是一个表格</td>
<td style="text-align: center;">同样需要前后空行</td>
</tr>
</tbody>
</table>
<blockquote>
<p>这是引用内容,
每个段落的开头要有顶格的 <code>></code></p>
<p>中间的空白行 (包括只有空格的行) 会自动合并成一行, 并合并上下的引用内容</p>
</blockquote>
<p>用别的文字 …</p>
<h2 id="目的">目的<a class="headerlink" href="#目的" title="Permanent link">¶</a></h2>
<p>记录一下各种形式的内容格式, 备忘</p>
<h2 id="meta信息示例">meta信息示例<a class="headerlink" href="#meta信息示例" title="Permanent link">¶</a></h2>
<p>这是 Markdown meta 信息的完全版, 放在每个 MD 的开头(无空行). 部分功能用到了插件.</p>
<div class="highlight"><pre><span></span><code>---
title: My super title
date: 2010-12-03
modified: 2010-12-05 19:30
category: Python
tags: pelican, publishing
slug: my-post-template
authors: nobody, no one
summary: a template for blog articles
lang: en
translation: false
status: draft
template: article
save_as: index.md
url: whatever
---
</code></pre></div>
<h2 id="内容书写示例">内容书写示例<a class="headerlink" href="#内容书写示例" title="Permanent link">¶</a></h2>
<p>这是普通文字</p>
<div class="highlight"><pre><span></span><code>这是代码块
</code></pre></div>
<p>段落间的 <code>代码</code> 两边最好加上空格, 其它 <strong>粗体</strong> <em>斜体</em> 也是类似.</p>
<p>删除线用 HTML 代码块 <del><code><del>被划线内容1</del></code></del> <s><code><s>被划线内容2</s></code></s> 来包裹最方便, 不易出错</p>
<p><em><del>用符号是不行嘀</del></em></p>
<ul>
<li>这是列表</li>
<li>前后要空行</li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: left;">Key1</th>
<th style="text-align: center;">Key2</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">这是一个表格</td>
<td style="text-align: center;">同样需要前后空行</td>
</tr>
</tbody>
</table>
<blockquote>
<p>这是引用内容,
每个段落的开头要有顶格的 <code>></code></p>
<p>中间的空白行 (包括只有空格的行) 会自动合并成一行, 并合并上下的引用内容</p>
</blockquote>
<p>用别的文字分割开来才行</p>
<blockquote>
<p>感觉略有奇怪</p>
</blockquote>
<div class="admonition note">
<p class="admonition-title">注意标题要有引号, 默认为note</p>
<p>非标准形式的 note, 应该是用到了拓展的 Markdown(?)</p>
</div>
<div class="highlight"><pre><span></span><code>!!! note "注意标题要有引号, 默认为note"
非标准形式的 note, 应该是用到了拓展的 Markdown(?)
</code></pre></div>
<div class="admonition important">
<p class="admonition-title">important 同理</p>
<p>把上面的 note 改成 important 就行, 自己多试试</p>
</div>
<div class="admonition warning">
<p class="admonition-title">这是警告</p>
<p>把上面的note改成warning就行</p>
</div>
<div class="admonition attention">
<p class="admonition-title">attention 同理</p>
<p>自己多试试</p>
</div>
<div class="admonition danger">
<p class="admonition-title">Danger</p>
<p>此外还有 <code>danger</code></p>
</div>
<div class="admonition tip">
<p class="admonition-title">Tip</p>
<p>我也不知道 <code>tip</code> 是干嘛的</p>
</div>
<div class="admonition hint">
<p class="admonition-title">Hint</p>
<p>为什么还要有 <code>hint</code></p>
</div>
<p>分割线前</p>
<hr/>
<p>分割线后</p>