跳到主要内容

Wio Terminal 表情交互

img

Demo 概述

这个 Demo 的功能是在 Wio Terminal 上实现表情交互,实现的方式是在 LCD 屏幕上显示多张图片(例如眼睛和嘴巴),这些图片是 BMP 格式的,并且存储在 SD 卡中。为了增强交互效果,我们还使用 Wio Terminal 内置的按键和陀螺仪。

提示:图片可以在这里下载,然后放在 SD 卡的 face 文件夹中。

交互方式

  • 左键:上一张图片(切换眼睛)
  • 中键:动画
  • 右键:下一张图片(切换眼睛)
  • 陀螺仪:眼睛会随 Wio Terminal 的倾斜方向移动

安装依赖库

本示例 Demo 依赖 LCD 库、 Linechart 库和 LIS3DHTR 库:

另外,本示例还依赖一个 RawImage.h 库,使用这个库可以使加载和显示图像更加容易,执行下面命令下载。

wget https://files.seeedstudio.com/wiki/Wio-Terminal/res/RawImage.h

提示:将 RawImage.h 添加到 Arduino 工程,可参考 Wio Terminal LCD 加载图片

功能实现

初始化 LCD 显示屏

TFT_eSPI tft;

void setup() {
...
tft.begin();
tft.setRotation(3);
...
}

初始化 SD 卡

void setup() {
...
if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
while (1);
}
...
}

初始化陀螺仪

LIS3DHTR<TwoWire>  lis;

void setup () {
...
lis.begin(Wire1);
lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ); //Output rate of Accelerator
lis.setFullScaleRange(LIS3DHTR_RANGE_2G); //Scale selection
...
}

void loop() {
...
float x_raw = lis.getAccelerationX(); //Read the raw x-axis values of acc
float y_raw = lis.getAccelerationY(); //Read the raw y-axis values of acc
...
}

初始化按键

void setup() {
...
pinMode(BUTTON_1, INPUT); //left button
pinMode(BUTTON_2, INPUT); //middle button
pinMode(BUTTON_3, INPUT); //right button
...
}

初始化图片变量和绘图

由于图片不是 320x240 的全尺寸,所以在绘图时,需要指定图片的开始位置。为避免摇晃 Wio Terminal 造成图片滞后,应先将图片加载到缓冲区中。(可参考 Wio Terminal LCD 加载图片

void loop() {
...
Raw8 * eyes = newImage<uint8_t>(eye[eye_count]); //initilising 8-bit images
writeToBuffer(x_axis, y_axis, eyes); //writing to buffer first, see full code to check this function
...
}

完整代码

#ifdef KENDRYTE_K210
#include <SPIClass.h>
#else
#include <SPI.h>
#endif

#include<string.h>
#include "Seeed_FS.h"
#include"LIS3DHTR.h"
#include"TFT_eSPI.h"
#include"RawImage.h"

LIS3DHTR<TwoWire> lis;
TFT_eSPI tft;

#define debug(...) //Serial.printf(__VA_ARGS__);
#define debug_begin(baud) //Serial.begin(baud); while (!Serial);
#define screen_height 240
#define screen_width 320

uint8_t raw[screen_height * screen_width];

void writeToBuffer(int x, int y, Raw8 * img) {
// debug("%p\n", img);
// debug("%d, %d\n", img->width, img->height);
for (int j = y; j < y + img->height(); j++) {
for (int i = x; i < x + img->width(); i++) {
raw[j * screen_width + i] = img->get(i - x, j - y);
}
}
}

void clearBuffer() {
memset(raw, 0, sizeof(raw));
}

void flushBuffer() {
tft.pushImage(0, 0, tft.width(), tft.height(), raw);
}

bool flag_left = false;
void button_handler_left() {
flag_left = true;
}

bool flag_middle = false;
void button_handler_middle() {
flag_middle = true;
}

bool flag_right = false;
void button_handler_right() {
flag_right = true;
}

#define FILLTER_N 20
int filter(int16_t val) {
int32_t filter_sum = 0;
for (uint8_t i = 0; i < FILLTER_N; i++) {
filter_sum += val;
delay(1);
}
return (int)(filter_sum / FILLTER_N);
}

const char* face[] = {"face/defaultface.bmp", "face/face0.bmp", "face/face1.bmp", "face/face2.bmp", "face/face3.bmp"};

const char* eye[] = {"face/cool.bmp", "face/happy.bmp", "face/heart.bmp", "face/robot.bmp", "face/sad.bmp",
"face/star.bmp", "face/cross.bmp", "face/kaws.bmp", "face/red.bmp"
};

void setup() {
debug_begin(115200);
pinMode(BUTTON_1, INPUT);
pinMode(BUTTON_2, INPUT);
pinMode(BUTTON_3, INPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON_1), button_handler_left, FALLING);
attachInterrupt(digitalPinToInterrupt(BUTTON_2), button_handler_middle, FALLING);
attachInterrupt(digitalPinToInterrupt(BUTTON_3), button_handler_right, FALLING);
if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI, 16000000)) {
while (1);
}

tft.begin();
tft.setRotation(3);

tft.fillScreen(TFT_BLACK);

lis.begin(Wire1);
lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ);
lis.setFullScaleRange(LIS3DHTR_RANGE_2G);
}

int8_t eye_count = 0;

void loop() {
float x_raw = lis.getAccelerationX();
float y_raw = lis.getAccelerationY();

int16_t x_value = 100 * x_raw;
int16_t y_value = 100 * y_raw;

delay(30);

int16_t x_axis = map(filter(x_value), -15, 15, 73, 77); // mapped x-axis
int16_t y_axis = map(filter(y_value), -15, 15, 53, 57); // mapped y-axis

Raw8 * eyes = newImage<uint8_t>(eye[eye_count]);
writeToBuffer(x_axis, y_axis, eyes);
writeToBuffer(x_axis + 120, y_axis, eyes);
delete[] eyes;

Raw8 * mouth = newImage<uint8_t>("face/mouth.bmp");
writeToBuffer(112, 175, mouth);
delete[] mouth;
flushBuffer();
clearBuffer();

if (flag_left) {
flag_left = false;
eye_count++;
if (eye_count == 9) eye_count = 0;
}
if (flag_right) {
flag_right = false;
eye_count--;
if (eye_count == -1) eye_count = 8;
}

if (flag_middle) {
flag_middle = false;
for (uint8_t cnt = 1; cnt < 5; cnt++) {
drawImage<uint8_t>(face[cnt], 75, 55);
}
}
}

GitHub 仓库:GetIoT_WioTerminal_Demo/Emoji_face