~$ /opt/gcc-riscv/riscv-wch-embedded-gcc-v1.70/bin/riscv-none-embed-gcc --version
+riscv-none-embed-gcc (xPack GNU RISC-V Embedded GCC, 64-bit)8.2.0
+Copyright (C)2018 Free Software Foundation, Inc.
+This is free software; see the sourcefor copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+~$ /opt/openocd/wch-openocd-v1.70/bin/openocd --version
+Open On-Chip Debugger 0.11.0+dev-02415-gfad123a16-dirty (2023-02-22-15:09)
+Licensed under GNU GPL v2
+For bug reports, read
+ http://openocd.org/doc/doxygen/bugs.html
/opt/openocd/wch-openocd-v1.70/bin$ ./openocd -f wch-riscv.cfg
+Open On-Chip Debugger 0.11.0+dev-02415-gfad123a16-dirty (2023-02-22-15:09)
+Licensed under GNU GPL v2
+For bug reports, read
+ http://openocd.org/doc/doxygen/bugs.html
+Info : only one transport option; autoselect 'sdi'
+Warn : Transport "sdi" was already selected
+Ready for Remote Connections
+Info : Listening on port 6666 for tcl connections
+Info : Listening on port 4444 for telnet connections
+Info : WCH-Link-CH549 mode:RV version 2.8
+Info : wlink_init ok
+Info : clock speed 6000 kHz
+Info : [wch_riscv.cpu.0] datacount=2 progbufsize=8
+Info : [wch_riscv.cpu.0] Examined RISC-V core; found 1 harts
+Info : [wch_riscv.cpu.0] XLEN=32, misa=0x40901105
+[wch_riscv.cpu.0] Target successfully examined.
+Info : starting gdb server for wch_riscv.cpu.0 on 3333
+Info : Listening on port 3333 for gdb connections
在第二个终端中, 启动 GDB Client
/opt/gcc-riscv/riscv-wch-embedded-gcc-v1.70/bin/riscv-none-embed-gdb Build/app.elf
+GNU gdb (xPack GNU RISC-V Embedded GCC, 64-bit) 8.3
+Copyright (C) 2019 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+Type "show copying" and "show warranty" for details.
+This GDB was configured as "--host=x86_64-unknown-linux-gnu --target=riscv-none-embed".
+Type "show configuration" for configuration details.
+For bug reporting instructions, please see:
+<https://github.com/sifive/freedom-tools/issues>.
+Find the GDB manual and other documentation resources online at:
+ <http://www.gnu.org/software/gdb/documentation/>.
+For help, type "help".
+Type "apropos word" to search for commands related to "word"...
+Reading symbols from Build/app.elf...
设置GDB参数
(gdb) set mem inaccessible-by-default off
+(gdb) set remotetimeout unlimited
+(gdb) set architecture riscv:rv32
+The target architecture is assumed to be riscv:rv32
连接到GDB服务, load 载入程序, b 设置断点, c 继续执行, i r 查看寄存器, i local 查看全部局部变量, list 查看代码. c过程中可以用Ctrl+C暂停, quit 退出
(gdb) target remote localhost:3333
+Remote debugging using localhost:3333
+0x00000428 in Delay_Ms (n=n@entry=1000) at Debug/debug.c:74
+74 while((SysTick->SR & (1 << 0)) != (1 << 0));
+(gdb) i r pc
+pc 0x428 0x428 <Delay_Ms+46>
+(gdb) load
+Loading section .init, size 0x38 lma 0x0
+Loading section .vector, size 0x148 lma 0x38
+Loading section .text, size 0x1e4c lma 0x180
+Loading section .data, size 0x88 lma 0x1fcc
+Start address 0x0, load size 8276
+Transfer rate: 4 KB/sec, 2069 bytes/write.
+(gdb) i r pc
+pc 0x0 0x0 <_start>
+(gdb) b main
+Breakpoint 1 at 0x25e: file User/main.c, line 55.
+(gdb) c
+Continuing.
+Note: automatically using hardware breakpoints for read-only addresses.
+Breakpoint 1, main () at User/main.c:55
+55 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
+(gdb) i r pc
+pc 0x25e 0x25e <main>
+(gdb) list
+50 */
+51 int main(void)
+52 {
+53 u8 i = 0;
+54
+55 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
+56 Delay_Init();
+57 USART_Printf_Init(115200);
+58 printf("SystemClk:%ld\r\n", SystemCoreClock);
+59
+(gdb)
ERROR: GDB major version should be >=9, yours is 8; GDB could not start as expected. Bad installation or version mismatch. See if you can start gdb from a shell prompt and check its version (Must be >=9)
配置完成后就可以开始 Debug了, 可以通过右侧的 Run And Debug 面板, 点绿色三角形启动, 也可以按 F5启动, 我使用的是 IntelliJ IDEA Keybinding, 所以debug快捷键和 IDEA 是一样的, 单步 F8, 继续 F9, 进入 F7. 在 Run And Debug 面板左侧可以观察变量和外设寄存器对应的值. 非常方便.
Prompt: You are now acting as an expert in the field of [Put professional fields here…]. From a professional point of view, do you think there is any need to modify the above content? Be careful not to modify the whole text, you need to point out the places that need to be modified one by one, and give revision opinions and recommended revision content.
+
+提示:你现在扮演一个[这里放你所研究的领域] 领域的专家,从专业的角度,您认为上面这些内容是否有需要修改的地方? 注意,不要全文修改,您需要一一指出需要修改的地方,并且给出修改意见以及推荐的修改内容。
节省空间
下面这种方式可以在一定程度上解决一次输出不完整与输出过程中断网的情况。
Prompt: [Put your requirements here…] , since your output length is limited, in order to save space. Please use ellipses for the parts you don’t think need to be modified.
Prompt: I am trying to get good results from GPT-4 on the following prompt: ‘你的提示词.’ Could you write a better prompt that is more optimal for GPT-4 and would produce better results?
Prompt:Still the above question, I think your answer is not good enough. Please answer again, this time focusing on removing redundancy from this passage.
Prompt: You are now acting as an expert in the field of [Insert your research field]. From a professional point of view, please make minor edits to the introduction to better match the title “[Insert your latest paper title here]”. Note, this is a subtle edit, don’t change the overall content and only adjust details to match the title.
Prompt:Note that in addition to giving the modified content, please also indicate which paragraphs and sentences have been modified in the revised version.
提示:注意,除了给出润色修改之后的内容,还请指明修订的版本中具体修改了哪些段落的哪几句话。
补充:这里最好是用一些在线的文本对比工具来观察,例如下面这种,更加一目了然。
+
段落润色
直接润色
Prompt: Polish the paragraph above to make it more logical, and academic.
Prompt:For the sentence “[Before polished sentence]”, why did you polish it to be “[Polished sentence]”.
提示:对于“[润色前的句子]”这句话,为什么你润色为成“[润色后的句子]”。
同理,本文接下来的所有用法都可以配合着上面的方式进行追问。
特定要求
1)结合背景知识
相比于上面直接的润色,这种方式可能会让它输出一些更丰富的信息。
Prompt: According to your knowledge about XXX and XXX, is there a better way to write the above paragraph, please help to revise it so that it can be used in academic papers.
Prompt: Polish and rewrite the above content to make it more in line with the style of academic papers, and at the same time, it can be more professional. If there are parts that do not conform to facts or logic, please refer to the part of xxxxx for the above content modification.
Prompt: Based on the knowledge you have mastered about [xxx], polish and continue writing the above content to make the content richer and more complete.
Prompt: Please help me analyze and optimize the logical structure of this argument to make it more convincing.
提示:请帮我分析和优化这段论证的逻辑结构,以使其更具说服力。
长篇文本处理能力
由于GPT 4.0具有更长的记忆窗口,它可以更有效地进行长篇幅文本的润色。
Prompt: Please read and polish the entire paper to ensure consistency and coherence.
提示:请阅读并润色整篇论文,确保一致性和连贯性。(就是这么简单粗暴!)
当然,如果论文特别长,可以分为多次喂给它,像下面这样:
+
+给完第一部分之后:
+
Prompt: I have written the XXX section, but I am not satisfied with its structure and coherence. Please help me reorganize the content and improve its coherence.
提示:我写了XXX部分,但我对其结构和连贯性不满意。请帮我重新组织内容,提高其连贯性。
Prompt: Please review and revise the entire literature review section of my paperensuring that it meets the standards of academic writing and the content iscoherent and well-structured.
提示:请审查并修改我论文的整个文献综述部分,确保符合学术写作标准,内容连贯且结构合理。
提供独特见解
Prompt: Please provide me with some unique insights that I can discuss in my paper, based on the latest research that you are aware of.
提示:请根据你所了解的最新研究,为我提供一些独特的见解以便我在论文中进行讨论。
深度分析与评估
Prompt: Please help me to conduct an in-depth analysis of these research methods and data, and provide me with an assessment of their advantages and disadvantages.
+
+
+
+
\ No newline at end of file
diff --git a/LaTeX-Environment.html b/LaTeX-Environment.html
new file mode 100644
index 0000000..828167d
--- /dev/null
+++ b/LaTeX-Environment.html
@@ -0,0 +1 @@
+The template name has zero length, please check the "template" field in your Notion table.
\ No newline at end of file
diff --git a/LaTeX-Teach.html b/LaTeX-Teach.html
new file mode 100644
index 0000000..828167d
--- /dev/null
+++ b/LaTeX-Teach.html
@@ -0,0 +1 @@
+The template name has zero length, please check the "template" field in your Notion table.
\ No newline at end of file
diff --git a/Latex_refer.html b/Latex_refer.html
new file mode 100644
index 0000000..828167d
--- /dev/null
+++ b/Latex_refer.html
@@ -0,0 +1 @@
+The template name has zero length, please check the "template" field in your Notion table.
\ No newline at end of file
diff --git a/Note.html b/Note.html
new file mode 100644
index 0000000..c012f48
--- /dev/null
+++ b/Note.html
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Note | 413’s Website
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Posted on Wed, Nov 30, 2022
+
+
+
+
+ Web
+
+
+
+
+
+
+
配置的内容:
test tets
PS E:\Dev\rclone-v1.62.2-windows-amd64> .\rclone authorize "onedrive""6cxxxoqMTDWnJtWbqE"
+2023/03/22 12:18:45 NOTICE: Make sure your Redirect URL is set to "http://localhost:53682/"in your custom config.
+2023/03/22 12:18:45 NOTICE: If your browser doesn't open automatically go to the following link: http://127.0.0.1:53682/auth?state=h-IKR1LgmsDyQUd_tz2x-Q
+2023/03/22 12:18:45 NOTICE: Log in and authorize rclone for access
+2023/03/22 12:18:45 NOTICE: Waiting for code...
+2023/03/22 12:18:49 NOTICE: Got code
+Paste the following into your remote machine --->
+ {"access_token":"","expiry":"2023-03-22T12:40:42.6524031+08:00"}
+PS E:\Dev\rclone-v1.62.2-windows-amd64> .\rclone config
+No remotes found, make a new one?
+n) New remote
+s) Set configuration password
+q) Quit config
+n/s/q> n
+
+Enter name for new remote.
+name> OneMusic
+
+Option Storage.
+Type of storage to configure.
+Choose a number from below, or typein your own value.
+ 1 / 1Fichier
+ \(fichier)
+ 2 / Akamai NetStorage
+ \(netstorage)
+ 3 / Alias for an existing remote
+ \(alias)
+ 4 / Amazon Drive
+ \(amazon cloud drive)
+ 5 / Amazon S3 Compliant Storage Providers including AWS, Alibaba, Ceph, China Mobile, Cloudflare, ArvanCloud, DigitalOcean, Dreamhost, Huawei OBS, IBM COS, IDrive e2, IONOS Cloud, Liara, Lyve Cloud, Minio, Netease, RackCorp, Scaleway, SeaweedFS, StackPath, Storj, Tencent COS, Qiniu and Wasabi
+ \(s3)
+ 6 / Backblaze B2
+ \(b2)
+ 7 / Better checksums for other remotes
+ \(hasher)
+ 8 / Box
+ \(box)
+ 9 / Cache a remote
+ \(cache)
+10 / Citrix Sharefile
+ \(sharefile)
+11 / Combine several remotes into one
+ \(combine)
+12 / Compress a remote
+ \(compress)
+13 / Dropbox
+ \(dropbox)
+14 / Encrypt/Decrypt a remote
+ \(crypt)
+15 / Enterprise File Fabric
+ \(filefabric)
+16 / FTP
+ \(ftp)
+17 / Google Cloud Storage (this is not Google Drive)
+ \(google cloud storage)
+18 / Google Drive
+ \(drive)
+19 / Google Photos
+ \(google photos)
+20 / HTTP
+ \(http)
+21 / Hadoop distributed file system
+ \(hdfs)
+22 / HiDrive
+ \(hidrive)
+23 / In memory object storage system.
+ \(memory)
+24 / Internet Archive
+ \(internetarchive)
+25 / Jottacloud
+ \(jottacloud)
+26 / Koofr, Digi Storage and other Koofr-compatible storage providers
+ \(koofr)
+27 / Local Disk
+ \(local)
+28 / Mail.ru Cloud
+ \(mailru)
+29 / Mega
+ \(mega)
+30 / Microsoft Azure Blob Storage
+ \(azureblob)
+31 / Microsoft OneDrive
+ \(onedrive)
+32 / OpenDrive
+ \(opendrive)
+33 / OpenStack Swift (Rackspace Cloud Files, Memset Memstore, OVH)
+ \(swift)
+34 / Oracle Cloud Infrastructure Object Storage
+ \(oracleobjectstorage)
+35 / Pcloud
+ \(pcloud)
+36 / Put.io
+ \(putio)
+37 / QingCloud Object Storage
+ \(qingstor)
+38 / SMB / CIFS
+ \(smb)
+39 / SSH/SFTP
+ \(sftp)
+40 / Sia Decentralized Cloud
+ \(sia)
+41 / Storj Decentralized Cloud Storage
+ \(storj)
+42 / Sugarsync
+ \(sugarsync)
+43 / Transparently chunk/split large files
+ \(chunker)
+44 / Union merges the contents of several upstream fs
+ \(union)
+45 / Uptobox
+ \(uptobox)
+46 / WebDAV
+ \(webdav)
+47 / Yandex Disk
+ \(yandex)
+48 / Zoho
+ \(zoho)
+49 / premiumize.me
+ \(premiumizeme)
+50 / seafile
+ \(seafile)
+Storage>31
+
+Option client_id.
+OAuth Client Id.
+Leave blank normally.
+Enter a value. Press Enter to leave empty.
+client_id> 6c1a28xxxxe0
+
+Option client_secret.
+OAuth Client Secret.
+Leave blank normally.
+Enter a value. Press Enter to leave empty.
+client_secret> WVQxxxxxzoqMTDWnJtWbqE
+
+Option region.
+Choose national cloud region for OneDrive.
+Choose a number from below, or typein your own string value.
+Press Enter for the default (global).
+ 1 / Microsoft Cloud Global
+ \(global)
+ 2 / Microsoft Cloud for US Government
+ \(us)
+ 3 / Microsoft Cloud Germany
+ \(de)
+ 4 / Azure and Office 365 operated by Vnet Group in China
+ \(cn)
+region>1
+
+Edit advanced config?
+y) Yes
+n) No (default)
+y/n> n
+
+Use web browser to automatically authenticate rclone with remote?
+ * Say Y if the machine running rclone has a web browser you can use
+ * Say N if running rclone on a (remote) machine without web browser access
+If not sure try Y. If Y failed, try N.
+
+y) Yes (default)
+n) No
+y/n> n
+
+Option config_token.
+For this to work, you will need rclone available on a machine that has
+a web browser available.
+For morehelp and alternate methods see: https://rclone.org/remote_setup/
+Execute the following on the machine with the web browser (same rclone
+version recommended):
+ rclone authorize "onedrive""eyJjbGllxxUxxbkp0V2JxRSJ9"
+Then paste the result.
+Enter a value.
+config_token>{"access_token":"","expiry":"2023-03-22T12:40:42.6524031+08:00"}
+Option config_type.
+Type of connection
+Choose a number from below, or typein an existing string value.
+Press Enter for the default (onedrive).
+ 1 / OneDrive Personal or Business
+ \(onedrive)
+ 2 / Root Sharepoint site
+ \(sharepoint)
+ / Sharepoint site name or URL
+ 3| E.g. mysite or https://contoso.sharepoint.com/sites/mysite
+ \(url)
+ 4 / Search for a Sharepoint site
+ \(search)
+ 5 / Type in driveID (advanced)
+ \(driveid)
+ 6 / Type in SiteID (advanced)
+ \(siteid)
+ / Sharepoint server-relative path (advanced)
+ 7| E.g. /teams/hr
+ \(path)
+config_type>1
+
+Option config_driveid.
+Select drive you want to use
+Choose a number from below, or typein your own string value.
+Press Enter for the default (b!v83P4RgYYEWmnzOjKs3kRyJ-nHD7OyRIu8y4erYw7RmUsMPzXJQ-RZfn26kOtqlo).
+ 1 / OneDrive (business)
+ \(b!v83P4RgYYEWmnzOjKs3kRyJ-nHD7OyRIu8y4erYw7RmUsMPzXJQ-RZfn26kOtqlo)
+config_driveid>1
+
+Drive OK?
+
+Found drive "root" of type"business"
+URL: https://mdx-my.sharxxxxsoft_com/Documents
+
+y) Yes (default)
+n) No
+y/n>1
+This value must be one of the following characters: y, n.
+y/n> y
+
+Configuration complete.
+Options:
+- type: onedrive
+- client_id: 6c1a285d-4fexxxxxx4109a6e0
+- client_secret: WVQ8Q~UAksxxxxxxqMTDWnJtWbqE
+- token: {"access_token":"","expiry":"2023-03-22T12:40:42.6524031+08:00"}
+- drive_id: b!v83P4RgYYEWmnzOjKs3kRxxxxxxsMPzXJQ-RZfn26kOtqlo
+- drive_type: business
+Keep this "OneMusic" remote?
+y) Yes this is OK (default)
+e) Edit this remote
+d) Delete this remote
+y/e/d> y
+
+Current remotes:
+
+Name Type
+========
+OneMusic onedrive
+
+e) Edit existing remote
+n) New remote
+d) Delete remote
+r) Rename remote
+c) Copy remote
+s) Set configuration password
+q) Quit config
+e/n/d/r/c/s/q>
# 提供 cargo generate 子命令
+cargo install cargo-generate
+
+# A tool that forwards linker arguments to the actual linker that is also given as an argument to
+# ldproxy
+cargo install ldproxy
+
+# A tool that simplifies installing and maintaining the components required to develop Rust
+# applications for the Xtensa and RISC-V architectures.
+cargo install espup
+
+brew install libuv # espflash 依赖它
+
+cargo install espflash
+cargo install cargo-espflash # 可选,后续可以直接使用命令 cargo espflash
espup 同时安装和维护 Xtensa and RISC-V architectures 两种 CPU 架构工具链
zj@a:~/docs$ espflash board-info
+[2024-05-10T13:38:06Z INFO ] Detected 2 serial ports
+[2024-05-10T13:38:06Z INFO ] Ports which match a known common dev board are highlighted
+[2024-05-10T13:38:06Z INFO ] Please select a port
+[2024-05-10T13:38:19Z INFO ] Serial port: '/dev/cu.usbmodem2101'
+[2024-05-10T13:38:19Z INFO ] Connecting...
+[2024-05-10T13:38:21Z INFO ] Using flash stub
+Chip type: esp32s3 (revision v0.2)
+Crystal frequency: 40 MHz
+Flash size: 16MB
+Features: WiFi, BLE
+MAC address: 3c:84:27:04:fe:18
+
+# 使用 probe-rs 工具
+zj@a:~/docs$ probe-rs info
+Probing target via JTAG
+
+ WARN probe_rs::probe::espusbjtag: More than one TAP detected, defaulting to tap0
+No DAP interface was found on the connected probe. ARM-specific information cannot be printed.
+Error while reading RISC-V info: Connected target is not a RISC-V device.
+Xtensa Chip:
+ IDCODE: 00120034e5
+ Version: 1
+ Part: 8195
+ Manufacturer: 626 (Tensilica)
+
+Probing target via SWD
+
+Error identifying target using protocol SWD: Probe does not support SWD
+
+zj@a:~/docs$
probe-rs 最重要的是 run 命令,可以集成到 cargo run 中:.cargo/config.toml
Copy
[target.<architecture-triple>]
+runner = 'probe-rs run --chip esp32s3'
Now you can execute cargo run in your project as you would for any native binaries and you will receive RTT and defmt logs in that very same console as if you wrote to standard out.
It makes it possible to flash, start and print logs from your target with a simple cargo run.
probe-rs attach works exactly like probe-rs run except that it does not issue a reset and does not flash the target on connecting to preserve the currently running state. This is great for inspecting a target - where you might not even have knowledge of the firmware - without altering its state.
The draw method for text drawables returns the position of the next character. This can be used to combine text with different character styles on a single line of text.
Copy
// https://docs.rs/embedded-graphics/latest/embedded_graphics/text/index.html#examples
+// Create a small and a large character style.
+let small_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
+let large_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
+
+// Draw the first text at (20, 30) using the small character style.
+let next = Text::new("small ", Point::new(20, 30), small_style).draw(&mut display)?;
+
+// Draw the second text after the first text using the large character style.
+let next = Text::new("large", next, large_style).draw(&mut display)?;
Images are the basic object to display images from flash (as arrays) or from files. Images can display symbols (LV_SYMBOL_…) too.
Using the Image decoder interface custom image formats can be supported as well.
Image source:To provide maximum flexibility, the source of the image can be:
a variable in code (a C array with the pixels).
a file stored externally (e.g. on an SD card).
a text with Symbols.
To set the source of an image, use lv_img_set_src(img, src).
To generate a pixel array from a PNG, JPG or BMP image, use the Online image converter tool and set the converted image with its pointer: lv_img_set_src(img1, &converted_img_var); To make the variable visible in the C file, you need to declare it with LV_IMG_DECLARE(converted_img_var).
To use external files, you also need to convert the image files using the online converter tool but now you should select the binary output format. You also need to use LVGL’s file system module and register a driver with some functions for the basic file operation. Go to the File system to learn more. To set an image sourced from a file, use lv_img_set_src(img, “S:folder1/my_img.bin”).
You can also set a symbol similarly to Labels. In this case, the image will be rendered as text according to the font specified in the style. It enables to use of light-weight monochrome “letters” instead of real images. You can set symbol like lv_img_set_src(img1, LV_SYMBOL_OK).
You would basically need to convert the pixel into SharedPixelBuffer and use the slint::Image constructor to convert it to an image. Then you can use a property to pass that image to the slint view.
Copy
export component View {
+ in property <image> img <=> i.source;
+ i := Image { }
+}
For LCDs with SPI/I80 interfaces, the screen refresh rate is determined by the LCD driver IC and can typically be set by sending specific commands, such as the ST7789 command FRCTRL2 (C6h).
For LCDs with RGB interfaces, the screen refresh rate is determined by the main controller and is equivalent to the interface frame rate.
// https://github.com/espressif/esp32-camera/blob/master/driver/include/esp_camera.h#L115
+
+/**
+ * @brief Configuration structure for camera initialization
+ */
+typedef struct {
+ int pin_pwdn; /*!< GPIO pin for camera power down line */
+ int pin_reset; /*!< GPIO pin for camera reset line */
+ int pin_xclk; /*!< GPIO pin for camera XCLK line */
+ union {
+ int pin_sccb_sda; /*!< GPIO pin for camera SDA line */
+ int pin_sscb_sda __attribute__((deprecated("please use pin_sccb_sda instead"))); /*!< GPIO pin for camera SDA line (legacy name) */
+ };
+ union {
+ int pin_sccb_scl; /*!< GPIO pin for camera SCL line */
+ int pin_sscb_scl __attribute__((deprecated("please use pin_sccb_scl instead"))); /*!< GPIO pin for camera SCL line (legacy name) */
+ };
+ int pin_d7; /*!< GPIO pin for camera D7 line */
+ int pin_d6; /*!< GPIO pin for camera D6 line */
+ int pin_d5; /*!< GPIO pin for camera D5 line */
+ int pin_d4; /*!< GPIO pin for camera D4 line */
+ int pin_d3; /*!< GPIO pin for camera D3 line */
+ int pin_d2; /*!< GPIO pin for camera D2 line */
+ int pin_d1; /*!< GPIO pin for camera D1 line */
+ int pin_d0; /*!< GPIO pin for camera D0 line */
+ int pin_vsync; /*!< GPIO pin for camera VSYNC line */
+ int pin_href; /*!< GPIO pin for camera HREF line */
+ int pin_pclk; /*!< GPIO pin for camera PCLK line */
+
+ int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */
+
+ ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */
+ ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */
+
+ pixformat_t pixel_format; /*!< Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG */
+ framesize_t frame_size; /*!< Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA */
+
+ int jpeg_quality; /*!< Quality of JPEG output. 0-63 lower means higher quality */
+ size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
+ camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
+ camera_grab_mode_t grab_mode; /*!< When buffers should be filled */
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+ camera_conv_mode_t conv_mode; /*!< RGB<->YUV Conversion mode */
+#endif
+
+ int sccb_i2c_port; /*!< If pin_sccb_sda is -1, use the already configured I2C bus by number */
+} camera_config_t;
+
+
+/**
+ * @brief Camera frame buffer location
+ */
+typedef enum {
+ CAMERA_FB_IN_PSRAM, /*!< Frame buffer is placed in external PSRAM */
+ CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
+} camera_fb_location_t;
+
+
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+/**
+ * @brief Camera RGB\YUV conversion mode
+ */
+typedef enum {
+ CONV_DISABLE,
+ RGB565_TO_YUV422,
+
+ YUV422_TO_RGB565,
+ YUV422_TO_YUV420
+} camera_conv_mode_t;
+#endif
对于 RAW RGB 和 YUV 像素,一般使用 8-10 位并行数字接口(DVP)输出, 该接口和 RGB LCD 接口类型一致 ,所以 LCD 和 Camera 在 esp-hal 仓库中属于同一个 module:lcd_camera;
// https://github.com/esp-rs/esp-hal/blob/main/esp-hal/src/lcd_cam/cam.rs
+
+//! ## Examples
+//!/Following code shows how to receive some bytes from an 8 bit DVP stream in
+//! master mode.
+//!
+//! ```no_run
+//! let mclk_pin = io.pins.gpio15;
+//! let vsync_pin = io.pins.gpio6;
+//! let href_pin = io.pins.gpio7;
+//! let pclk_pin = io.pins.gpio13;
+//! let data_pins = RxEightBits::new(
+//! io.pins.gpio11,
+//! io.pins.gpio9,
+//! io.pins.gpio8,
+//! io.pins.gpio10,
+//! io.pins.gpio12,
+//! io.pins.gpio18,
+//! io.pins.gpio17,
+//! io.pins.gpio16,
+//! );
+//!
+//! let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
+//! let mut camera = Camera::new(lcd_cam.cam, channel.rx, data_pins, 20u32.MHz(), &clocks)
+//! .with_master_clock(mclk_pin) // Remove this for slave mode.
+//! .with_ctrl_pins(vsync_pin, href_pin, pclk_pin);
+//! ```
+
+/// Generation of GDMA SUC EOF
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum EofMode {
+ /// Generate GDMA SUC EOF by data byte length
+ ByteLen,
+ /// Generate GDMA SUC EOF by the external enable signal
+ EnableSignal,
+}
+
+ /// Perform a DMA read.
+ ///
+ /// This will return a [RxDmaTransfer]
+ ///
+ /// The maximum amount of data is 32736 bytes when using [EofMode::ByteLen].
+ ///
+ /// It's only limited by the size of the DMA buffer when using
+ /// [EofMode::EnableSignal].
esp_err_t camera_capture(){
+ //acquire a frame
+ camera_fb_t * fb = esp_camera_fb_get();
+ if (!fb) {
+ ESP_LOGE(TAG, "Camera Capture Failed");
+ return ESP_FAIL;
+ }
+ //replace this with your own function
+ process_image(fb->width, fb->height, fb->format, fb->buf, fb->len);
+
+ //return the frame buffer back to the driver for reuse
+ esp_camera_fb_return(fb);
+ return ESP_OK;
+}
开发调试:My initial idea for this (which is the example I have in my project) was to dump the JPEG from the camera as HEX onto the console and use xxd -r -p uart.txt image.jpg to convert it to JPEG file. Somewhat tedious but it works haha.
将 esp-camera 驱动的摄像头进行 RTSP+RTP(Over UDP) 进行流式输出的例子:camera-streamer:Example for ESP32 TimerCam rebuilt using ESPP to stream video over the network. It uses RTSP + RTP (over UDP) to perform real-time streaming of the camera data over the network to multiple clients.
Flashing this software on a ESP32CAM module will make it a RTSP streaming camera server, a HTTP Motion JPEG streamer and a HTTP image server.
RTSP The RTSP protocol is an industry standard and allows many CCTV systems and applications (like for example VLC) to connect directly to the ESP32CAM camera stream. It is also possible to stream directly to a server using ffmpeg. This makes the module a camera server allowing recording and the stream can be stored on a disk and replayed later. The URL is rtsp://<ip address>:554/mjpeg/1
HTTP Motion JPEG The HTTP JPEG streamer makes it possible to watch the camera stream directly in your browser. The URL is http://<ip address>/stream
HTTP image The HTTP Image returns an HTTP JPEG image of the camera. The URL is http://<ip address>/snapshot
RTSP client that receives mjpeg frames split into RTP packets, turns them back into JPEG images, and pushes them into a queue.
Display task, which pulls image data from the queue, decodes the jpeg, and displays it on the screen.
其他 Streaming JPEG 或任何文件的方式:
Copy
# 使用 Gstreamer 将 webcam 发送的串行 JPEG image 捕获为 mp4 视频输出:
+$ gst-launch-1.0 v4l2src ! jpegdec ! xvimagesink
+# Capture a single image and save it in JPEG format.
+$ gst-launch v4l2src num-buffers=1 ! jpegenc ! filesink location=/tmp/test.jpg
+# Stream video from a webcam.
+$ gst-launch v4l2src ! xvimagesink
+# if camera supports MJPG
+$ gst-launch-1.0 v4l2src num-buffers=1 ! image/jpeg,framerate=5/1,width=1280,height=960 ! jpegparse
+! filesink location=/tmp/test2.jpg
+# raw image
+$ gst-launch-1.0 v4l2src num-buffers=1 ! videoconvert ! 'video/x-raw,width=1280,height=960,format=RGBx' ! filesink location=image.raw
关于 Streaming 的解释: Playing media straight from the Internet without storing it locally is known as Streaming. We have been doing it throughout the tutorials whenever we used a URI starting with http://. This tutorial shows a couple of additional points to keep in mind when streaming. In particular:
information Embedding multiple streams inside a single file is called “multiplexing” or “muxing”, and such file is then known as a “container”. Common container formats are Matroska (.mkv), Quicktime (.qt, .mov, .mp4), Ogg (.ogg) or Webm (.webm).
Retrieving the individual streams from within the container is called “demultiplexing” or “demuxing”.
The Real-Time Streaming Protocol (RTSP) is an application-level network protocol designed for multiplexing and packetizing multimedia transport streams (such as interactive media, video and audio) over a suitable transport protocol. RTSP is used in entertainment and communications systems to control streaming media servers. The protocol is used for establishing and controlling media sessions between endpoints. Clients of media servers issue commands such as play, record and pause, to facilitate real-time control of the media streaming from the server to a client (video on demand) or from a client to the server (voice recording).
Like HTTP, RTSP uses TCP to maintain an end-to-end connection and, while most RTSP control messages are sent by the client to the server, some commands travel in the other direction (i.e. from server to client).
PLAY:
C->S: PLAY rtsp://example.com/media.mp4 RTSP/1.0 CSeq: 4 Range: npt=5-20 Session: 12345678
S->C: RTSP/1.0 200 OK CSeq: 4 Session: 12345678 RTP-Info: url=rtsp://example.com/media.mp4/streamid=0;seq=9810092;rtptime=3450012
The transmission of streaming data itself is not a task of RTSP. Most RTSP servers use the Real-time Transport Protocol (RTP) in conjunction with Real-time Control Protocol (RTCP) for media stream delivery . However, some vendors implement proprietary transport protocols. The RTSP server software from RealNetworks, for example, also used RealNetworks’ proprietary Real Data Transport (RDT).
RTP VS RTSP
RTSP is a realtime streaming protocol. Meaning, you can stream whatever you want in real time. So you can use it to stream LIVE content (no matter what it is, video, audio, text, presentation…). RTP is a transport protocol which is used to transport media data which is negotiated over RTSP.
You use RTSP to control media transmission over RTP. You use it to setup, play, pause, teardown the stream…
So, if you want your server to just start streaming when the URL is requested, you can implement some sort of RTP-only server. But if you want more control and if you are streaming live video, you must use RTSP, because it transmits SDP and other important decoding data.
总结:
RTSP 提供了丰富的流媒体控制能力;
如果不需要控制,只是 stream,可以只实现 RTP server;
RTSP is widely used in IP camera, running as RTSP server in camera, so that user could play(pull) the RTSP stream from camera. It is a low cost solution, because we don’t need a central media server (think about thousands of camera streams). The arch is bellow:
IP Camera —-RTSP(pull)—> Player (RTSP server) (User Agent)
The RTSP protocol actually contains: A signaling over TCP, at port 554, used to exchange the SDP (also used in WebRTC), about media capabilities. UDP/TCP streams over serval ports, generally two ports, one for RTCP and one for RTP (also used in WebRTC).
Comparing to WebRTC, which is now available in H5:
A signaling over HTTP/WebSocket or exchange by any other protocols, used to exchange the SDP. UDP streams(RTP/RTCP) over one or many ports, generally bind to one port, to make cloud services load balancer happy.
In protocol view, RTSP and WebRTC are similar, but the use scenario is very different, because it’s off the topic, let’s grossly simplified, WebRTC is design for web conference, while RTSP is used for IP camera systems.
So it’s clear both RTSP and WebRTC are solution and protocol, used in different scenario. While RTP is transport protocol, also it can be used in live streaming by WebRTC.
服务端:
Darwin Streaming Server: Open-sourced version of QuickTime Streaming Server maintained by Apple.
GStreamer based RTSP Server and client.
Many CCTV / Security cameras, often called IP cameras, support RTSP streaming too, especially those with ONVIF (the Open Network Video Interface Forum) profiles G, S, T.
RTSP(Real Time Streaming Protocol),RFC2326,实时流传输协议,是TCP/IP协议体系中的一个应用层协议,由哥伦比亚大学、网景和RealNetworks公司提交的IETF RFC标准。该协议定义了一对多应用程序如何有效地通过IP网络传送多媒体数据。RTSP在体系结构上位于RTP和RTCP之上,它使用TCP或UDP完成数据传输。
Real-time Transport Protocol或简写RTP,它是由IETF的多媒体传输工作小组1996年在RFC 1889中公布的。RTP协议详细说明了在互联网上传递音频和视频的标准数据包格式。它是创建在UDP协议上的。
Real-time Transport Control Protocol或RTP Control Protocol或简写RTCP)是实时传输协议(RTP)的一个姐妹协议。RTCP由RFC 3550定义(取代作废的RFC 1889)。RTP 使用一个 偶数 UDP port ;而RTCP 则使用 RTP 的下一个 port,也就是一个奇数 port。RTCP与RTP联合工作,RTP实施实际数据的传输,RTCP则负责将控制包送至电话中的每个人。其主要功能是就RTP正在提供的服务质量做出反馈。
Motion JPEG (M-JPEG or MJPEG) is a video compression format in which each video frame or interlaced field of a digital video sequence is compressed separately as a JPEG image.
Originally developed for multimedia PC applications, Motion JPEG enjoys broad client support: most major web browsers and players provide native support, and plug-ins are available for the rest. Software and devices using the M-JPEG standard include web browsers, media players, game consoles, digital cameras, IP cameras, webcams, streaming servers, video cameras, and non-linear video editors
M-JPEG is now used by video-capture devices such as digital cameras, IP cameras, and webcams, as well as by non-linear video editing systems. It is natively supported by the QuickTime Player, the PlayStation console, and web browsers such as Safari, Google Chrome, Mozilla Firefox and Microsoft Edge.
Video streaming
HTTP streaming separates each image into individual HTTP replies on a specified marker. HTTP streaming creates packets of a sequence of JPEG images that can be received by clients such as QuickTime or VLC. In response to a GET request for a MJPEG file or stream, the server streams the sequence of JPEG frames over HTTP .
A special mime-type content type multipart/x-mixed-replace;boundary=<boundary-name> informs the client to expect several parts (frames) as an answer delimited by . This boundary name is expressly disclosed within the MIME-type declaration itself. The TCP connection is not closed as long as the client wants to receive new frames and the server wants to provide new frames.
Two basic implementations of a M-JPEG streaming server are cambozola and MJPG-Streamer. The more robust ffmpeg-server also provides M-JPEG streaming support.
Native web browser support includes: Safari, Google Chrome, Microsoft Edge[8] and Firefox.[9] Other browsers, such as Internet Explorer can display M-JPEG streams with the help of external plugins. Cambozola is an applet that can show M-JPEG streams in Java-enabled browsers. M-JPEG is also natively supported by PlayStation and QuickTime. Most commonly, M-JPEG is used in IP based security cameras.[10]
gst-launch-1.0 v4l2src ! xvimagesink: This pipeline shows the video captured from /dev/video0 tv card and for webcams.
gst-launch-1.0 v4l2src ! jpegdec ! xvimagesink :This pipeline shows the video captured from a webcam that delivers jpeg images.
Video4Linux (V4L for short) is a collection of device drivers and an API for supporting realtime video capture on Linux systems.[1] It supports many USB webcams, TV tuners, and related devices, standardizing their output, so programmers can easily add video support to their applications.
Video4Linux is responsible for creating V4L2 device nodes aka a device file (/dev/videoX, /dev/vbiX and /dev/radioX ) and tracking data from these nodes. The device node creation is handled by V4L device drivers using the video_device struct (v4l2-dev.h) and it can either be allocated dynamically or embedded in another larger struct.
Video4Linux was named after Video for Windows (which is sometimes abbreviated “V4W”), but is not technically related to it.[2][3]
PCM:脉冲编码调制(英语:Pulse-code modulation,缩写:PCM)是一种模拟信号的数字化方法。 A PCM stream has two basic properties that determine the stream’s fidelity to the original analog signal:
the sampling rate, which is the number of times per second that samples are taken;
and the bit depth, which determines the number of possible digital values that can be used to represent each sample.
The compact disc (CD) brought PCM to consumer audio applications with its introduction in 1982. The CD uses a 44,100 Hz sampling frequency and 16-bit resolution and stores up to 80 minutes of stereo audio per disc. stereo audio 是通过 two-channel 提供的。
The audio contained in a CD-DA consists of two-channel signed 16-bit LPCM sampled at 44,100 Hz and written as a little-endian interleaved stream with left channel coming first
There are three major groups of audio file formats :
Uncompressed audio formats, such as WAV, AIFF, AU or raw header-less PCM; Note wav can also use compression as well.
Formats with lossless compression, such as FLAC, Monkey's Audio (filename extension .ape), WavPack (filename extension .wv), TTA, ATRAC Advanced Lossless, ALAC (filename extension .m4a, Apple Lossless), MPEG-4 SLS, MPEG-4 ALS, MPEG-4 DST, Windows Media Audio Lossless (WMA Lossless), and Shorten (SHN).
Formats with lossy compression, such as Opus, MP3, Vorbis, Musepack, AAC, ATRAC and Windows Media Audio Lossy (WMA lossy).
.m4a An audio-only MPEG-4 file, used by Apple for unprotected music downloaded from their iTunes Music Store. Audio within the m4a file is typically encoded with AAC, although lossless ALAC may also be used.
可以解码播放: mp3, m4a and wav files from SD card via I2S,HELIX-mp3 and -aac decoder is included. There is also an OPUS decoder for Fullband, n VORBIS decoder( .ogg 格式) and a FLAC decoder.
Wm8960 is a low power, high quality stereo CODEC, that provides two interface types: voice input and output. The communication between ESP32 and WM8960 is I2S.
一般 I2S 接口的数字音频功放芯片 codec chip,除了可以播放 PCM 编码格式的数字音频信号外,还提供控制(静音、音量大小等)和 MIC 输入功能,如 ES8374
I (397) PLAY_FLASH_MP3_CONTROL: [ 1 ] Start audio codec chip
+I (427) PLAY_FLASH_MP3_CONTROL: [ 2 ] Create audio pipeline, add all elements to pipeline, and subscribe pipeline event
+I (427) PLAY_FLASH_MP3_CONTROL: [2.1] Create mp3 decoder to decode mp3 file and set custom read callback
+I (437) PLAY_FLASH_MP3_CONTROL: [2.2] Create i2s stream to write data to codec chip
+I (467) PLAY_FLASH_MP3_CONTROL: [2.3] Register all elements to audio pipeline
+I (467) PLAY_FLASH_MP3_CONTROL: [2.4] Link it together [mp3_music_read_cb]-->mp3_decoder-->i2s_stream-->[codec_chip]
+I (477) PLAY_FLASH_MP3_CONTROL: [ 3 ] Set up event listener
+I (477) PLAY_FLASH_MP3_CONTROL: [3.1] Listening event from all elements of pipeline
+I (487) PLAY_FLASH_MP3_CONTROL: [ 4 ] Start audio_pipeline
+I (507) PLAY_FLASH_MP3_CONTROL: [ * ] Receive music info from mp3 decoder, sample_rates=44100, bits=16, ch=2
+I (7277) PLAY_FLASH_MP3_CONTROL: [ 5 ] Stop audio_pipeline
The ESP32-S3’s I2S interface is set up to handle the audio data using Direct Memory Access (DMA) buffers. DMA allows for efficient data transfer without involving the main processor, offloading the task to a dedicated DMA controller. By configuring the DMA buffer in I2S, the captured audio samples can be stored and transmitted seamlessly.
zj@a:~/codes/esp32/$ cargo generate esp-rs/esp-idf-template cargo
+⚠️ Favorite `esp-rs/esp-idf-template` not found in config, using it as a git repository: https://github.com/esp-rs/esp-idf-template.git
+🔧 project-name: myesp ...
+🔧 Generating template ...
+✔ 🤷 Which MCU to target? · esp32s3
+✔ 🤷 Configure advanced template options? · true
+✔ 🤷 Enable STD support? · true
+✔ 🤷 Configure project to use Dev Containers (VS Code and GitHub Codespaces)? · false
+✔ 🤷 Configure project to support Wokwi simulation with Wokwi VS Code extension? · false
+✔ 🤷 Add CI files for GitHub Action? · false
+✔ 🤷 ESP-IDF version (master = UNSTABLE) · v5.1
+🔧 Moving generated files into: `/Users/zhangjun/codes/esp32/esp-demo2/myesp`...
+🔧 Initializing a fresh Git repository
+✨ Done! New project created /Users/zhangjun/codes/esp32/esp-demo2/myesp
+
+zj@a:~/codes/esp32/$ cd myesp
+
+zj@a:~/code/esp32/std/myespv4$ cat src/main.rs
+fn main(){
+ // It is necessary to call this function once. Otherwise some patches to the runtime
+ // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
+ esp_idf_svc::sys::link_patches();
+
+ // Bind the log crate to the ESP Logging facilities
+ esp_idf_svc::log::EspLogger::initialize_default();
+
+ log::info!("Hello, world!");
+}
zj@a:~/codes/esp32/myesp$ cat .cargo/config.toml
+[build]
+target = "xtensa-esp32s3-espidf" # 要构建的 target,这里使用链接 esp-idf 的 target
+
+[target.xtensa-esp32s3-espidf] # target 对应的配置
+linker = "ldproxy" # 位于 ~/.cargo/bin/
+# runner = "espflash --monitor" # Select this runner for espflash v1.x.x
+runner = "espflash flash --monitor" # Select this runner for espflash v2.x.x
+rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
+
+[unstable]
+build-std = ["std", "panic_abort"] # 构建和使用 std(对于 no_std 是 core)
+
+[env] # 被 embuild 使用的环境变量
+MCU="esp32s3"
+# Note: this variable is not used by the pio builder (`cargo build --features pio`)
+ESP_IDF_VERSION = "v5.2.1"
+# 使用全局 ~/.espressif/ 工具链,默认是 by 项目 workspace 的。
+ESP_IDF_TOOLS_INSTALL_DIR = "global"
按需修改 esp-idf 的配置参数文件:sdkconfig.defaults
zj@a:~/docs$ cat ~/code/esp32/std/myespv2/sdkconfig.defaults
+# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
+CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000
+
+# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
+# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
+#CONFIG_FREERTOS_HZ=1000
+
+# Workaround for https://github.com/espressif/esp-idf/issues/7631
+#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
+#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n
[package.metadata.esp-idf-sys.extra_components.0.remote_component]
+# The name of the remote component. Corresponds to a key in the dependencies of
+# `idf_component.yml`.
+name = "component_name"
+# The version of the remote component. Corresponds to the `version` field of the
+# `idf_component.yml`.
+version = "1.2"
+# A git url that contains this remote component. Corresponds to the `git`
+# field of the `idf_component.yml`.
+#
+# This field is optional.
+git = "https://github.com/espressif/esp32-camera.git"
+
+# A path to the component.
+# Corresponds to the `path` field of the `idf_component.yml`.
+#
+# Note: This should not be used for local components, use
+# `component_dirs` of extra components instead.
+#
+# This field is optional.
+path = "path/to/component"
+
+# A url to a custom component registry. Corresponds to the `service_url`
+# field of the `idf_component.yml`.
+#
+# This field is optional.
+service_url = "https://componentregistry.company.com"
out - the tooling will be installed or used inside esp-idf-sys’s build output directory, and will be deleted when cargo clean is invoked;
global(建议) - the tooling will be installed or used in its standard directory (~/.platformio for PlatformIO, and ~/.espressif for the native ESP-IDF toolset);
custom: - the tooling will be installed or used in the directory specified by . If this directory is a relative location, it is assumed to be relative to the workspace directory;
idf_path, $IDF_PATH (native builder only): A path to a user-provided local clone of the esp-idf, that will be used instead of the one downloaded by the build script.
esp_idf_version, $ESP_IDF_VERSION (native builder only) : The version used for the esp-idf, can be one of the following
mcu, $MCU: The MCU name (i.e. esp32, esp32s2, esp32s3 esp32c3, esp32c2, esp32h2, esp32c5, esp32c6, esp32p4).
esp_idf_components, $ESP_IDF_COMPONENTS (native builder only) : Defaults to all components being built.
[build]
+target = "xtensa-esp32s3-espidf"
+# 使用 sccache 来调用 rustc 编译器, 有利用缓存加快构建速度.
+# 先安装 sccache: cargo install sccache --locked
+rustc-wrapper = "/opt/homebrew/bin/sccache"
+
+[target.xtensa-esp32s3-espidf]
+linker = "ldproxy"
+# runner = "espflash --monitor" # Select this runner for espflash v1.x.x
+runner = "espflash flash --monitor" # Select this runner for espflash v2.x.x
+
+# 以下是使用 rustc 构建 esp-rs/esp-idf-sys 时传递的参数
+# https://github.com/esp-rs/esp-idf-sys/blob/master/BUILD-OPTIONS.md This is a flag for the libc
+# crate that uses 64-bits (instead of 32-bits) for time_t. This must be set for ESP-IDF 5.0 and
+# above and must be unset for lesser versions.
+rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
+# https://github.com/esp-rs/esp-idf-sys/blob/master/BUILD-OPTIONS.md
+# 等效于: -Zbuild-std=std,panic_abort
+# Required for std support. Rust does not provide std libraries for ESP32 targets since they are tier-2/-3.
+[unstable]
+build-std = ["std", "panic_abort"]
+
+# cargo 调用命令时使用的环境变量
+# 参考:https://github.com/esp-rs/esp-idf-sys/blob/master/BUILD-OPTIONS.md#esp-idf-configuration
+[env]
+MCU="esp32s3"
+# Note: this variable is not used by the pio builder (`cargo build --features pio`)
+# 最新 esp-idf 版本:https://github.com/espressif/esp-idf/releases
+ESP_IDF_VERSION = "v5.2.q"
+
+# 使用全局 ~/.espressif/ 工具链,默认是 by 项目 workspace 的。
+ESP_IDF_TOOLS_INSTALL_DIR = "global"
2024.03.08 发布的 0.16.0 版本开始,将以前芯片相关的 esp32-hal, esp32c3-hal 等 crate 合并为一个单独的 esp-hal crate。使用时在 features 中指定 CPU 类型,如: esp-hal = { version = "0.17.0", features = [ "esp32s3" ] }
说明:esp-pacs 和 esp-hal 是 no_std 应用开发的基础。
esp-wifi : Wi-Fi, BLE and ESP-NOW support
esp-alloc : Simple heap allocator. This allocator is built on top of the phil-opp/linked-list-allocator crate, which does most of the heavy lifting. While it’s often advisable to avoid allocations in such limited environments, there are scenarios when an allocator is still required and/or desirable.
esp-println : print!, println!. esp-println allows for printing over UART, USB Serial JTAG, or RTT without any required dependencies. 实现 log crate 的 trait,用于打印日志到串口;
esp-backtrace : Exception and panic handlers. As the name implies, esp-backtrace enables backtraces in no_std applications. It additionally provides a panic handler and exception handler , both behind features. This crate makes debugging issues much easier.
创建一个 no_std Bare-Metal 项目, 自定义是否使用 WiFi/Bluetooth/ESP-NOW via the esp-wifi crate;
zj@a:~/code/esp32$ cargo generate esp-rs/esp-template
+
+# no_std 应用需要声明 no_std 和 no_main 宏,这样编译器才不会导入 std 库。
+# #![no_std] 告诉编译器不导入和链接 libstd 库。
+# #![no_main] 告诉编译器不使用标准的 main 接口,而是使用 esp 提供的 main 入口。
+zj@a:~/code/esp32/non_std$ cat myesp-nonstd/src/main.rs
+#![no_std]
+#![no_main]
+
+# in a bare-metal environment, we need a panic handler that runs if a panic occurs in code There are
+# a few different crates you can use (e.g panic-halt) but esp-backtrace provides an implementation
+# that prints _the address of a backtrace_ - together with espflash these addresses can get decoded
+# into source code locations
+use esp_backtrace as _;
+
+use esp_hal::{clock::ClockControl, peripherals::Peripherals, prelude::*, delay::Delay};
+
+#[entry]
+fn main() -> ! {
+ let peripherals = Peripherals::take();
+ let system = peripherals.SYSTEM.split();
+
+ let clocks = ClockControl::max(system.clock_control).freeze();
+ let delay = Delay::new(&clocks);
+
+ esp_println::logger::init_logger_from_env();
+
+ loop {
+ log::info!("Hello world!");
+ delay.delay(500.millis());
+ }
+}
按需配置 Cargo.toml:
zj@a:~/code/esp32/non_std$ cat myesp-nonstd/Cargo.toml
+[package]
+name = "myesp-nonstd"
+version = "0.1.0"
+authors = ["alizj"]
+edition = "2021"
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+# esp-has 是 no_std 类型 crate, features 中指定了 CPU 类型
+esp-hal = { version = "0.17.0", features = [ "esp32s3" ] }
+
+# bare-metal 环境下,当程序 panic 时打印调用栈
+esp-backtrace = { version = "0.11.0", features = [
+ "esp32s3",
+ "exception-handler",
+ "panic-handler",
+ "println",
+] }
+
+# esp-println 启用 log feature 后,为 log 提供具体的实现
+esp-println = { version = "0.9.0", features = ["esp32s3", "log"] }
+
+# 向终端打印日志。esp_println 提供了 log 的具体实现
+log = { version = "0.4.20" }
+
+[profile.dev]
+# Rust debug is too slow. For debug builds always builds with some optimization
+opt-level = "s" # optimize for binary size
+
+# dev profile 的 debug 参数默认为 2,表示 full debug info,
+# release profile 的 debug 参数默认为 0,表示关闭 debug info;
+
+[profile.release]
+codegen-units = 1 # LLVM can perform better optimizations using a single thread
+debug = 2 # 2/full/true:full debug info, 虽然二进制包含 debuginfo,但是烧写时会被去掉,所以不会增加 flash app 体积
+debug-assertions = false
+incremental = false
+lto = 'fat'
+opt-level = 's'
+overflow-checks = false
对于 ESP32 no_std 应用来说, esp-println, esp-backtrace and espflash/cargo-espflash provide mechanisms to use defmt :
espflash has support for different logging formats, one of them being defmt.
espflash requires framming bytes as when using defmt it also needs to print non-defmt messages, like the bootloader prints. It’s important to note that other defmt-enabled tools like probe-rs won’t be able to parse these messages due to the extra framing bytes. Uses rzcobs encoding
esp-println has a defmt-espflash feature, which adds framming bytes so espflash knows that is a defmt message.
esp-backtrace has a defmt feature that uses defmt logging to print panic and exception handler messages.
在代码里使用 defmt::println!() 等宏来打印日志。
If you want to use any of the logging macros like info, debug
zj@a:~/code/esp32/mycmake/components/rust-mycmake$ cat CMakeLists.txt
+# If this component depends on other components - be it ESP-IDF or project-specific ones - enumerate those in the double-quotes below, separated by spaces
+# Note that pthread should always be there, or else STD will not work
+set(RUST_DEPS "pthread" "driver" "vfs")
+# Here's a non-minimal, reasonable set of ESP-IDF components that one might want enabled for Rust:
+#set(RUST_DEPS "pthread" "esp_http_client" "esp_http_server" "espcoredump" "app_update" "esp_serial_slave_link" "nvs_flash" "spi_flash" "esp_adc_cal" "mqtt")
+
+idf_component_register(
+ SRCS "placeholder.c"
+ INCLUDE_DIRS ""
+ PRIV_REQUIRES "${RUST_DEPS}"
+)
+
+if(CONFIG_IDF_TARGET_ARCH_RISCV)
+ if (CONFIG_IDF_TARGET_ESP32C6 OR CONFIG_IDF_TARGET_ESP32C5 OR CONFIG_IDF_TARGET_ESP32H2)
+ set(RUST_TARGET "riscv32imac-esp-espidf")
+ else ()
+ set(RUST_TARGET "riscv32imc-esp-espidf")
+ endif()
+elseif(CONFIG_IDF_TARGET_ESP32)
+ set(RUST_TARGET "xtensa-esp32-espidf")
+elseif(CONFIG_IDF_TARGET_ESP32S2)
+ set(RUST_TARGET "xtensa-esp32s2-espidf")
+elseif(CONFIG_IDF_TARGET_ESP32S3)
+ set(RUST_TARGET "xtensa-esp32s3-espidf")
+else()
+ message(FATAL_ERROR "Unsupported target ${CONFIG_IDF_TARGET}")
+endif()
+
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(CARGO_BUILD_TYPE "debug")
+ set(CARGO_BUILD_ARG "")
+else()
+ set(CARGO_BUILD_TYPE "release")
+ set(CARGO_BUILD_ARG "--release")
+endif()
+
+
+set(CARGO_BUILD_STD_ARG -Zbuild-std=std,panic_abort)
+
+
+if(IDF_VERSION_MAJOR GREATER "4")
+set(ESP_RUSTFLAGS "--cfg espidf_time64")
+endif()
+
+set(CARGO_PROJECT_DIR "${CMAKE_CURRENT_LIST_DIR}")
+set(CARGO_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+set(CARGO_TARGET_DIR "${CARGO_BUILD_DIR}/target")
+
+set(RUST_INCLUDE_DIR "${CARGO_TARGET_DIR}")
+set(RUST_STATIC_LIBRARY "${CARGO_TARGET_DIR}/${RUST_TARGET}/${CARGO_BUILD_TYPE}/librust_mycmake.a")
+
+# if this component uses CBindGen to generate a C header, uncomment the lines below and adjust the header name accordingly
+#set(RUST_INCLUDE_HEADER "${RUST_INCLUDE_DIR}/RustApi.h")
+#set_source_files_properties("${RUST_INCLUDE_HEADER}" PROPERTIES GENERATED true)
+
+idf_build_get_property(sdkconfig SDKCONFIG)
+idf_build_get_property(idf_path IDF_PATH)
+
+ExternalProject_Add(
+ project_rust_mycmake
+ PREFIX "${CARGO_PROJECT_DIR}"
+ DOWNLOAD_COMMAND ""
+ CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env
+ cargo clean --target ${RUST_TARGET} --target-dir ${CARGO_TARGET_DIR}
+ USES_TERMINAL_BUILD true
+ BUILD_COMMAND ${CMAKE_COMMAND} -E env
+ CARGO_CMAKE_BUILD_INCLUDES=$<TARGET_PROPERTY:${COMPONENT_LIB},INCLUDE_DIRECTORIES>
+ CARGO_CMAKE_BUILD_LINK_LIBRARIES=$<TARGET_PROPERTY:${COMPONENT_LIB},LINK_LIBRARIES>
+ CARGO_CMAKE_BUILD_SDKCONFIG=${sdkconfig}
+ CARGO_CMAKE_BUILD_ESP_IDF=${idf_path}
+ CARGO_CMAKE_BUILD_COMPILER=${CMAKE_C_COMPILER}
+ RUSTFLAGS=${ESP_RUSTFLAGS}
+ MCU=${CONFIG_IDF_TARGET}
+ cargo build --target ${RUST_TARGET} --target-dir ${CARGO_TARGET_DIR} ${CARGO_BUILD_ARG} ${CARGO_BUILD_STD_ARG}
+ INSTALL_COMMAND ""
+ BUILD_ALWAYS TRUE
+ TMP_DIR "${CARGO_BUILD_DIR}/tmp"
+ STAMP_DIR "${CARGO_BUILD_DIR}/stamp"
+ DOWNLOAD_DIR "${CARGO_BUILD_DIR}"
+ SOURCE_DIR "${CARGO_PROJECT_DIR}"
+ BINARY_DIR "${CARGO_PROJECT_DIR}"
+ INSTALL_DIR "${CARGO_BUILD_DIR}"
+ BUILD_BYPRODUCTS
+ "${RUST_INCLUDE_HEADER}"
+ "${RUST_STATIC_LIBRARY}"
+)
+
+add_prebuilt_library(library_rust_mycmake "${RUST_STATIC_LIBRARY}" PRIV_REQUIRES "${RUST_DEPS}")
+add_dependencies(library_rust_mycmake project_rust_mycmake)
+
+target_include_directories(${COMPONENT_LIB} PUBLIC "${RUST_INCLUDE_DIR}")
+target_link_libraries(${COMPONENT_LIB} PRIVATE library_rust_mycmake)
配置了 crate-type 为 staticlib,故会被编译为 esp-idf 可以链接的 C 库
Copy
zj@a:~/code/esp32/mycmake/components/rust-mycmake$ cat Cargo.toml
+[package]
+name = "rust-mycmake"
+version = "0.1.0"
+authors = ["alizj"]
+edition = "2021"
+resolver = "2"
+rust-version = "1.71"
+
+[lib]
+crate-type = ["staticlib"]
+
+[profile.release]
+opt-level = "s"
+
+[profile.dev]
+debug = true # Symbols are nice and they don't increase the size on Flash
+opt-level = "z"
+[features]
+default = ["std", "embassy", "esp-idf-svc/native"]
+
+pio = ["esp-idf-svc/pio"]
+std = ["alloc", "esp-idf-svc/std"]
+alloc = ["esp-idf-svc/alloc"]
+nightly = ["esp-idf-svc/nightly"]
+experimental = ["esp-idf-svc/experimental"]
+embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"]
+
+[dependencies]
+log = { version = "0.4", default-features = false }
+esp-idf-svc = { version = "0.48", default-features = false }
+
+[build-dependencies]
+embuild = "0.31.3"
+
+# 编译为 staticlib,可以被 C 库调用的 Rust 代码
+zj@a:~/code/esp32/mycmake/components/rust-mycmake$ cat src/lib.rs
+#[no_mangle]
+extern "C" fn rust_main() -> i32 {
+ // It is necessary to call this function once. Otherwise some patches to the runtime
+ // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
+ esp_idf_svc::sys::link_patches();
+
+ // Bind the log crate to the ESP Logging facilities
+ esp_idf_svc::log::EspLogger::initialize_default();
+
+ log::info!("Hello, world!");
+ 42
+}
+
+zj@a:~/code/esp32/mycmake/components/rust-mycmake$ cat placeholder.c
+/* Hello World Example
+
+ This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+ Unless required by applicable law or agreed to in writing, this
+ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+/* This is an empty source file to force the build of a CMake library that
+ can participate in the CMake dependency graph. This placeholder library
+ will depend on the actual library generated by external Rust build.
+*/
+
+# main.c 中调用 Rust 的 rust_main() 函数代码
+zj@a:~/code/esp32/mycmake$ cat main/main.c
+/* Hello World Example
+
+ This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+ Unless required by applicable law or agreed to in writing, this
+ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <stdio.h>
+
+extern int rust_main(void);
+
+void app_main(void) {
+ printf("Hello world from C!\n");
+
+ int result = rust_main();
+
+ printf("Rust returned code: %d\n", result);
+}
Using managed esp-idf repository: RemoteSdk { repo_url: None, git_ref: Tag("v5.1.2") }
+ Using esp-idf v5.1.2 at '/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2'
+ ERROR: /Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/espidf.constraints.v5.1.txt doesn't exist. Perhaps you've forgotten to run the install scripts. Please check the installation guide for more information.
+ CMake Error at /Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2/tools/cmake/build.cmake:363 (message):
+ Some Python dependencies must be installed. Check above message for
+ details.
+ Call Stack (most recent call first):
+ /Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2/tools/cmake/build.cmake:498 (__build_check_python)
+ /Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2/tools/cmake/project.cmake:547 (idf_build_process)
+ CMakeLists.txt:28 (project)
+
+
+ thread 'main' panicked at /Users/zhangjun/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cmake-0.1.50/src/lib.rs:1098:5:
+
+ command did not execute successfully, got: exit status: 1
+
+ build script failed, must exit now
+ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
解决办法:
Copy
cargo clean && rm -rf .embuild && cargo build
报错: Missing dependencies for SOCKS support.
Copy
来自 https://github.com/ThrowTheSwitch/Unity
+ * branch 7d2bf62b7e6afaf38153041a9d53c21aeeca9a25 -> FETCH_HEAD
+ ERROR: Could not install packages due to an OSError: Missing dependencies for SOCKS support.
+
+ WARNING: There was an error checking the latest version of pip.
+ Error: Command '['/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/python_env/idf5.1_py3.12_env/bin/python3', '-m', 'pip', 'install', '--upgrade', 'pip']' returned non-zero exit status 1.
+ Traceback (most recent call last):
+ File "/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2/tools/idf_tools.py", line 2687, in <module>
+ main(sys.argv[1:])
+ File "/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2/tools/idf_tools.py", line 2679, in main
+ action_func(args)
+ File "/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2/tools/idf_tools.py", line 2098, in action_install_python_env
+ subprocess.check_call([sys.executable, '-m', 'venv',
+ File "/Users/zhangjun/.pyenv/versions/3.12.1/lib/python3.12/subprocess.py", line 413, in check_call
+ raise CalledProcessError(retcode, cmd)
+ subprocess.CalledProcessError: Command '['/Users/zhangjun/.pyenv/versions/3.12.1/bin/python3', '-m', 'venv', '--clear', '--upgrade-deps', '/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/python_env/idf5.1_py3.12_env']' returned non-zero exit status 1.
+ Error: Could not install esp-idf
+
+ Caused by:
+ command 'env -u IDF_PYTHON_ENV_PATH -u MSYSTEM IDF_TOOLS_PATH="/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif" "python3" "/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2/tools/idf_tools.py" "--idf-path" "/Users/zhangjun/codes/esp32/esp-demo/.embuild/espressif/esp-idf/v5.1.2" "--non-interactive" "install-python-env"' exited with non-zero status code 1
+zj@a:~/codes/esp32/esp-demo$
zj@a:~/code/esp32/std$ source ~/esp/esp-idf/v5.2.1/export.sh
+ zj@a:~/code/esp32/std$ source ~/esp/export-esp.sh
+
+ zj@a:~/code/esp32/std$ cargo build
+ warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
+ package: /Users/alizj/code/esp32/std/myesp/Cargo.toml
+ workspace: /Users/alizj/code/esp32/std/Cargo.toml
+ warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
+ package: /Users/alizj/code/esp32/std/myespv2/Cargo.toml
+ workspace: /Users/alizj/code/esp32/std/Cargo.toml
+ warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
+ package: /Users/alizj/code/esp32/std/myespv3/Cargo.toml
+ workspace: /Users/alizj/code/esp32/std/Cargo.toml
+ warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
+ package: /Users/alizj/code/esp32/std/myespv4/Cargo.toml
+ workspace: /Users/alizj/code/esp32/std/Cargo.toml
+ Compiling esp-idf-sys v0.34.1
+ The following warnings were emitted during compilation:
+
+ warning: esp-idf-sys@0.34.1: could not identify the root crate and `ESP_IDF_SYS_ROOT_CRATE` not specified
+
+ error: failed to run custom build command for `esp-idf-sys v0.34.1`
+
+ Caused by:
+ process didn't exit successfully: `/Users/alizj/code/esp32/std/target/debug/build/esp-idf-sys-eac13132720836e4/build-script-build` (exit status: 101)
+ --- stdout
+ cargo:rerun-if-env-changed=ESP_IDF_TOOLS_INSTALL_DIR
+ cargo:rerun-if-env-changed=ESP_IDF_SDKCONFIG
+ cargo:rerun-if-env-changed=ESP_IDF_SDKCONFIG_DEFAULTS
+ cargo:rerun-if-env-changed=MCU
zj@a:~/codes/esp32$ espflash --help
+A command-line tool for flashing Espressif devices
+
+Usage: espflash <COMMAND>
+
+Commands:
+ board-info Print information about a connected target device
+ completions Generate completions for the given shell
+ erase-flash Erase Flash entirely
+ erase-parts Erase specified partitions
+ erase-region Erase specified region
+ flash Flash an application in ELF format to a connected target device
+ monitor Open the serial monitor without flashing the connected target device
+ partition-table Convert partition tables between CSV and binary format
+ save-image Generate a binary application image and save it to a local disk
+ write-bin Write a binary file to a specific address in a target device's flash
+ help Print this message or the help of the given subcommand(s)
+
+Options:
+ -h, --help Print help
+ -V, --version Print version
+zj@a:~/codes/esp32$
defmt : Uses [defmt] logging framework. With logging format, logging strings have framing bytes to indicate that they are defmt messages.
一般是在 no_std 应用中使用,需要和 esp-println crate 联合使用。
Establishing a serial connection with the ESP32-S3 target device could be done using USB-to-UART bridge or USB peripheral supported in ESP32-S3. For the ESP32-S3, the USB peripheral is available, allowing you to flash the binaries without the need for an external USB-to-UART bridge.
If you are flashing for the first time, you need to get the ESP32-S3 into the download mode manually. To do so, press and hold the BOOT button and then press the RESET button once. After that release the BOOT button.
使用 gen_esp32part.py 工具来将 CSV 分区表转换为烧写到 FLASH 的 bin 格式。
Copy
# Erase partition with name 'storage'
+parttool.py --port "/dev/ttyUSB1" erase_partition --partition-name=storage
+
+# Read partition with type 'data' and subtype 'spiffs' and save to file 'spiffs.bin'
+parttool.py --port "/dev/ttyUSB1" read_partition --partition-type=data --partition-subtype=spiffs --output "spiffs.bin"
+
+# Write to partition 'factory' the contents of a file named 'factory.bin'
+parttool.py --port "/dev/ttyUSB1" write_partition --partition-name=factory --input "factory.bin"
+
+# Print the size of default boot partition
+parttool.py --port "/dev/ttyUSB1" get_partition_info --partition-boot-default --info size
7. 回到你在 步骤4 中fork的名为 notablog-starter 仓库,依次点击 Actions → New workflow → set up a workflow yourself
7.1. 使用下方代码覆盖原始文件,注意代码需要修改两处位置:
# This is a basic workflow to help you get started with Actions
+
+ name: Github Pages
+
+ # Controls when the workflow will run
+ on:
+ schedule:
+ # 下一行是说在每天的国际标准时间23点(北京时间早上7点)触发该任务
+ # 你可以随意修改
+ - cron: '0 23 * * *'
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+ # A workflow run is made up of one or more jobs that can run sequentially or in parallel
+ jobs:
+ # This workflow contains a single job called "build"
+ build:
+ # The type of runner that the job will run on
+ runs-on: ubuntu-latest
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ # 拉取代码
+ - name: Checkout
+ uses: actions/checkout@v2
+ # 1、生成静态文件
+ - name: Build
+ run: npm i -g notablog && notablog generate .
+ # 2、部署到 GitHub Pages
+ - name: Deploy
+ uses: JamesIves/github-pages-deploy-action@v4.2.5
+ with:
+ token: ${{ secrets.ACCESS_TOKEN }}
+ repository-name: FizzerYu/FizzerYu.github.io # 修改这里
+ BRANCH: main # 如果你的仓库默认分支是 master 记得修改这里
+ FOLDER: public