Skip to content

Commit e04d072

Browse files
authored
Merge pull request #90 from hollyhockberry/nativeBuild
Support native environment in PlatformIO
2 parents accdb7b + cdf6ab8 commit e04d072

14 files changed

Lines changed: 219 additions & 26 deletions
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
; PlatformIO Project Configuration File
2+
;
3+
; Build options: build flags, source filter
4+
; Upload options: custom upload port, speed and extra flags
5+
; Library options: dependencies, extra library storages
6+
; Advanced options: extra scripting
7+
;
8+
; Please visit documentation for the other options and examples
9+
; https://docs.platformio.org/page/projectconf.html
10+
11+
[platformio]
12+
default_envs = native
13+
14+
[env]
15+
lib_extra_dirs=../../../
16+
lib_deps = m5stack/M5Unified@^0.1.11
17+
18+
[env:native]
19+
platform = native
20+
build_type = debug
21+
build_flags = -O0 -xc++ -std=c++14 -lSDL2
22+
-I"/usr/local/include/SDL2" ; for intel mac homebrew SDL2
23+
-L"/usr/local/lib" ; for intel mac homebrew SDL2
24+
-I"${sysenv.HOMEBREW_PREFIX}/include/SDL2" ; for arm mac homebrew SDL2
25+
-L"${sysenv.HOMEBREW_PREFIX}/lib" ; for arm mac homebrew SDL2
26+
27+
[env:native_m5stack]
28+
extends = native
29+
platform = native
30+
build_flags = ${env:native.build_flags}
31+
-DM5GFX_BOARD=board_M5Stack
32+
33+
[env:native_stickCPlus]
34+
extends = native
35+
platform = native
36+
build_flags = ${env:native.build_flags}
37+
-DM5GFX_SCALE=2
38+
-DM5GFX_ROTATION=0
39+
-DM5GFX_BOARD=board_M5StickCPlus
40+
41+
[esp32_base]
42+
build_type = debug
43+
platform = espressif32
44+
board = esp32dev
45+
upload_speed = 1500000
46+
monitor_speed = 115200
47+
monitor_filters = esp32_exception_decoder
48+
49+
[env:esp32_arduino]
50+
extends = esp32_base
51+
framework = arduino
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <M5Unified.h>
2+
#include <Avatar.h>
3+
4+
using namespace m5avatar;
5+
6+
Avatar avatar;
7+
8+
void setup()
9+
{
10+
M5.begin();
11+
avatar.init(); // start drawing
12+
13+
// adjust position
14+
const auto r = avatar.getFace()->getBoundingRect();
15+
const auto scale_w = M5.Display.width() / (float)r->getWidth();
16+
const auto scale_h = M5.Display.height() / (float)r->getHeight();
17+
const auto scale = std::min(scale_w, scale_h);
18+
avatar.setScale(scale);
19+
const auto offs_x = (r->getWidth() - M5.Display.width()) / 2;
20+
const auto offs_y = (r->getHeight() - M5.Display.height()) / 2;
21+
avatar.setPosition(-offs_y, -offs_x);
22+
}
23+
24+
void loop()
25+
{
26+
// avatar's face updates in another thread
27+
// so no need to loop-by-loop rendering
28+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <M5GFX.h>
2+
#if defined ( SDL_h_ )
3+
4+
void setup(void);
5+
void loop(void);
6+
7+
__attribute__((weak))
8+
int user_func(bool* running)
9+
{
10+
setup();
11+
do
12+
{
13+
loop();
14+
} while (*running);
15+
return 0;
16+
}
17+
18+
int main(int, char**)
19+
{
20+
// The second argument is effective for step execution with breakpoints.
21+
// You can specify the time in milliseconds to perform slow execution that ensures screen updates.
22+
return lgfx::Panel_sdl::main(user_func, 128);
23+
}
24+
25+
#endif

library.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@
1515
}
1616
],
1717
"license": "MIT",
18-
"dependencies": {
19-
},
18+
"dependencies": [
19+
{
20+
"name": "M5Unified",
21+
"version": ">=0.1.11"
22+
}
23+
],
2024
"version": "0.9.1",
21-
"frameworks": "arduino",
22-
"platforms": "espressif32"
25+
"frameworks": ["arduino", "espidf", "*"],
26+
"platforms": ["espressif32", "native"]
2327
}

library.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ category=Device Control
88
url=https://platformio.org/lib/show/4529/M5Stack-Avatar
99
architectures=esp32
1010
includes=Avatar.h
11+
depends=M5Unified

src/Avatar.cpp

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,54 @@
33
// license information.
44

