返回

PikaPython 笔记

目录

本笔记使用ESP32 S3作为开发板

快速开始

  1. 下载包管理器(https://pikapython.com/doc//%E5%8C%85%E7%AE%A1%E7%90%86%E5%99%A8%E4%B8%8E%E6%A8%A1%E5%9D%97%E7%AE%A1%E7%90%86.html )
  2. 点击会安装依赖

组件cmake配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
file(GLOB_RECURSE MODULE_SOURCES
    "${CMAKE_CURRENT_SOURCE_DIR}/pikascript-lib/*/*.c"
    "${CMAKE_CURRENT_SOURCE_DIR}/pikascript-core/*.c"
    "${CMAKE_CURRENT_SOURCE_DIR}/pikascript-api/*.c"
)

idf_component_register(
    SRCS ${MODULE_SOURCES}
    INCLUDE_DIRS "pikascript-api" "pikascript-core" "pikascript-lib" "pikascript-lib/PikaStdLib"
)

例程

1
2
3
4
5
6
7
8
#include <stdio.h>

#include "pikascript.h"

void app_main(void) {
  printf("test pikapython");
  PikaObj *pikaMain = pikaScriptInit();
}

适配

运行

固件

  1. pikapython目录下编写.py文件,并使用pikapython编译器的进行编译
  2. 编译结果存放于pikascript-api/目录下
  3. 注意:如果涉及多个文件需要在主文件中显式声明文件间的关系,不然编译器不会生成对应的编译产物

字符串

注意:固件中烧录的代码会优先执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void app_main(void) {

  PikaObj *pikaMain = pikaScriptInit();

  char *python_script = "print('Hello from PikaScript!')\n"
                        "a = 10\n"
                        "b = 20\n"
                        "print('a + b =', a + b)\n";

  printf("--- Start PikaScript String Execution ---\n");
  obj_run(pikaMain, python_script);
  printf("--- Execution Finished ---\n");

  int res_a = obj_getInt(pikaMain, "a");
  printf("Value of 'a' read from C: %d\n", res_a);

  obj_deinit(pikaMain);
}

串口

pikaSutio安装地址(实际上是个串口工具就行) 重写__platform_getchar()接口

1
2
3
char __platform_getchar(){
    return getchar();
}

文件系统接口

注意:pikaVM_runSingleFilepikaVM_runFile有区别。区别在于一个不管引用其他文件一个管引用其他文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "pikaScript.h"
#include <stdio.h>
#include <string.h>

#include "PikaVM.h"
#include "esp_littlefs.h"

FILE *__platform_fopen(const char *filename, const char *modes) {
  return fopen(filename, modes);
}

int __platform_fclose(FILE *stream) {
  if (NULL == stream) {
    return -1;
  }
  return fclose(stream);
}

size_t __platform_fwrite(const void *ptr, size_t size, size_t n, FILE *stream) {
  if (NULL == stream || NULL == ptr) {
    return 0;
  }
  return fwrite(ptr, size, n, stream);
}

size_t __platform_fread(void *ptr, size_t size, size_t n, FILE *stream) {
  if (NULL == stream || NULL == ptr) {
    return 0;
  }
  return fread(ptr, size, n, stream);
}

int __platform_fseek(FILE *stream, long offset, int whence) {
  return fseek(stream, offset, whence);
}

long __platform_ftell(FILE *stream) { return ftell(stream); }

void vfs_init() {
  esp_vfs_littlefs_conf_t conf = {.base_path = "/littlefs",
                                  .partition_label = "storage",
                                  .format_if_mount_failed = true,
                                  .dont_mount = false};
  esp_vfs_littlefs_register(&conf);
}

void app_main(void) {
  vfs_init();
  PikaObj *PikaMain = pikaScriptInit();
  obj_run(PikaMain, "print(\"helloworld\")");
  pikaVM_runSingleFile(PikaMain, "/littlefs/test.py");
}

有关 pikaVM_runFile

因为需要将编译文件放在/pikascript-api目录,因此需要首先手动创建。
第二种方式就是直接使用读取并使用字符串的方式运行,这里不做过多介绍。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <sys/stat.h>

void vfs_init() {
  esp_vfs_littlefs_conf_t conf = {.base_path = "/littlefs",
                                  .partition_label = "storage",
                                  .format_if_mount_failed = true,
                                  .dont_mount = false};
  esp_vfs_littlefs_register(&conf);

  struct stat st = {0};
  if (stat("/littlefs/pikascript-api", &st) == -1) {
    mkdir("/littlefs/pikascript-api", 0700);
    printf("Created pikascript-api directory\n");
  }
}

void app_main(void) {
  vfs_init();
  PikaObj *PikaMain = pikaScriptInit();
  obj_run(PikaMain, "print(\"helloworld\")");

  pikaVM_runFile(PikaMain, "/littlefs/test.py");
}

概念

对象树

模块

通用模块

如何使用包管理器

编写requestment.txt

PikaStdLib 标准库

提供内存占用查询接口

PikaStdDevice 标准设备

提供了抽象接口,需要添加对应设备模块使用(见下章)

PikaStdData 数据结构

提供List (列表),Dict(字典)数据结构

HAL 层模块

调用规则:通用模块->抽象函数->HAL->具体底层实现

ESP32模块

编写自己的模块

创建.pyi接口

pikapython目录下编写.pyi

1
2
3
4
5
6
# Math.pyi
class Adder:
    def byInt(self, a:int, b:int)->int:
        pass
    def byFloat(self, a:float, b:float)->float:
        pass

运行./rust-msc-latest-win10来编译,注意需要在main.py中预先引用

创建对应的底层实现

上一步中编译器会生成对应的头文件,存放于pikascript-api目录下,但是具体的实现需要我们进行。
请在pikascript-lib目录下创建一个子目录(如Math),并创建对应的.c文件(如Math_Adder.c

1
2
3
4
5
6
/* Math_Adder.c */
#include "pikaScript.h"

double Math_Adder_byFloat(PikaObj *self, double a, double b) { return a + b; }

int Math_Adder_byInt(PikaObj *self, int a, int b) { return a + b; }

来调用吧

main.py 中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# main.py
import Math

adder = Math.Adder()

res1 = adder.byInt(1, 2)
print('1 + 2')
print(res1)

res2 = adder.byFloat(2.3, 4.2)
print('2.3 + 4.2')
print(res2)

补充

手册

可变参数
1
2
# test.pyi
def vals(a:int, *val):...
1
2
3
4
5
6
7
8
// test.c
void test_vals(PikaObj* self, int a, PikaTuple* val){
    printf("a: %d\n", a);
    for(int i =0; i< pikaTuple_getSize(val); i++){
        Arg* arg_i = pikaTuple_getArg(val, i);
        printf("val[%d]: %d\n", i, arg_getInt(arg_i));
    }
}

裁剪

  1. 按照模块进行裁剪非常简单,只要在 main.py 中删除 import 语句即可。
  2. 将不需要全局使用函数下放到子模块中,因为在 main.py 中直接导入的模块,处于运行时随时可用状态,因此所有的类都会被添加进工程。而被其他文件间接导入的模块,预编译能够判断哪些是不会被用到的,因此只有用到的类会被添加进工程。
Licensed under CC BY-NC-SA 4.0