# 编译中文字体显示固件


在上一个教程中我们已经学习了如何编译固件：<a href="https://www.zhiyanbang.com/website/md/89?content_id=hKcS5DYQOUgCVib1" target="_blank">点击这里查看</a> 接下来我们准备编译一个自定义的固件！


## 如何编译固件


在官网呢，给出了相关的文档，但是随着版本的更新，文档可能有些出入

- 拓展方法库1： https://www.espruino.com/Extending+Espruino+1
- 拓展方法库2： https://www.espruino.com/Extending+Espruino+2


## 此次编译目的

<a href="https://www.zhiyanbang.com/website/md/37?content_id=hBPz09Emdq8Yvact" target="_blank">点击这里查看</a>

实现JavaScript的中文调用
```javascript
LAOXU.chineseGet(data, width)
```

```javascript
LAOXU.chineseSimpleGet(data)
```

## 新建库的目录：

老许将此库放在了  Espruino/targets/esp32 中， 新增了`myLibs` 目录

![](https://cdn.zhiyanbang.com/gm2/WeChat384df1d9bf01000fa8c15a9a2a0abdd8.jpg)

## 修改 Espruino/Makefile 文件

因为我们是为了给 ESP32 拓展方法库。因此我们查找到代码：` ifdef USE_ESP32 ` 在下面添加如下代码：

```makefile
  ifdef USE_MYLIB
    WRAPPERSOURCES += $(wildcard targets/esp32/myLibs/jswrap*.c)
    SOURCES += $(wildcard targets/esp32/myLibs/lib*.c)
  endif 
```
- 完整代码片段：

```makefile
ifdef USE_ESP32
 DEFINES += -DUSE_ESP32
 WRAPPERSOURCES += \
   libs/network/jswrap_wifi.c \
   libs/network/esp32/jswrap_esp32_network.c \
   targets/esp32/jswrap_esp32.c
 INCLUDE += -I$(ROOT)/libs/network/esp32
 SOURCES +=  libs/network/esp32/network_esp32.c \
  targets/esp32/jshardwareI2c.c \
  targets/esp32/jshardwareSpi.c \
  targets/esp32/jshardwareUart.c \
  targets/esp32/jshardwareAnalog.c \
  targets/esp32/jshardwarePWM.c \
  targets/esp32/rtosutil.c \
  targets/esp32/jshardwareTimer.c \
  targets/esp32/jshardwarePulse.c
  ifdef USE_MYLIB
    WRAPPERSOURCES += $(wildcard targets/esp32/myLibs/jswrap*.c)
    SOURCES += $(wildcard targets/esp32/myLibs/lib*.c)
  endif 
  ifdef RTOS
   DEFINES += -DRTOS
   WRAPPERSOURCES += targets/esp32/jswrap_rtos.c
  endif # RTOS
 endif # USE_ESP32
```

## 新增jswrap_chinese.c、jswrap_chinese.h 文件

### 新增 targets/esp32/myLibs/jswrap_chinese.h

```c
#include "jsvar.h"

JsVar *jswrap_chinese_simpleGet(JsVar *data);
JsVar *jswrap_chinese_get(JsVar *data, JsVar *width);
```
我们定义了2个JavaScript的方法：jswrap_chinese_simpleGet、jswrap_chinese_get

- jswrap_chinese_get：data根据传入的中文UTF编码汉字

- targets/esp32/myLibs/jswrap_chinese.c: 多个字符需要自动换行展示的方法，data根据传入的中文UTF编码汉字, width为需要显示宽度。


data的数据可从 <a href="https://cdn.zhiyanbang.com/font_html/getZM.html " target="_blank">这里的</a> 最下方获取

![](https://cdn.zhiyanbang.com/md18/WeChat269a02c6472763ac0a827ef5daf3d6e8.jpg)

### 新增 targets/esp32/myLibs/jswrap_chinese.c

引入和定义相关变量： char_map为所有中文。可在这里获得

![](https://cdn.zhiyanbang.com/md18/WeChat2fb930f9db1b5ab2e7306994267b7784.jpg)

```c
#include "jswrap_chinese.h"
#include "jsinteractive.h"
#include "jsvariterator.h"
#include "jswrap_storage.h"

// 每个字符的点阵大小
#define CHAR_SIZE 32
#define CHAR_WIDTH 16
#define CHAR_HEIGHT 16
static const unsigned int char_map[] = {
// 示例:
0x554a,0x963f,0x57c3,0x6328,
0x54ce,0x5509,0x8bf6,0x54c0,
0x7691,0x764c,0x853c,0x77ee,
0x827e,0x788d,0x7231,0x9698,
0x978d,0x6c28,0x5b89,0x4ffa,
0x6309,0x6697,0x5cb8,0x80fa,
...
}

// 字符映射表的大小
#define CHAR_MAP_SIZE (sizeof(char_map) / sizeof(char_map[0]))
// 静态缓冲区，用于存储单个字符的点阵数据
static unsigned char static_dot_matrix_buffer[CHAR_SIZE];
```

-  定义一个获取字符映射的 get_dot_matrix_data 函数, 我们的思路是根据传递进来的中文字符，然后将其对应的`位图数据`找到，再进行返回，让espruino去渲染

```c
const unsigned char* get_dot_matrix_data(unsigned int unicode) {
	// 查找 Unicode 字符在 char_map 中的索引
	int charIndex = -1;
	for (size_t i = 0; i < CHAR_MAP_SIZE; i++) {
		if (char_map[i] == unicode) {
			charIndex = i;
			break;
		}
	}

	if (charIndex == -1) {
		jsiConsolePrintf('not find');
		// 未找到字符
		return NULL;
	}

	size_t bytes_per_char = CHAR_SIZE;
	size_t offset = charIndex * bytes_per_char;

	// 读取存储在外部 flash 的点阵数据
	JsVar *name = jsvNewFromString("font_data.bin");
	JsVar *fontDataBuffer = jswrap_storage_readArrayBuffer(name);
	jsvUnLock(name);

	if (!fontDataBuffer) {
		return NULL;
	}

	uint32_t bufferOffset;
	JsVar *backingString = jsvGetArrayBufferBackingString(fontDataBuffer, &bufferOffset);

	if (!backingString || offset + bytes_per_char > jsvGetArrayBufferLength(fontDataBuffer)) {
		jsvUnLock(fontDataBuffer);
		return NULL;
	}

	// 读取点阵数据到缓冲区
	for (size_t i = 0; i < bytes_per_char; i++) {
		static_dot_matrix_buffer[i] = (unsigned char)jsvGetInteger(jsvArrayBufferGet(fontDataBuffer, bufferOffset + offset + i));
	}

	jsvUnLock(fontDataBuffer);
	return static_dot_matrix_buffer;
}
```
- 定义jswrap_chinese_get 的JavaScript函数，他可以让我们的JavaScript去调用，如何给JavaScript注入方法呢？注释部分很重要.
- 注释中的class :LAOXU 为我们JavaScript全局变量。
- name: chineseGet 为JavaScript中的函数名称，比如这个示例则为：`LAOXU.chineseGet`。
- generate 为对应的c函数名称。
- params 为传入JavaScript函数的变量
- return 为函数的返回值。


```c

/*JSON{
  "type" : "staticmethod",
  "class" : "LAOXU",
  "name" : "chineseGet",
  "generate" : "jswrap_chinese_get",
  "params" : [
    ["str","JsVar","string is chinese data"],
    ["width","JsVar","img width"]
   ],
  "return" : ["JsVar","font_arr"]
}
 */
 
JsVar *jswrap_chinese_get(JsVar *data, JsVar *width) {

	int charsPerLine = jsvGetInteger(width) / CHAR_WIDTH;
	JsVar *resultArray = jsvNewEmptyArray(); // 创建一个空数组

	// 初始化字符串迭代器并计算字符数量
	JsvStringIterator it;
	jsvStringIteratorNew(&it, data, 0);
	int charCount = 0;

	while (jsvStringIteratorHasChar(&it)) {
		unsigned char c = (unsigned char)jsvStringIteratorGetCharAndNext(&it);
		if ((c & 0x80) == 0x00 || (c & 0xE0) == 0xC0 || (c & 0xF0) == 0xE0 || (c & 0xF8) == 0xF0) {
			charCount++;
		}
	}

	jsvStringIteratorFree(&it); // 释放迭代器资源

	int numFullLines = charCount / charsPerLine;
	int remainder = charCount % charsPerLine;
	int arrLength = numFullLines + (remainder ? 1 : 0);
	int yOffset = 0;

	jsvStringIteratorNew(&it, data, 0); // 重新初始化字符串迭代器

	for (int j = 0; j < arrLength; j++) {
		int charsInThisLine = (j < numFullLines) ? charsPerLine : remainder;
		JsVar *resultObj = jsvNewObject(); // 每行的结果对象
		JsVar *bigObj = jsvNewObject(); // 包含y属性和结果对象的对象
		int totalWidth = CHAR_WIDTH * charsInThisLine;

		// 设置 bitmap 的属性
		jsvObjectSetChildAndUnLock(resultObj, "width", jsvNewFromInteger(totalWidth));
		jsvObjectSetChildAndUnLock(resultObj, "height", jsvNewFromInteger(CHAR_HEIGHT));
		jsvObjectSetChildAndUnLock(resultObj, "bpp", jsvNewFromInteger(1));
		jsvObjectSetChildAndUnLock(resultObj, "transparent", jsvNewFromInteger(0));

		// 创建 buffer 存储点阵数据
		JsVar *buffer = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, totalWidth * CHAR_HEIGHT / 8);
		int currentX = 0;

		for (int k = 0; k < charsInThisLine; k++) {
			unsigned char c = (unsigned char)jsvStringIteratorGetCharAndNext(&it);
			int cp = c;
			int ra = 0;

			if (c > 127) {
				if ((c & 0xE0) == 0xC0) {
					cp = c & 0x1F; ra = 1;
				} else if ((c & 0xF0) == 0xE0) {
					cp = c & 0x0F; ra = 2;
				} else if ((c & 0xF8) == 0xF0) {
					cp = c & 0x07; ra = 3;
				}
				while (ra--) {
					cp = (cp << 6) | ((unsigned char)jsvStringIteratorGetCharAndNext(&it) & 0x3F);
				}
			}

			const unsigned char* dot_matrix = get_dot_matrix_data(cp);
			if (dot_matrix) {
				for (int row = 0; row < CHAR_HEIGHT; row++) {
					int bufferIndex = row * (totalWidth / 8) + (currentX / 8);
					for (int byte = 0; byte < CHAR_WIDTH / 8; byte++) {
						int dotMatrixIndex = row * (CHAR_WIDTH / 8) + byte;
						JsvArrayBufferIterator it2;
						jsvArrayBufferIteratorNew(&it2, buffer, bufferIndex + byte);
						jsvArrayBufferIteratorSetByteValue(&it2, dot_matrix[dotMatrixIndex]);
						jsvArrayBufferIteratorFree(&it2);
					}
				}
				currentX += CHAR_WIDTH;
			} else {
				jsiConsolePrintf("not find chinese string");
			}
		}

		// 释放资源
		jsvObjectSetChildAndUnLock(resultObj, "buffer", buffer);

		// 设置 bigObj 的属性
		jsvObjectSetChildAndUnLock(bigObj, "y", jsvNewFromInteger(yOffset));
		jsvObjectSetChildAndUnLock(bigObj, "fontData", resultObj);

		// 将 bigObj 添加到 resultArray 中
		jsvArrayPushAndUnLock(resultArray, bigObj);

		yOffset += CHAR_HEIGHT; // 每行的 yOffset 递增
	}

	jsvStringIteratorFree(&it); // 释放迭代器资源
	return resultArray;
}

```

- 同样我们可以接着定义JavaScript中 LAOXU.chineseSimpleGet，c中的jswrap_chinese_simpleGet 方法

```c

/*JSON{
  "type" : "staticmethod",
  "class" : "LAOXU",
  "name" : "chineseSimpleGet",
  "generate" : "jswrap_chinese_simpleGet",
  "params" : [
    ["str","JsVar","string is chinese data"]
   ],
  "return" : ["JsVar","font_Object"]
}
 */
 
JsVar *jswrap_chinese_simpleGet(JsVar *data) {

	// 初始化字符串迭代器
	JsvStringIterator it1;
	jsvStringIteratorNew(&it1, data, 0);
    // 获取字符串的长度
    
    // 计算实际字符数
	int charCount = 0;
	while (jsvStringIteratorHasChar(&it1)) {
		unsigned char c = (unsigned char)jsvStringIteratorGetCharAndNext(&it1);
		if ((c & 0x80) == 0x00 || (c & 0xE0) == 0xC0 || (c & 0xF0) == 0xE0 || (c & 0xF8) == 0xF0) {
			charCount++;
		}
	}
	jsvStringIteratorFree(&it1); // 释放迭代器资源
    // 设置总宽度为字符宽度的倍数
    int totalWidth = CHAR_WIDTH * charCount;
	
    JsVar *resultObj = jsvNewObject();

    // 设置 bitmap 的属性
    jsvObjectSetChildAndUnLock(resultObj, "width", jsvNewFromInteger(totalWidth));
    jsvObjectSetChildAndUnLock(resultObj, "height", jsvNewFromInteger(CHAR_HEIGHT));
    jsvObjectSetChildAndUnLock(resultObj, "bpp", jsvNewFromInteger(1));
    jsvObjectSetChildAndUnLock(resultObj, "transparent", jsvNewFromInteger(0));
    
    // 创建总 buffer 存储拼接后的点阵数据
	JsVar *buffer = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, totalWidth * CHAR_HEIGHT / 8);

	// 初始化字符串迭代器
	JsvStringIterator it;
	jsvStringIteratorNew(&it, data, 0);

	int currentX = 0; // 当前字符的 X 偏移量
	while (jsvStringIteratorHasChar(&it)) {
		unsigned char c = (unsigned char)jsvStringIteratorGetCharAndNext(&it);
		int cp = c; // Code point defaults to ASCII
		int ra = 0; // Read ahead
		if (c > 127) {
			if ((c & 0xE0) == 0xC0) { // 2-byte code starts with 0b110xxxxx
				cp = c & 0x1F; ra = 1;
			} else if ((c & 0xF0) == 0xE0) { // 3-byte code starts with 0b1110xxxx
				cp = c & 0x0F; ra = 2;
			} else if ((c & 0xF8) == 0xF0) { // 4-byte code starts with 0b11110xxx
				cp = c & 0x07; ra = 3;
			}
			while (ra--) {
				cp = (cp << 6) | ((unsigned char)jsvStringIteratorGetCharAndNext(&it) & 0x3F);
			}
		}

		// 获取字符的点阵数据
		const unsigned char* dot_matrix = get_dot_matrix_data(cp);
		if (dot_matrix) {
			// 使用迭代器将点阵数据复制到总 buffer 中
			for (int row = 0; row < CHAR_HEIGHT; row++) {
				// 计算目标 buffer 的起始位置
				int bufferIndex = row * (totalWidth / 8) + (currentX / 8);

				// 获取当前字符的点阵数据
				for (int byte = 0; byte < CHAR_WIDTH / 8; byte++) {
					int dotMatrixIndex = row * (CHAR_WIDTH / 8) + byte;

					// 将字符的点阵数据直接复制到总 buffer 的相应位置
					JsvArrayBufferIterator it2;
					jsvArrayBufferIteratorNew(&it2, buffer, bufferIndex + byte);
					jsvArrayBufferIteratorSetByteValue(&it2, dot_matrix[dotMatrixIndex]);
					jsvArrayBufferIteratorFree(&it2); // 释放 it2 迭代器
				}
			}
			currentX += CHAR_WIDTH; // 移动到下一个字符的位置
		} else {
			jsiConsolePrintf('not find chinese string');
		}
	}
    // 释放资源
    jsvStringIteratorFree(&it);

    // 将结果对象的 "buffer" 设置为拼接后的 buffer
    jsvObjectSetChildAndUnLock(resultObj, "buffer", buffer);

    return resultObj; // 这里需要加分号
}

``
### 完整代码

```c
#include "jswrap_chinese.h"
#include "jsinteractive.h"
#include "jsvariterator.h"
#include "jswrap_storage.h"

// 每个字符的点阵大小
#define CHAR_SIZE 32
#define CHAR_WIDTH 16
#define CHAR_HEIGHT 16

// 字符映射表 (仅存储 Unicode 编码)
static const unsigned int char_map[] = {
0x554a,0x963f,0x57c3,0x6328,
0x54ce,0x5509,0x8bf6,0x54c0,
0x7691,0x764c,0x853c,0x77ee,
0x827e,0x788d,0x7231,0x9698,
0x978d,0x6c28,0x5b89,0x4ffa,
... //更多字符数据
};


// 字符映射表的大小
#define CHAR_MAP_SIZE (sizeof(char_map) / sizeof(char_map[0]))
// 静态缓冲区，用于存储单个字符的点阵数据
static unsigned char static_dot_matrix_buffer[CHAR_SIZE];

const unsigned char* get_dot_matrix_data(unsigned int unicode) {
	// 查找 Unicode 字符在 char_map 中的索引
	int charIndex = -1;
	for (size_t i = 0; i < CHAR_MAP_SIZE; i++) {
		if (char_map[i] == unicode) {
			charIndex = i;
			break;
		}
	}

	if (charIndex == -1) {
		jsiConsolePrintf('not find');
		// 未找到字符
		return NULL;
	}

	size_t bytes_per_char = CHAR_SIZE;
	size_t offset = charIndex * bytes_per_char;

	// 读取存储在外部 flash 的点阵数据
	JsVar *name = jsvNewFromString("font_data.bin");
	JsVar *fontDataBuffer = jswrap_storage_readArrayBuffer(name);
	jsvUnLock(name);

	if (!fontDataBuffer) {
		return NULL;
	}

	uint32_t bufferOffset;
	JsVar *backingString = jsvGetArrayBufferBackingString(fontDataBuffer, &bufferOffset);

	if (!backingString || offset + bytes_per_char > jsvGetArrayBufferLength(fontDataBuffer)) {
		jsvUnLock(fontDataBuffer);
		return NULL;
	}

	// 读取点阵数据到缓冲区
	for (size_t i = 0; i < bytes_per_char; i++) {
		static_dot_matrix_buffer[i] = (unsigned char)jsvGetInteger(jsvArrayBufferGet(fontDataBuffer, bufferOffset + offset + i));
	}

	jsvUnLock(fontDataBuffer);
	return static_dot_matrix_buffer;
}

/*JSON{
  "type" : "staticmethod",
  "class" : "LAOXU",
  "name" : "chineseGet",
  "generate" : "jswrap_chinese_get",
  "params" : [
    ["str","JsVar","string is chinese data"],
    ["width","JsVar","img width"]
   ],
  "return" : ["JsVar","font_arr"]
}
 */
 
JsVar *jswrap_chinese_get(JsVar *data, JsVar *width) {

	int charsPerLine = jsvGetInteger(width) / CHAR_WIDTH;
	JsVar *resultArray = jsvNewEmptyArray(); // 创建一个空数组

	// 初始化字符串迭代器并计算字符数量
	JsvStringIterator it;
	jsvStringIteratorNew(&it, data, 0);
	int charCount = 0;

	while (jsvStringIteratorHasChar(&it)) {
		unsigned char c = (unsigned char)jsvStringIteratorGetCharAndNext(&it);
		if ((c & 0x80) == 0x00 || (c & 0xE0) == 0xC0 || (c & 0xF0) == 0xE0 || (c & 0xF8) == 0xF0) {
			charCount++;
		}
	}

	jsvStringIteratorFree(&it); // 释放迭代器资源

	int numFullLines = charCount / charsPerLine;
	int remainder = charCount % charsPerLine;
	int arrLength = numFullLines + (remainder ? 1 : 0);
	int yOffset = 0;

	jsvStringIteratorNew(&it, data, 0); // 重新初始化字符串迭代器

	for (int j = 0; j < arrLength; j++) {
		int charsInThisLine = (j < numFullLines) ? charsPerLine : remainder;
		JsVar *resultObj = jsvNewObject(); // 每行的结果对象
		JsVar *bigObj = jsvNewObject(); // 包含y属性和结果对象的对象
		int totalWidth = CHAR_WIDTH * charsInThisLine;

		// 设置 bitmap 的属性
		jsvObjectSetChildAndUnLock(resultObj, "width", jsvNewFromInteger(totalWidth));
		jsvObjectSetChildAndUnLock(resultObj, "height", jsvNewFromInteger(CHAR_HEIGHT));
		jsvObjectSetChildAndUnLock(resultObj, "bpp", jsvNewFromInteger(1));
		jsvObjectSetChildAndUnLock(resultObj, "transparent", jsvNewFromInteger(0));

		// 创建 buffer 存储点阵数据
		JsVar *buffer = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, totalWidth * CHAR_HEIGHT / 8);
		int currentX = 0;

		for (int k = 0; k < charsInThisLine; k++) {
			unsigned char c = (unsigned char)jsvStringIteratorGetCharAndNext(&it);
			int cp = c;
			int ra = 0;

			if (c > 127) {
				if ((c & 0xE0) == 0xC0) {
					cp = c & 0x1F; ra = 1;
				} else if ((c & 0xF0) == 0xE0) {
					cp = c & 0x0F; ra = 2;
				} else if ((c & 0xF8) == 0xF0) {
					cp = c & 0x07; ra = 3;
				}
				while (ra--) {
					cp = (cp << 6) | ((unsigned char)jsvStringIteratorGetCharAndNext(&it) & 0x3F);
				}
			}

			const unsigned char* dot_matrix = get_dot_matrix_data(cp);
			if (dot_matrix) {
				for (int row = 0; row < CHAR_HEIGHT; row++) {
					int bufferIndex = row * (totalWidth / 8) + (currentX / 8);
					for (int byte = 0; byte < CHAR_WIDTH / 8; byte++) {
						int dotMatrixIndex = row * (CHAR_WIDTH / 8) + byte;
						JsvArrayBufferIterator it2;
						jsvArrayBufferIteratorNew(&it2, buffer, bufferIndex + byte);
						jsvArrayBufferIteratorSetByteValue(&it2, dot_matrix[dotMatrixIndex]);
						jsvArrayBufferIteratorFree(&it2);
					}
				}
				currentX += CHAR_WIDTH;
			} else {
				jsiConsolePrintf("not find chinese string");
			}
		}

		// 释放资源
		jsvObjectSetChildAndUnLock(resultObj, "buffer", buffer);

		// 设置 bigObj 的属性
		jsvObjectSetChildAndUnLock(bigObj, "y", jsvNewFromInteger(yOffset));
		jsvObjectSetChildAndUnLock(bigObj, "fontData", resultObj);

		// 将 bigObj 添加到 resultArray 中
		jsvArrayPushAndUnLock(resultArray, bigObj);

		yOffset += CHAR_HEIGHT; // 每行的 yOffset 递增
	}

	jsvStringIteratorFree(&it); // 释放迭代器资源
	return resultArray;
}

/*JSON{
  "type" : "staticmethod",
  "class" : "LAOXU",
  "name" : "chineseSimpleGet",
  "generate" : "jswrap_chinese_simpleGet",
  "params" : [
    ["str","JsVar","string is chinese data"]
   ],
  "return" : ["JsVar","font_Object"]
}
 */
 
JsVar *jswrap_chinese_simpleGet(JsVar *data) {

	// 初始化字符串迭代器
	JsvStringIterator it1;
	jsvStringIteratorNew(&it1, data, 0);
    // 获取字符串的长度
    
    // 计算实际字符数
	int charCount = 0;
	while (jsvStringIteratorHasChar(&it1)) {
		unsigned char c = (unsigned char)jsvStringIteratorGetCharAndNext(&it1);
		if ((c & 0x80) == 0x00 || (c & 0xE0) == 0xC0 || (c & 0xF0) == 0xE0 || (c & 0xF8) == 0xF0) {
			charCount++;
		}
	}
	jsvStringIteratorFree(&it1); // 释放迭代器资源
    // 设置总宽度为字符宽度的倍数
    int totalWidth = CHAR_WIDTH * charCount;
	
    JsVar *resultObj = jsvNewObject();

    // 设置 bitmap 的属性
    jsvObjectSetChildAndUnLock(resultObj, "width", jsvNewFromInteger(totalWidth));
    jsvObjectSetChildAndUnLock(resultObj, "height", jsvNewFromInteger(CHAR_HEIGHT));
    jsvObjectSetChildAndUnLock(resultObj, "bpp", jsvNewFromInteger(1));
    jsvObjectSetChildAndUnLock(resultObj, "transparent", jsvNewFromInteger(0));
    
    // 创建总 buffer 存储拼接后的点阵数据
	JsVar *buffer = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, totalWidth * CHAR_HEIGHT / 8);

	// 初始化字符串迭代器
	JsvStringIterator it;
	jsvStringIteratorNew(&it, data, 0);

	int currentX = 0; // 当前字符的 X 偏移量
	while (jsvStringIteratorHasChar(&it)) {
		unsigned char c = (unsigned char)jsvStringIteratorGetCharAndNext(&it);
		int cp = c; // Code point defaults to ASCII
		int ra = 0; // Read ahead
		if (c > 127) {
			if ((c & 0xE0) == 0xC0) { // 2-byte code starts with 0b110xxxxx
				cp = c & 0x1F; ra = 1;
			} else if ((c & 0xF0) == 0xE0) { // 3-byte code starts with 0b1110xxxx
				cp = c & 0x0F; ra = 2;
			} else if ((c & 0xF8) == 0xF0) { // 4-byte code starts with 0b11110xxx
				cp = c & 0x07; ra = 3;
			}
			while (ra--) {
				cp = (cp << 6) | ((unsigned char)jsvStringIteratorGetCharAndNext(&it) & 0x3F);
			}
		}

		// 获取字符的点阵数据
		const unsigned char* dot_matrix = get_dot_matrix_data(cp);
		if (dot_matrix) {
			// 使用迭代器将点阵数据复制到总 buffer 中
			for (int row = 0; row < CHAR_HEIGHT; row++) {
				// 计算目标 buffer 的起始位置
				int bufferIndex = row * (totalWidth / 8) + (currentX / 8);

				// 获取当前字符的点阵数据
				for (int byte = 0; byte < CHAR_WIDTH / 8; byte++) {
					int dotMatrixIndex = row * (CHAR_WIDTH / 8) + byte;

					// 将字符的点阵数据直接复制到总 buffer 的相应位置
					JsvArrayBufferIterator it2;
					jsvArrayBufferIteratorNew(&it2, buffer, bufferIndex + byte);
					jsvArrayBufferIteratorSetByteValue(&it2, dot_matrix[dotMatrixIndex]);
					jsvArrayBufferIteratorFree(&it2); // 释放 it2 迭代器
				}
			}
			currentX += CHAR_WIDTH; // 移动到下一个字符的位置
		} else {
			jsiConsolePrintf('not find chinese string');
		}
	}
    // 释放资源
    jsvStringIteratorFree(&it);

    // 将结果对象的 "buffer" 设置为拼接后的 buffer
    jsvObjectSetChildAndUnLock(resultObj, "buffer", buffer);

    return resultObj; // 这里需要加分号
}

```

## 编译固件

因为我们在makefile中已经写了相关引入，所以直接编译，但是我们有了USE_MYLIB的条件，也就是说在 USE_MYLIB=1 的时候，才会编译我们自己写的类库。因此我们需要转换我们的编译命令

-  确保编译环境正确  

```bash
source scripts/provision.sh ESP32
```

- 运行编译代码

```bash
make clean && BOARD=ESP32 USE_MYLIB=1 mak
```

一直到编译成功，如果不出意外，再次找到bin中的固件，给ESP32刷入之后。您就可以直接在JavaScript中调用 LAOXU 类的相关方法了！