55
#include "Avatar.h"
6+
7+
#ifndef PI
8+
#define PI 3.1415926535897932384626433832795
9+
#endif
10+
611
namespace m5avatar {
712

813
unsigned int seed = 0;
914

15+
#ifndef rand_r
16+
#define init_rand() srand(seed)
17+
#define _rand() rand()
18+
#else
19+
#define init_rand() ;
20+
#define _rand() rand_r(&seed)
21+
#endif
22+
23+
#ifdef SDL_h_
24+
#define TaskResult() return 0
25+
#define TaskDelay(ms) lgfx::delay(ms)
26+
long random(long howbig) {
27+
return std::rand() % howbig;
28+
}
29+
#else
30+
#define TaskResult() vTaskDelete(NULL)
31+
#define TaskDelay(ms) vTaskDelay(ms/portTICK_PERIOD_MS)
32+
#endif
33+
1034
// TODO(meganetaaan): make read-only
1135
DriveContext::DriveContext(Avatar *avatar) : avatar{avatar} {}
1236

1337
Avatar *DriveContext::getAvatar() { return avatar; }
1438

1539
TaskHandle_t drawTaskHandle;
1640

17-
void drawLoop(void *args) {
41+
TaskResult_t drawLoop(void *args) {
1842
DriveContext *ctx = reinterpret_cast<DriveContext *>(args);
1943
Avatar *avatar = ctx->getAvatar();
2044
while (avatar->isDrawing()) {
2145
if (avatar->isDrawing()) {
2246
avatar->draw();
2347
}
24-
vTaskDelay(10/portTICK_PERIOD_MS);
48+
TaskDelay(10);
2549
}
26-
vTaskDelete(NULL);
50+
TaskResult();
2751
}
2852

29-
void facialLoop(void *args) {
53+
TaskResult_t facialLoop(void *args) {
3054
int c = 0;
3155
DriveContext *ctx = reinterpret_cast<DriveContext *>(args);
3256
Avatar *avatar = ctx->getAvatar();
@@ -38,17 +62,18 @@ void facialLoop(void *args) {
3862
float vertical = 0.0f;
3963
float horizontal = 0.0f;
4064
float breath = 0.0f;
65+
init_rand();
4166
while (avatar->isDrawing()) {
4267

43-
if ((millis() - last_saccade_millis) > saccade_interval) {
44-
vertical = rand_r(&seed) / (RAND_MAX / 2.0) - 1;
45-
horizontal = rand_r(&seed) / (RAND_MAX / 2.0) - 1;
68+
if ((lgfx::millis() - last_saccade_millis) > saccade_interval) {
69+
vertical = _rand() / (RAND_MAX / 2.0) - 1;
70+
horizontal = _rand() / (RAND_MAX / 2.0) - 1;
4671
avatar->setGaze(vertical, horizontal);
4772
saccade_interval = 500 + 100 * random(20);
48-
last_saccade_millis = millis();
73+
last_saccade_millis = lgfx::millis();
4974
}
5075

51-
if ((millis()- last_blink_millis) > blink_interval) {
76+
if ((lgfx::millis()- last_blink_millis) > blink_interval) {
5277
if (eye_open) {
5378
avatar->setEyeOpenRatio(1);
5479
blink_interval = 2500 + 100 * random(20);
@@ -57,14 +82,14 @@ void facialLoop(void *args) {
5782
blink_interval = 300 + 10 * random(20);
5883
}
5984
eye_open = !eye_open;
60-
last_blink_millis = millis();
85+
last_blink_millis = lgfx::millis();
6186
}
6287
c = (c + 1) % 100;
6388
breath = sin(c * 2 * PI / 100.0);
6489
avatar->setBreath(breath);
65-
vTaskDelay(33/portTICK_PERIOD_MS);
90+
TaskDelay(33);
6691
}
67-
vTaskDelete(NULL);
92+
TaskResult();
6893
}
6994

7095
Avatar::Avatar() : Avatar(new Face()) {}
@@ -100,6 +125,13 @@ void Avatar::addTask(TaskFunction_t f
100125
, TaskHandle_t* const task_handle
101126
, const BaseType_t core_id) {
102127
DriveContext *ctx = new DriveContext(this);
128+
#ifdef SDL_h_
129+
if (task_handle == NULL) {
130+
SDL_CreateThreadWithStackSize(f, name, stack_size, ctx);
131+
} else {
132+
*task_handle = SDL_CreateThreadWithStackSize(f, name, stack_size, ctx);
133+
}
134+
#else
103135
// TODO(meganetaaan): set a task handler
104136
xTaskCreateUniversal(f, /* Function to implement the task */
105137
name, /* Name of the task */
@@ -108,6 +140,7 @@ void Avatar::addTask(TaskFunction_t f
108140
priority, /* Priority of the task */
109141
task_handle, /* Task handle. */
110142
core_id); /* Core No*/
143+
#endif
111144
}
112145

113146
void Avatar::init(int colorDepth) {
@@ -118,11 +151,15 @@ void Avatar::init(int colorDepth) {
118151
void Avatar::stop() { _isDrawing = false; }
119152

120153
void Avatar::suspend() {
154+
#ifndef SDL_h_
121155
vTaskSuspend(drawTaskHandle);
156+
#endif
122157
}
123158

124159
void Avatar::resume() {
160+
#ifndef SDL_h_
125161
vTaskResume(drawTaskHandle);
162+
#endif
126163
}
127164

128165
void Avatar::start(int colorDepth) {
@@ -132,6 +169,10 @@ void Avatar::start(int colorDepth) {
132169

133170
this->colorDepth = colorDepth;
134171
DriveContext *ctx = new DriveContext(this);
172+
#ifdef SDL_h_
173+
drawTaskHandle = SDL_CreateThreadWithStackSize(drawLoop, "drawLoop", 2048, ctx);
174+
SDL_CreateThreadWithStackSize(facialLoop, "facialLoop", 1024, ctx);
175+
#else
135176
// TODO(meganetaaan): keep handle of these tasks
136177
xTaskCreateUniversal(drawLoop, /* Function to implement the task */
137178
"drawLoop", /* Name of the task */
@@ -148,6 +189,7 @@ void Avatar::start(int colorDepth) {
148189
2, /* Priority of the task */
149190
NULL, /* Task handle. */
150191
APP_CPU_NUM);
192+
#endif
151193
}
152194

153195
void Avatar::draw() {

src/Avatar.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,26 @@
88
#include "Face.h"
99
#include <M5GFX.h>
1010

11+
#ifdef SDL_h_
12+
typedef SDL_ThreadFunction TaskFunction_t;
13+
typedef int BaseType_t;
14+
typedef unsigned int UBaseType_t;
15+
typedef SDL_Thread* TaskHandle_t;
16+
typedef int TaskResult_t;
17+
#define APP_CPU_NUM (1)
18+
#else
19+
typedef void TaskResult_t;
20+
#endif
21+
1122
#ifndef APP_CPU_NUM
1223
#define APP_CPU_NUM PRO_CPU_NUM
1324
#endif
1425

26+
#ifndef ARDUINO
27+
#include <string>
28+
typedef std::string String;
29+
#endif // ARDUINO
30+
1531
namespace m5avatar {
1632
class Avatar {
1733
private:

src/Balloon.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
#include "DrawContext.h"
1010
#include "Drawable.h"
1111

12+
#ifndef ARDUINO
13+
#include <string>
14+
typedef std::string String;
15+
#endif // ARDUINO
16+
1217
const int16_t TEXT_HEIGHT = 8;
1318
const int16_t TEXT_SIZE = 2;
1419
const int16_t MIN_WIDTH = 40;
@@ -39,7 +44,7 @@ class Balloon final : public Drawable {
3944
spi->setTextColor(primaryColor, backgroundColor);
4045
spi->setTextDatum(MC_DATUM);
4146
M5.Lcd.setFont(font);
42-
int textWidth = M5.Lcd.textWidth(text);
47+
int textWidth = M5.Lcd.textWidth(text.c_str());
4348
int textHeight = TEXT_HEIGHT * TEXT_SIZE;
4449
spi->fillEllipse(cx - 20, cy,textWidth + 2, textHeight * 2 + 2,
4550
primaryColor);
@@ -49,7 +54,7 @@ class Balloon final : public Drawable {
4954
backgroundColor);
5055
spi->fillTriangle(cx - 60, cy - 40, cx - 10, cy - 10, cx - 40, cy - 10,
5156
backgroundColor);
52-
spi->drawString(text, cx - textWidth / 6 - 15, cy, font); // Continue printing from new x position
57+
spi->drawString(text.c_str(), cx - textWidth / 6 - 15, cy, font); // Continue printing from new x position
5358
}
5459
};
5560

src/BoundingRect.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// license information.
44

55
#include "BoundingRect.h"
6-
#include <Arduino.h>
76

87
namespace m5avatar {
98
BoundingRect::BoundingRect(int16_t top, int16_t left)

src/BoundingRect.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#ifndef BOUNDINGRECT_H_
66
#define BOUNDINGRECT_H_
7-
#include <Arduino.h>
7+
#include <stdint.h>
88

99
namespace m5avatar {
1010
class BoundingRect {

0 commit comments

Comments
 (0)