-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
640 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
#include "defines.h" | ||
#include "app.h" | ||
#include "ui.h" | ||
|
||
#define EXIT_BUTTON_PRESS_SECONDS 5 | ||
#define EXIT_BUTTON_PRESS_DELAY ONE_SECOND* EXIT_BUTTON_PRESS_SECONDS | ||
|
||
namespace ble_gamepad_app { | ||
|
||
MainApp::MainApp() : | ||
App(BLE_GAMEPAD_APP_NAME), uiFps(UI_FPS_WATCHER, UI_DELAY_MILLIS), lastSecondsToExit(EXIT_BUTTON_PRESS_SECONDS) { | ||
} | ||
|
||
void MainApp::run() { | ||
if (!bleGamepadController.start()) { | ||
lilka::serial_err("[%s] Starting BLE error!", LOG_TAG); | ||
bleGamepadController.stop(); | ||
} | ||
uiLoop(); | ||
} | ||
|
||
void MainApp::uiLoop() { | ||
UI ui(canvas->width(), canvas->height()); | ||
while (bleGamepadController.isActive()) { | ||
uiFps.onStartFrame(); | ||
if (isExitHotkeyPressed()) { | ||
onStop(); | ||
return; | ||
} | ||
ui.drawFrame(bleGamepadController.isConnected(), lastSecondsToExit); | ||
canvas->drawCanvas(ui.getFrameBuffer()); | ||
queueDraw(); | ||
uiFps.onEndFrame(); | ||
vTaskDelay(uiFps.getLimitMillis() / portTICK_PERIOD_MS); | ||
if (DEBUG) { | ||
uiFps.logEveryOneSecond(); | ||
} | ||
} | ||
} | ||
|
||
void MainApp::onStop() { | ||
bleGamepadController.stop(); | ||
} | ||
|
||
bool MainApp::isExitHotkeyPressed() { | ||
lilka::State st = lilka::controller.peekState(); | ||
lilka::ButtonState hotkeyState = st.select; | ||
if (!hotkeyState.pressed) { | ||
lastSecondsToExit = EXIT_BUTTON_PRESS_SECONDS; | ||
return false; | ||
} | ||
uint64_t hotkeyPressTime = hotkeyState.time; | ||
uint64_t curTime = millis(); | ||
uint64_t delta = curTime - hotkeyPressTime; | ||
lastSecondsToExit = EXIT_BUTTON_PRESS_SECONDS - delta / ONE_SECOND; | ||
if(lastSecondsToExit < 0) { | ||
lastSecondsToExit = 0; | ||
} | ||
return lastSecondsToExit == 0; | ||
} | ||
|
||
MainApp::~MainApp() { | ||
onStop(); | ||
} | ||
|
||
} // namespace ble_gamepad_app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#pragma once | ||
|
||
#include <app.h> | ||
#include "fps.h" | ||
#include "controller.h" | ||
|
||
namespace ble_gamepad_app { | ||
|
||
class MainApp : public App { | ||
public: | ||
MainApp(); | ||
~MainApp() override; | ||
|
||
private: | ||
void run() override; | ||
void uiLoop(); | ||
void onStop() override; | ||
bool isExitHotkeyPressed(); | ||
|
||
Controller bleGamepadController; | ||
FPS uiFps; | ||
int lastSecondsToExit; | ||
}; | ||
|
||
} // namespace ble_gamepad_app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
#include <Arduino.h> | ||
#include <NimBLEDevice.h> | ||
#include <lilka.h> | ||
#include "defines.h" | ||
#include "controller.h" | ||
|
||
#define BUTTON_A BUTTON_2 | ||
#define BUTTON_B BUTTON_1 | ||
#define BUTTON_C BUTTON_4 | ||
#define BUTTON_D BUTTON_3 | ||
|
||
namespace ble_gamepad_app { | ||
|
||
Controller::Controller() : | ||
BleGamepad(DEVICE_NAME), | ||
active(false), | ||
mainLoopFps(MAIN_LOOP_FPS_WATCHER, CONTROLLER_TIMER_DELAY_MILLIS), | ||
batteryLevel(BATTERY_LEVEL_UNKNOWN), | ||
checkBatteryLevelTimer(ONE_MINUTE) { | ||
} | ||
|
||
bool Controller::start() { | ||
lilka::serial_log("[%s] Starting BLE", LOG_TAG); | ||
|
||
controllerTimer = xTimerCreate( | ||
"bleControllerTimer", | ||
pdMS_TO_TICKS(CONTROLLER_TIMER_DELAY_MILLIS), | ||
pdTRUE, | ||
(void*)this, | ||
Controller::controllerTimerCallback | ||
); | ||
if (controllerTimer != NULL) { | ||
xTimerStart(controllerTimer, 0); | ||
|
||
BleGamepadConfiguration cfg; | ||
cfg.setControllerType(CONTROLLER_TYPE_GAMEPAD); | ||
cfg.setAutoReport(false); | ||
cfg.setButtonCount(NUM_OF_BUTTONS); | ||
cfg.setIncludeStart(true); | ||
cfg.setIncludeSelect(true); | ||
cfg.setWhichAxes(false, false, false, false, false, false, false, false); | ||
cfg.setWhichSimulationControls(false, false, false, false, false); | ||
begin(&cfg); | ||
|
||
return active = true; | ||
} | ||
return active; | ||
} | ||
|
||
bool Controller::isActive() { | ||
return active; | ||
} | ||
|
||
void Controller::controllerTimerCallback(TimerHandle_t xTimer) { | ||
Controller* instance = static_cast<Controller*>(pvTimerGetTimerID(xTimer)); | ||
if (instance) { | ||
instance->updateControllerState(); | ||
} | ||
} | ||
|
||
void Controller::updateControllerState() { | ||
mainLoopFps.onStartFrame(); | ||
if (!isConnected()) { | ||
return; | ||
} | ||
bool needReport = false; | ||
if (checkBatteryLevelTimer.isTimeOnFirstCall()) { | ||
checkBatteryLevelTimer.go(); | ||
needReport |= updateBatteryLevel(); | ||
} | ||
needReport |= updateButtons(); | ||
if (needReport) { | ||
sendReport(); | ||
} | ||
mainLoopFps.onEndFrame(); | ||
if (DEBUG) { | ||
mainLoopFps.logEveryOneSecond(); | ||
} | ||
} | ||
|
||
bool Controller::updateBatteryLevel() { | ||
int newBatteryLevel = lilka::battery.readLevel(); | ||
if (batteryLevel == newBatteryLevel || newBatteryLevel < BATTERY_LEVEL_NIN || newBatteryLevel > BATTERY_LEVEL_MAX) { | ||
return false; | ||
} | ||
batteryLevel = newBatteryLevel; | ||
setBatteryLevel(batteryLevel); | ||
if (DEBUG) { | ||
lilka::serial_log("[%s] New battery level: %d", LOG_TAG, batteryLevel); | ||
} | ||
return true; | ||
} | ||
|
||
bool Controller::updateButtons() { | ||
lilka::State st = lilka::controller.getState(); | ||
bool needReport = st.any.justPressed || st.any.justReleased; | ||
if (st.up.justPressed || st.right.justPressed || st.down.justPressed || st.left.justPressed || st.up.justReleased || | ||
st.right.justReleased || st.down.justReleased || st.left.justReleased) { | ||
if (!st.up.pressed && !st.right.pressed && !st.down.pressed && !st.left.pressed) { | ||
setHat(DPAD_CENTERED); | ||
} else { | ||
if (st.up.pressed) { | ||
if (st.right.pressed) { | ||
setHat(DPAD_UP_RIGHT); | ||
} else if (st.left.pressed) { | ||
setHat(DPAD_UP_LEFT); | ||
} else { | ||
setHat(DPAD_UP); | ||
} | ||
} else if (st.down.pressed) { | ||
if (st.right.pressed) { | ||
setHat(DPAD_DOWN_RIGHT); | ||
} else if (st.left.pressed) { | ||
setHat(DPAD_DOWN_LEFT); | ||
} else { | ||
setHat(DPAD_DOWN); | ||
} | ||
} else if (st.right.pressed) { | ||
setHat(DPAD_RIGHT); | ||
} else { // st.left.pressed | ||
setHat(DPAD_LEFT); | ||
} | ||
} | ||
} | ||
if (st.a.justPressed) { | ||
press(BUTTON_A); | ||
} else if (st.a.justReleased) { | ||
release(BUTTON_A); | ||
} | ||
if (st.b.justPressed) { | ||
press(BUTTON_B); | ||
} else if (st.b.justReleased) { | ||
release(BUTTON_B); | ||
} | ||
if (st.c.justPressed) { | ||
press(BUTTON_C); | ||
} else if (st.c.justReleased) { | ||
release(BUTTON_C); | ||
} | ||
if (st.d.justPressed) { | ||
press(BUTTON_D); | ||
} else if (st.d.justReleased) { | ||
release(BUTTON_D); | ||
} | ||
if (st.select.justPressed) { | ||
pressSelect(); | ||
} else if (st.select.justReleased) { | ||
releaseSelect(); | ||
} | ||
if (st.start.justPressed) { | ||
pressStart(); | ||
} else if (st.start.justReleased) { | ||
releaseStart(); | ||
} | ||
return needReport; | ||
} | ||
|
||
void Controller::stop() { | ||
if (!active) { | ||
return; | ||
} | ||
lilka::serial_log("[%s] Stopping BLE", LOG_TAG); | ||
int waitTime = 100 / portTICK_PERIOD_MS; | ||
end(); | ||
if (controllerTimer != NULL) { | ||
xTimerStop(controllerTimer, pdFALSE); | ||
xTimerDelete(controllerTimer, 0); | ||
} | ||
vTaskDelay(waitTime); | ||
NimBLEDevice::stopAdvertising(); | ||
vTaskDelay(waitTime); | ||
std::list<NimBLEClient*>* clients = NimBLEDevice::getClientList(); | ||
for (auto it = clients->begin(); it != clients->end(); ++it) { | ||
NimBLEClient* client = *it; | ||
NimBLEDevice::deleteClient(client); | ||
} | ||
clients = nullptr; | ||
vTaskDelay(waitTime); | ||
NimBLEDevice::deinit(true); | ||
vTaskDelay(waitTime); | ||
esp_restart(); | ||
active = false; | ||
} | ||
|
||
Controller::~Controller() { | ||
stop(); | ||
} | ||
|
||
} // namespace ble_gamepad_app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#pragma once | ||
|
||
#include <Arduino.h> | ||
#include <BleGamepad.h> | ||
#include "fps.h" | ||
#include "timer.h" | ||
|
||
namespace ble_gamepad_app { | ||
|
||
class Controller : public BleGamepad { | ||
public: | ||
Controller(); | ||
~Controller(); | ||
bool start(); | ||
void stop(); | ||
static void controllerTimerCallback(TimerHandle_t xTimer); | ||
void updateControllerState(); | ||
bool isActive(); | ||
|
||
private: | ||
static constexpr const int NUM_OF_BUTTONS = 4; | ||
|
||
int batteryLevel; | ||
FPS mainLoopFps; | ||
TimerHandle_t controllerTimer; | ||
Timer checkBatteryLevelTimer; | ||
bool active; | ||
|
||
bool updateBatteryLevel(); | ||
bool updateButtons(); | ||
}; | ||
|
||
} // namespace ble_gamepad_app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#pragma once | ||
|
||
#define BLE_GAMEPAD_APP_NAME "BLE Геймпад" | ||
#define DEBUG false | ||
#define DEVICE_NAME "Lilka BLE Gamepad" | ||
#define UI_DELAY_MILLIS 68 // 15 fps | ||
#define CONTROLLER_TIMER_DELAY_MILLIS 34 // 30 fps | ||
#define LOG_TAG "BLE Gamepad" | ||
#define UI_FPS_WATCHER "VIEW LOOP" | ||
#define MAIN_LOOP_FPS_WATCHER "MAIN LOOP" | ||
#define ONE_SECOND 1000 | ||
#define ONE_MINUTE 60 * ONE_SECOND | ||
#define BATTERY_LEVEL_UNKNOWN -1 | ||
#define BATTERY_LEVEL_NIN 0 | ||
#define BATTERY_LEVEL_MAX 100 |
Oops, something went wrong.