Update apps
3
applications/external/4inrow/4inrow.c
vendored
@@ -4,6 +4,7 @@
|
||||
#include <input/input.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
static int matrix[6][7] = {0};
|
||||
static int cursorx = 3;
|
||||
@@ -234,6 +235,8 @@ int32_t four_in_row_app(void* p) {
|
||||
return 255;
|
||||
}
|
||||
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
|
||||
// Создаем новый view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
// Создаем callback отрисовки, без контекста
|
||||
|
||||
1
applications/external/4inrow/application.fam
vendored
@@ -7,7 +7,6 @@ App(
|
||||
"gui",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_icon="4inrow_10px.png",
|
||||
fap_category="Games",
|
||||
fap_author="leo-need-more-coffee",
|
||||
|
||||
@@ -6,6 +6,6 @@ App(
|
||||
stack_size=10 * 1024,
|
||||
fap_category="GPIO",
|
||||
fap_icon="mouse_10px.png",
|
||||
fap_version="0.6",
|
||||
fap_version="0.8",
|
||||
sources=["*.c", "*.cc"],
|
||||
)
|
||||
|
||||
329
applications/external/airmouse/tracking/main_loop.cc
vendored
@@ -12,178 +12,219 @@
|
||||
static const float CURSOR_SPEED = 1024.0 / (M_PI / 4);
|
||||
static const float STABILIZE_BIAS = 16.0;
|
||||
|
||||
float g_yaw = 0;
|
||||
float g_pitch = 0;
|
||||
float g_dYaw = 0;
|
||||
float g_dPitch = 0;
|
||||
bool firstRead = true;
|
||||
bool stabilize = true;
|
||||
CalibrationData calibration;
|
||||
cardboard::OrientationTracker tracker(10000000l); // 10 ms / 100 Hz
|
||||
uint64_t ippms, ippms2;
|
||||
class TrackingState {
|
||||
private:
|
||||
float yaw;
|
||||
float pitch;
|
||||
float dYaw;
|
||||
float dPitch;
|
||||
bool firstRead;
|
||||
bool stabilize;
|
||||
CalibrationData calibration;
|
||||
cardboard::OrientationTracker tracker;
|
||||
uint64_t ippus, ippus2;
|
||||
|
||||
static inline float clamp(float val)
|
||||
{
|
||||
while (val <= -M_PI) {
|
||||
val += 2 * M_PI;
|
||||
}
|
||||
while (val >= M_PI) {
|
||||
val -= 2 * M_PI;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline float highpass(float oldVal, float newVal)
|
||||
{
|
||||
if (!stabilize) {
|
||||
return newVal;
|
||||
}
|
||||
float delta = clamp(oldVal - newVal);
|
||||
float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0));
|
||||
return newVal + alpha * delta;
|
||||
}
|
||||
|
||||
void sendCurrentState(MouseMoveCallback mouse_move, void *context)
|
||||
{
|
||||
float dX = g_dYaw * CURSOR_SPEED;
|
||||
float dY = g_dPitch * CURSOR_SPEED;
|
||||
|
||||
// Scale the shift down to fit the protocol.
|
||||
if (dX > 127) {
|
||||
dY *= 127.0 / dX;
|
||||
dX = 127;
|
||||
}
|
||||
if (dX < -127) {
|
||||
dY *= -127.0 / dX;
|
||||
dX = -127;
|
||||
}
|
||||
if (dY > 127) {
|
||||
dX *= 127.0 / dY;
|
||||
dY = 127;
|
||||
}
|
||||
if (dY < -127) {
|
||||
dX *= -127.0 / dY;
|
||||
dY = -127;
|
||||
private:
|
||||
float clamp(float val) {
|
||||
while (val <= -M_PI) {
|
||||
val += 2 * M_PI;
|
||||
}
|
||||
while (val >= M_PI) {
|
||||
val -= 2 * M_PI;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
const int8_t x = (int8_t)std::floor(dX + 0.5);
|
||||
const int8_t y = (int8_t)std::floor(dY + 0.5);
|
||||
|
||||
mouse_move(x, y, context);
|
||||
|
||||
// Only subtract the part of the error that was already sent.
|
||||
if (x != 0) {
|
||||
g_dYaw -= x / CURSOR_SPEED;
|
||||
}
|
||||
if (y != 0) {
|
||||
g_dPitch -= y / CURSOR_SPEED;
|
||||
}
|
||||
}
|
||||
|
||||
void onOrientation(cardboard::Vector4& quaternion)
|
||||
{
|
||||
float q1 = quaternion[0]; // X * sin(T/2)
|
||||
float q2 = quaternion[1]; // Y * sin(T/2)
|
||||
float q3 = quaternion[2]; // Z * sin(T/2)
|
||||
float q0 = quaternion[3]; // cos(T/2)
|
||||
|
||||
float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3)));
|
||||
float pitch = std::asin(2 * (q0 * q1 + q2 * q3));
|
||||
// float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2)));
|
||||
|
||||
if (yaw == NAN || pitch == NAN) {
|
||||
// NaN case, skip it
|
||||
return;
|
||||
float highpass(float oldVal, float newVal) {
|
||||
if (!stabilize) {
|
||||
return newVal;
|
||||
}
|
||||
float delta = clamp(oldVal - newVal);
|
||||
float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0));
|
||||
return newVal + alpha * delta;
|
||||
}
|
||||
|
||||
if (firstRead) {
|
||||
g_yaw = yaw;
|
||||
g_pitch = pitch;
|
||||
firstRead = false;
|
||||
} else {
|
||||
const float newYaw = highpass(g_yaw, yaw);
|
||||
const float newPitch = highpass(g_pitch, pitch);
|
||||
void sendCurrentState(MouseMoveCallback mouse_move, void *context) {
|
||||
float dX = dYaw * CURSOR_SPEED;
|
||||
float dY = dPitch * CURSOR_SPEED;
|
||||
|
||||
float dYaw = clamp(g_yaw - newYaw);
|
||||
float dPitch = g_pitch - newPitch;
|
||||
g_yaw = newYaw;
|
||||
g_pitch = newPitch;
|
||||
// Scale the shift down to fit the protocol.
|
||||
if (dX > 127) {
|
||||
dY *= 127.0 / dX;
|
||||
dX = 127;
|
||||
}
|
||||
if (dX < -127) {
|
||||
dY *= -127.0 / dX;
|
||||
dX = -127;
|
||||
}
|
||||
if (dY > 127) {
|
||||
dX *= 127.0 / dY;
|
||||
dY = 127;
|
||||
}
|
||||
if (dY < -127) {
|
||||
dX *= -127.0 / dY;
|
||||
dY = -127;
|
||||
}
|
||||
|
||||
// Accumulate the error locally.
|
||||
g_dYaw += dYaw;
|
||||
g_dPitch += dPitch;
|
||||
const int8_t x = (int8_t)std::floor(dX + 0.5);
|
||||
const int8_t y = (int8_t)std::floor(dY + 0.5);
|
||||
|
||||
mouse_move(x, y, context);
|
||||
|
||||
// Only subtract the part of the error that was already sent.
|
||||
if (x != 0) {
|
||||
dYaw -= x / CURSOR_SPEED;
|
||||
}
|
||||
if (y != 0) {
|
||||
dPitch -= y / CURSOR_SPEED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onOrientation(cardboard::Vector4& quaternion) {
|
||||
float q1 = quaternion[0]; // X * sin(T/2)
|
||||
float q2 = quaternion[1]; // Y * sin(T/2)
|
||||
float q3 = quaternion[2]; // Z * sin(T/2)
|
||||
float q0 = quaternion[3]; // cos(T/2)
|
||||
|
||||
float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3)));
|
||||
float pitch = std::asin(2 * (q0 * q1 + q2 * q3));
|
||||
// float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2)));
|
||||
|
||||
if (yaw == NAN || pitch == NAN) {
|
||||
// NaN case, skip it
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstRead) {
|
||||
this->yaw = yaw;
|
||||
this->pitch = pitch;
|
||||
firstRead = false;
|
||||
} else {
|
||||
const float newYaw = highpass(this->yaw, yaw);
|
||||
const float newPitch = highpass(this->pitch, pitch);
|
||||
|
||||
float dYaw = clamp(this->yaw - newYaw);
|
||||
float dPitch = this->pitch - newPitch;
|
||||
this->yaw = newYaw;
|
||||
this->pitch = newPitch;
|
||||
|
||||
// Accumulate the error locally.
|
||||
this->dYaw += dYaw;
|
||||
this->dPitch += dPitch;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
TrackingState()
|
||||
: yaw(0)
|
||||
, pitch(0)
|
||||
, dYaw(0)
|
||||
, dPitch(0)
|
||||
, firstRead(true)
|
||||
, stabilize(true)
|
||||
, tracker(10000000l) { // 10 ms / 100 Hz
|
||||
ippus = furi_hal_cortex_instructions_per_microsecond();
|
||||
ippus2 = ippus / 2;
|
||||
}
|
||||
|
||||
void beginCalibration() {
|
||||
calibration.reset();
|
||||
}
|
||||
|
||||
bool stepCalibration() {
|
||||
if (calibration.isComplete())
|
||||
return true;
|
||||
|
||||
double vec[6];
|
||||
if (imu_read(vec) & GYR_DATA_READY) {
|
||||
cardboard::Vector3 data(vec[3], vec[4], vec[5]);
|
||||
furi_delay_ms(9); // Artificially limit to ~100Hz
|
||||
return calibration.add(data);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void saveCalibration() {
|
||||
CalibrationMedian store;
|
||||
cardboard::Vector3 median = calibration.getMedian();
|
||||
store.x = median[0];
|
||||
store.y = median[1];
|
||||
store.z = median[2];
|
||||
CALIBRATION_DATA_SAVE(&store);
|
||||
}
|
||||
|
||||
void loadCalibration() {
|
||||
CalibrationMedian store;
|
||||
cardboard::Vector3 median = calibration.getMedian();
|
||||
if (CALIBRATION_DATA_LOAD(&store)) {
|
||||
median[0] = store.x;
|
||||
median[1] = store.y;
|
||||
median[2] = store.z;
|
||||
}
|
||||
|
||||
tracker.SetCalibration(median);
|
||||
}
|
||||
|
||||
void beginTracking() {
|
||||
loadCalibration();
|
||||
tracker.Resume();
|
||||
}
|
||||
|
||||
void stepTracking(MouseMoveCallback mouse_move, void *context) {
|
||||
double vec[6];
|
||||
int ret = imu_read(vec);
|
||||
if (ret != 0) {
|
||||
uint64_t t = (DWT->CYCCNT * 1000llu + ippus2) / ippus;
|
||||
if (ret & ACC_DATA_READY) {
|
||||
cardboard::AccelerometerData adata
|
||||
= { .system_timestamp = t, .sensor_timestamp_ns = t,
|
||||
.data = cardboard::Vector3(vec[0], vec[1], vec[2]) };
|
||||
tracker.OnAccelerometerData(adata);
|
||||
}
|
||||
if (ret & GYR_DATA_READY) {
|
||||
cardboard::GyroscopeData gdata
|
||||
= { .system_timestamp = t, .sensor_timestamp_ns = t,
|
||||
.data = cardboard::Vector3(vec[3], vec[4], vec[5]) };
|
||||
cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata);
|
||||
onOrientation(pose);
|
||||
sendCurrentState(mouse_move, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stopTracking() {
|
||||
tracker.Pause();
|
||||
}
|
||||
};
|
||||
|
||||
static TrackingState g_state;
|
||||
|
||||
extern "C" {
|
||||
|
||||
void calibration_begin() {
|
||||
calibration.reset();
|
||||
g_state.beginCalibration();
|
||||
FURI_LOG_I(TAG, "Calibrating");
|
||||
}
|
||||
|
||||
bool calibration_step() {
|
||||
if (calibration.isComplete())
|
||||
return true;
|
||||
|
||||
double vec[6];
|
||||
if (imu_read(vec) & GYR_DATA_READY) {
|
||||
cardboard::Vector3 data(vec[3], vec[4], vec[5]);
|
||||
furi_delay_ms(9); // Artificially limit to ~100Hz
|
||||
return calibration.add(data);
|
||||
}
|
||||
|
||||
return false;
|
||||
return g_state.stepCalibration();
|
||||
}
|
||||
|
||||
void calibration_end() {
|
||||
CalibrationMedian store;
|
||||
cardboard::Vector3 median = calibration.getMedian();
|
||||
store.x = median[0];
|
||||
store.y = median[1];
|
||||
store.z = median[2];
|
||||
CALIBRATION_DATA_SAVE(&store);
|
||||
g_state.saveCalibration();
|
||||
}
|
||||
|
||||
void tracking_begin() {
|
||||
CalibrationMedian store;
|
||||
cardboard::Vector3 median = calibration.getMedian();
|
||||
if (CALIBRATION_DATA_LOAD(&store)) {
|
||||
median[0] = store.x;
|
||||
median[1] = store.y;
|
||||
median[2] = store.z;
|
||||
}
|
||||
|
||||
ippms = furi_hal_cortex_instructions_per_microsecond();
|
||||
ippms2 = ippms / 2;
|
||||
tracker.SetCalibration(median);
|
||||
tracker.Resume();
|
||||
g_state.beginTracking();
|
||||
}
|
||||
|
||||
void tracking_step(MouseMoveCallback mouse_move, void *context) {
|
||||
double vec[6];
|
||||
int ret = imu_read(vec);
|
||||
if (ret != 0) {
|
||||
uint64_t t = (DWT->CYCCNT * 1000llu + ippms2) / ippms;
|
||||
if (ret & ACC_DATA_READY) {
|
||||
cardboard::AccelerometerData adata
|
||||
= { .system_timestamp = t, .sensor_timestamp_ns = t,
|
||||
.data = cardboard::Vector3(vec[0], vec[1], vec[2]) };
|
||||
tracker.OnAccelerometerData(adata);
|
||||
}
|
||||
if (ret & GYR_DATA_READY) {
|
||||
cardboard::GyroscopeData gdata
|
||||
= { .system_timestamp = t, .sensor_timestamp_ns = t,
|
||||
.data = cardboard::Vector3(vec[3], vec[4], vec[5]) };
|
||||
cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata);
|
||||
onOrientation(pose);
|
||||
sendCurrentState(mouse_move, context);
|
||||
}
|
||||
}
|
||||
g_state.stepTracking(mouse_move, context);
|
||||
}
|
||||
|
||||
void tracking_end() {
|
||||
tracker.Pause();
|
||||
g_state.stopTracking();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ Vector4 OrientationTracker::OnGyroscopeData(const GyroscopeData& event)
|
||||
|
||||
sensor_fusion_->ProcessGyroscopeSample(data);
|
||||
|
||||
return OrientationTracker::GetPose(data.sensor_timestamp_ns + sampling_period_ns_);
|
||||
return GetPose(data.sensor_timestamp_ns + sampling_period_ns_);
|
||||
}
|
||||
|
||||
} // namespace cardboard
|
||||
|
||||
@@ -59,13 +59,14 @@ namespace {
|
||||
// angle = norm(a)
|
||||
// axis = a.normalized()
|
||||
// If norm(a) == 0, it returns an identity rotation.
|
||||
static inline Rotation RotationFromVector(const Vector3& a)
|
||||
static inline void RotationFromVector(const Vector3& a, Rotation& r)
|
||||
{
|
||||
const double norm_a = Length(a);
|
||||
if (norm_a < kEpsilon) {
|
||||
return Rotation::Identity();
|
||||
r = Rotation::Identity();
|
||||
return;
|
||||
}
|
||||
return Rotation::FromAxisAndAngle(a / norm_a, norm_a);
|
||||
r = Rotation::FromAxisAndAngle(a / norm_a, norm_a);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -199,7 +200,8 @@ void SensorFusionEkf::ComputeMeasurementJacobian()
|
||||
Vector3 delta = Vector3::Zero();
|
||||
delta[dof] = kFiniteDifferencingEpsilon;
|
||||
|
||||
const Rotation epsilon_rotation = RotationFromVector(delta);
|
||||
Rotation epsilon_rotation;
|
||||
RotationFromVector(delta, epsilon_rotation);
|
||||
const Vector3 delta_rotation
|
||||
= ComputeInnovation(epsilon_rotation * current_state_.sensor_from_start_rotation);
|
||||
|
||||
@@ -263,7 +265,8 @@ void SensorFusionEkf::ProcessAccelerometerSample(const AccelerometerData& sample
|
||||
* state_covariance_;
|
||||
|
||||
// Updates pose and associate covariance matrix.
|
||||
const Rotation rotation_from_state_update = RotationFromVector(state_update_);
|
||||
Rotation rotation_from_state_update;
|
||||
RotationFromVector(state_update_, rotation_from_state_update);
|
||||
|
||||
current_state_.sensor_from_start_rotation
|
||||
= rotation_from_state_update * current_state_.sensor_from_start_rotation;
|
||||
|
||||
94
applications/external/airmouse/views/bt_mouse.c
vendored
@@ -132,6 +132,11 @@ void bt_mouse_connection_status_changed_callback(BtStatus status, void* context)
|
||||
BtMouse* bt_mouse = context;
|
||||
|
||||
bt_mouse->connected = (status == BtStatusConnected);
|
||||
if(!bt_mouse->notifications) {
|
||||
tracking_end();
|
||||
return;
|
||||
}
|
||||
|
||||
if(bt_mouse->connected) {
|
||||
notification_internal_message(bt_mouse->notifications, &sequence_set_blue_255);
|
||||
tracking_begin();
|
||||
@@ -140,9 +145,6 @@ void bt_mouse_connection_status_changed_callback(BtStatus status, void* context)
|
||||
tracking_end();
|
||||
notification_internal_message(bt_mouse->notifications, &sequence_reset_blue);
|
||||
}
|
||||
|
||||
//with_view_model(
|
||||
// bt_mouse->view, void * model, { model->connected = connected; }, true);
|
||||
}
|
||||
|
||||
bool bt_mouse_move(int8_t dx, int8_t dy, void* context) {
|
||||
@@ -160,46 +162,6 @@ bool bt_mouse_move(int8_t dx, int8_t dy, void* context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void bt_mouse_enter_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BtMouse* bt_mouse = context;
|
||||
|
||||
bt_mouse->bt = furi_record_open(RECORD_BT);
|
||||
bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
bt_set_status_changed_callback(
|
||||
bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse);
|
||||
furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard));
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
|
||||
bool bt_mouse_custom_callback(uint32_t event, void* context) {
|
||||
UNUSED(event);
|
||||
furi_assert(context);
|
||||
BtMouse* bt_mouse = context;
|
||||
|
||||
tracking_step(bt_mouse_move, context);
|
||||
furi_delay_ms(3); // Magic! Removing this will break the buttons
|
||||
|
||||
view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void bt_mouse_exit_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BtMouse* bt_mouse = context;
|
||||
|
||||
tracking_end();
|
||||
notification_internal_message(bt_mouse->notifications, &sequence_reset_blue);
|
||||
|
||||
furi_hal_bt_stop_advertising();
|
||||
bt_set_profile(bt_mouse->bt, BtProfileSerial);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
bt_mouse->notifications = NULL;
|
||||
furi_record_close(RECORD_BT);
|
||||
bt_mouse->bt = NULL;
|
||||
}
|
||||
|
||||
static int8_t clamp(int t) {
|
||||
if(t < -128) {
|
||||
return -128;
|
||||
@@ -279,6 +241,50 @@ void bt_mouse_thread_stop(BtMouse* bt_mouse) {
|
||||
furi_thread_join(bt_mouse->thread);
|
||||
furi_thread_free(bt_mouse->thread);
|
||||
furi_mutex_free(bt_mouse->mutex);
|
||||
bt_mouse->mutex = NULL;
|
||||
bt_mouse->thread = NULL;
|
||||
}
|
||||
|
||||
void bt_mouse_enter_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BtMouse* bt_mouse = context;
|
||||
|
||||
bt_mouse->bt = furi_record_open(RECORD_BT);
|
||||
bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
bt_set_status_changed_callback(
|
||||
bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse);
|
||||
furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard));
|
||||
furi_hal_bt_start_advertising();
|
||||
bt_mouse_thread_start(bt_mouse);
|
||||
}
|
||||
|
||||
bool bt_mouse_custom_callback(uint32_t event, void* context) {
|
||||
UNUSED(event);
|
||||
furi_assert(context);
|
||||
BtMouse* bt_mouse = context;
|
||||
|
||||
tracking_step(bt_mouse_move, context);
|
||||
furi_delay_ms(3); // Magic! Removing this will break the buttons
|
||||
|
||||
view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void bt_mouse_exit_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BtMouse* bt_mouse = context;
|
||||
|
||||
bt_mouse_thread_stop(bt_mouse);
|
||||
tracking_end();
|
||||
notification_internal_message(bt_mouse->notifications, &sequence_reset_blue);
|
||||
|
||||
furi_hal_bt_stop_advertising();
|
||||
bt_set_profile(bt_mouse->bt, BtProfileSerial);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
bt_mouse->notifications = NULL;
|
||||
furi_record_close(RECORD_BT);
|
||||
bt_mouse->bt = NULL;
|
||||
}
|
||||
|
||||
BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) {
|
||||
@@ -293,13 +299,11 @@ BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) {
|
||||
view_set_enter_callback(bt_mouse->view, bt_mouse_enter_callback);
|
||||
view_set_custom_callback(bt_mouse->view, bt_mouse_custom_callback);
|
||||
view_set_exit_callback(bt_mouse->view, bt_mouse_exit_callback);
|
||||
bt_mouse_thread_start(bt_mouse);
|
||||
return bt_mouse;
|
||||
}
|
||||
|
||||
void bt_mouse_free(BtMouse* bt_mouse) {
|
||||
furi_assert(bt_mouse);
|
||||
bt_mouse_thread_stop(bt_mouse);
|
||||
view_free(bt_mouse->view);
|
||||
free(bt_mouse);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="arkanoid_game_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=20,
|
||||
fap_icon="arkanoid_10px.png",
|
||||
fap_category="Games",
|
||||
fap_author="@xMasterX & @gotnull",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <gui/view.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "Arkanoid"
|
||||
|
||||
@@ -397,6 +398,9 @@ int32_t arkanoid_game_app(void* p) {
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
// Call dolphin deed on game start
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
|
||||
GameEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_ASTEROIDS"],
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
order=50,
|
||||
fap_icon="appicon.png",
|
||||
fap_icon_assets="assets",
|
||||
fap_category="Games",
|
||||
|
||||
@@ -5,7 +5,8 @@ App(
|
||||
entry_point="avr_isp_app",
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=20,
|
||||
fap_description="Application for flashing AVR microcontrollers",
|
||||
fap_version="1.0",
|
||||
fap_icon="avr_app_icon_10x10.png",
|
||||
fap_category="GPIO",
|
||||
fap_icon_assets="images",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers/avr_isp_types.h"
|
||||
#include <avr_isp_icons.h>
|
||||
#include "avr_isp_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include "scenes/avr_isp_scene.h"
|
||||
#include <gui/gui.h>
|
||||
@@ -41,4 +42,4 @@ typedef struct {
|
||||
AvrIspError error;
|
||||
} AvrIspApp;
|
||||
|
||||
bool avr_isp_load_from_file(AvrIspApp* app);
|
||||
bool avr_isp_load_from_file(AvrIspApp* app);
|
||||
@@ -174,7 +174,7 @@ static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) {
|
||||
while((furi_get_tick() - starttime) < 30) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,7 +357,7 @@ uint8_t avr_isp_read_lock_byte(AvrIsp* instance) {
|
||||
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE);
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
data = 0x00;
|
||||
}
|
||||
return data;
|
||||
@@ -377,7 +377,7 @@ bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -392,7 +392,7 @@ uint8_t avr_isp_read_fuse_low(AvrIsp* instance) {
|
||||
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW);
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
data = 0x00;
|
||||
}
|
||||
return data;
|
||||
@@ -412,7 +412,7 @@ bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -427,7 +427,7 @@ uint8_t avr_isp_read_fuse_high(AvrIsp* instance) {
|
||||
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH);
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
data = 0x00;
|
||||
}
|
||||
return data;
|
||||
@@ -447,7 +447,7 @@ bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -462,7 +462,7 @@ uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) {
|
||||
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED);
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
data = 0x00;
|
||||
}
|
||||
return data;
|
||||
@@ -482,7 +482,7 @@ bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) {
|
||||
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -350,12 +350,12 @@ static void avr_isp_worker_rw_get_dump_flash(AvrIspWorkerRW* instance, const cha
|
||||
sizeof(data));
|
||||
flipper_i32hex_file_bin_to_i32hex_set_data(
|
||||
flipper_hex_flash, data, avr_isp_chip_arr[instance->chip_arr_ind].pagesize);
|
||||
//FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash));
|
||||
FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash));
|
||||
instance->progress_flash =
|
||||
(float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f);
|
||||
}
|
||||
flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_flash);
|
||||
//FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash));
|
||||
FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash));
|
||||
flipper_i32hex_file_close(flipper_hex_flash);
|
||||
instance->progress_flash = 1.0f;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ static uint8_t avr_isp_prog_getch(AvrIspProg* instance) {
|
||||
uint8_t data[1] = {0};
|
||||
while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) {
|
||||
if(instance->exit) break;
|
||||
}
|
||||
};
|
||||
return data[0];
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ static void avr_isp_prog_set_cfg(AvrIspProg* instance) {
|
||||
instance->cfg->lockbytes = instance->buff[6];
|
||||
instance->cfg->fusebytes = instance->buff[7];
|
||||
instance->cfg->flashpoll = instance->buff[8];
|
||||
// ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as <EFBFBD>flashpoll<EFBFBD>
|
||||
// ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as “flashpoll”
|
||||
instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11];
|
||||
instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13];
|
||||
instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15];
|
||||
@@ -348,7 +348,7 @@ static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t dat
|
||||
while((furi_get_tick() - starttime) < 30) {
|
||||
if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "avr_isp_view_chip_detect.h"
|
||||
#include <avr_isp_icons.h>
|
||||
#include "avr_isp_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include "../helpers/avr_isp_worker_rw.h"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "avr_isp_view_programmer.h"
|
||||
#include <avr_isp_icons.h>
|
||||
#include "avr_isp_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include "../helpers/avr_isp_worker.h"
|
||||
#include <gui/elements.h>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "barcode_app.h"
|
||||
|
||||
#include "barcode_app_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
/**
|
||||
* Opens a file browser dialog and returns the filepath of the selected file
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="blackjack_app",
|
||||
requires=["gui", "storage", "canvas"],
|
||||
stack_size=2 * 1024,
|
||||
order=30,
|
||||
fap_icon="blackjack_10px.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
|
||||
8
applications/external/blackjack/blackjack.c
vendored
@@ -1,6 +1,7 @@
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <stdlib.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <gui/canvas_i.h>
|
||||
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "ui.h"
|
||||
|
||||
#include "blackjack_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define DEALER_MAX 17
|
||||
|
||||
@@ -275,6 +277,7 @@ void dealer_tick(GameState* game_state) {
|
||||
|
||||
if(dealer_score >= DEALER_MAX) {
|
||||
if(dealer_score > 21 || dealer_score < player_score) {
|
||||
dolphin_deed(DolphinDeedPluginGameWin);
|
||||
enqueue(
|
||||
&(game_state->queue_state),
|
||||
game_state,
|
||||
@@ -568,6 +571,9 @@ int32_t blackjack_app(void* p) {
|
||||
|
||||
AppEvent event;
|
||||
|
||||
// Call dolphin deed on game start
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
furi_mutex_acquire(game_state->mutex, FuriWaitForever);
|
||||
@@ -625,4 +631,4 @@ free_and_exit:
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
return return_code;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ App(
|
||||
"gui",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_icon="bomb.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "bomberduck_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
int max(int a, int b) {
|
||||
return (a > b) ? a : b;
|
||||
@@ -381,6 +383,7 @@ int32_t bomberduck_app(void* p) {
|
||||
return 255;
|
||||
}
|
||||
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
// Создаем новый view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
// Создаем callback отрисовки, без контекста
|
||||
@@ -454,8 +457,9 @@ int32_t bomberduck_app(void* p) {
|
||||
notification_message(notification, &end);
|
||||
world.running = 0;
|
||||
world.level += 1;
|
||||
// if(world.level % 5 == 0) {
|
||||
// }
|
||||
if(world.level % 5 == 0) {
|
||||
dolphin_deed(DolphinDeedPluginGameWin);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < world.bombs_count; i++) {
|
||||
if(furi_get_tick() - world.bombs[i].planted >
|
||||
|
||||
@@ -9,7 +9,6 @@ App(
|
||||
fap_icon="bpm_10px.png",
|
||||
fap_category="Media",
|
||||
fap_icon_assets="icons",
|
||||
order=15,
|
||||
fap_author="@panki27",
|
||||
fap_weburl="https://github.com/panki27/bpm-tapper",
|
||||
fap_version="1.0",
|
||||
|
||||
@@ -10,7 +10,6 @@ App(
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="caesar_cipher_icon.png",
|
||||
fap_category="Tools",
|
||||
order=20,
|
||||
fap_author="@panki27",
|
||||
fap_weburl="https://github.com/panki27/caesar-cipher",
|
||||
fap_version="1.0",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_CALCULATOR"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=45,
|
||||
fap_icon="calcIcon.png",
|
||||
fap_category="Tools",
|
||||
fap_author="@n-o-T-I-n-s-a-n-e",
|
||||
|
||||
@@ -9,7 +9,6 @@ App(
|
||||
fap_icon="icons/camera_suite.png",
|
||||
fap_weburl="https://github.com/CodyTolene/Flipper-Zero-Cam",
|
||||
name="[ESP32] Camera Suite",
|
||||
order=1,
|
||||
requires=["gui", "storage"],
|
||||
stack_size=8 * 1024,
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <furi_hal.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
#include "../helpers/camera_suite_haptic.h"
|
||||
#include "../helpers/camera_suite_speaker.h"
|
||||
#include "../helpers/camera_suite_led.h"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <furi_hal.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
struct CameraSuiteViewGuide {
|
||||
View* view;
|
||||
|
||||
1
applications/external/chess/application.fam
vendored
@@ -7,7 +7,6 @@ App(
|
||||
"gui",
|
||||
],
|
||||
stack_size=4 * 1024,
|
||||
order=10,
|
||||
fap_icon="flipchess_10px.png",
|
||||
fap_icon_assets="icons",
|
||||
fap_icon_assets_symbol="flipchess",
|
||||
|
||||
|
Before Width: | Height: | Size: 446 B |
@@ -4,6 +4,7 @@
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include "flipchess_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
struct FlipChessStartscreen {
|
||||
View* view;
|
||||
|
||||
@@ -10,7 +10,6 @@ App(
|
||||
"gui",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
fap_icon="cntdown_timer.png",
|
||||
fap_category="Tools",
|
||||
fap_author="@0w0mewo",
|
||||
|
||||
@@ -8,7 +8,8 @@ App(
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=4 * 1024,
|
||||
order=20,
|
||||
fap_description="Enables use of Flipper as a debug probe for ARM devices, implements the CMSIS-DAP protocol",
|
||||
fap_version="1.0",
|
||||
fap_icon="dap_link.png",
|
||||
fap_category="GPIO",
|
||||
fap_private_libs=[
|
||||
|
||||
4
applications/external/dap_link/dap_link.c
vendored
@@ -15,7 +15,7 @@
|
||||
#include "usb/dap_v2_usb.h"
|
||||
#include <dialogs/dialogs.h>
|
||||
#include "dap_link_icons.h"
|
||||
#include "assets_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
/***************************************************************************/
|
||||
/****************************** DAP COMMON *********************************/
|
||||
@@ -525,4 +525,4 @@ int32_t dap_link_app(void* p) {
|
||||
dap_app_free(app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "dap_main_view.h"
|
||||
#include "dap_link_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
// extern const Icon I_ArrowDownEmpty_12x18;
|
||||
|
||||
4
applications/external/doom/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
dist/*
|
||||
.vscode
|
||||
.clang-format
|
||||
.editorconfig
|
||||
1
applications/external/doom/application.fam
vendored
@@ -8,7 +8,6 @@ App(
|
||||
"music_player",
|
||||
],
|
||||
stack_size=4 * 1024,
|
||||
order=75,
|
||||
fap_icon="doom_10px.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
|
||||
3
applications/external/doom/display.h
vendored
@@ -1,7 +1,8 @@
|
||||
#include <gui/gui.h>
|
||||
#include <furi_hal.h>
|
||||
#include "constants.h"
|
||||
#include <doom_icons.h>
|
||||
#include "doom_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include "assets.h"
|
||||
|
||||
#define CHECK_BIT(var, pos) ((var) & (1 << (pos)))
|
||||
|
||||
10
applications/external/doom/doom.c
vendored
@@ -13,6 +13,7 @@
|
||||
#include "level.h"
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define SOUND
|
||||
|
||||
@@ -156,9 +157,6 @@ void spawnEntity(uint8_t type, uint8_t x, uint8_t y, PluginState* const plugin_s
|
||||
plugin_state->entity[plugin_state->num_entities] = create_medikit(x, y);
|
||||
plugin_state->num_entities++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,9 +460,6 @@ void updateEntities(const uint8_t level[], Canvas* const canvas, PluginState* co
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
@@ -993,6 +988,9 @@ int32_t doom_app() {
|
||||
music_player_worker_load_rtttl_from_string(plugin_state->music_instance->worker, dsintro);
|
||||
music_player_worker_start(plugin_state->music_instance->worker);
|
||||
#endif
|
||||
// Call dolphin deed on game start
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
|
||||
|
||||
@@ -393,7 +393,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const
|
||||
if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
FURI_LOG_E(TAG, "Unable to open file");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
uint16_t ret = 0;
|
||||
do {
|
||||
|
||||
@@ -10,9 +10,8 @@ App(
|
||||
],
|
||||
fap_icon="phone.png",
|
||||
stack_size=8 * 1024,
|
||||
order=20,
|
||||
fap_category="Tools",
|
||||
fap_author="@litui & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_version="1.1",
|
||||
fap_description="DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.",
|
||||
)
|
||||
|
||||
@@ -86,4 +86,4 @@ int32_t dtmf_dolphin_app(void* p) {
|
||||
|
||||
app_free(app);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
BIN
applications/external/dtmf_dolphin/phone.png
vendored
|
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 8.5 KiB |
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_CAMERA"],
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
order=1,
|
||||
fap_icon="icon.png",
|
||||
fap_category="GPIO",
|
||||
fap_description="ESP32-CAM live feed and photo capture, use left/right for orientation/mode, up/down for brightness and center for saving a screenshot. [Unplug the USB cable to test with Mayhem]",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_EVIL_PORTAL"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_icon="icons/evil_portal_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_description="ESP32-CAM evil portal. When users try to connect to this access point they will be served a fake login screen. User credentials are sent to the Flipper and logged on the SD card. [Unplug the USB cable to test with Mayhem]",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_WIFI_MARAUDER"],
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=2,
|
||||
fap_icon="wifi_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_description="ESP32-CAM version of Marauder. Includes all functionality from the original plus some options to trigger the camera and flashlight. [Unplug the USB cable to test with Mayhem]",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_UART_TERMINAL"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_icon_assets="assets",
|
||||
fap_icon="icon.png",
|
||||
fap_category="GPIO",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_QRCODE"],
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
order=1,
|
||||
fap_icon="icon.png",
|
||||
fap_category="GPIO",
|
||||
fap_description="ESP32-CAM Motion detection. It generates a beep when motion is detected. Can be extended to trigger more stuff in the code. [Unplug the USB cable to test with Mayhem]",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_QRCODE"],
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
order=1,
|
||||
fap_icon="icon.png",
|
||||
fap_category="GPIO",
|
||||
fap_description="ESP32-CAM simple app to start a remote camera. [Unplug the USB cable to test with Mayhem]",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_QRCODE"],
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
order=1,
|
||||
fap_icon="icon.png",
|
||||
fap_category="GPIO",
|
||||
fap_description="ESP32-CAM simple app to show a payload from QR codes. Can be extended to trigger more stuff in the code. [Unplug the USB cable to test with Mayhem]",
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="esp8266_deauth_app",
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=100,
|
||||
fap_icon="wifi_10px.png",
|
||||
fap_category="WiFi",
|
||||
fap_author="@SequoiaSan & @xMasterX",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
entry_point="esp_flasher_app",
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=90,
|
||||
fap_icon="update_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_private_libs=[
|
||||
|
||||
BIN
applications/external/esubghz_chat/assets/Nfc_14px.png
vendored
Normal file
|
After Width: | Height: | Size: 159 B |
BIN
applications/external/esubghz_chat/assets/chat_14px.png
vendored
Normal file
|
After Width: | Height: | Size: 195 B |
|
Before Width: | Height: | Size: 171 B |
BIN
applications/external/esubghz_chat/assets/hex_14px.png
vendored
Normal file
|
After Width: | Height: | Size: 149 B |
BIN
applications/external/esubghz_chat/assets/keyboard_14px.png
vendored
Normal file
|
After Width: | Height: | Size: 146 B |
BIN
applications/external/esubghz_chat/assets/u2f_14px.png
vendored
Normal file
|
After Width: | Height: | Size: 180 B |
@@ -192,10 +192,46 @@ bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, u
|
||||
TAG_BYTES) == 0);
|
||||
#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
|
||||
|
||||
// increase internal counter
|
||||
// update replay dict and increase internal counter
|
||||
if(ret) {
|
||||
ESubGhzChatReplayDict_set_at(ctx->replay_dict, ctx->run_id, ctx->counter);
|
||||
ctx->counter++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t crypto_ctx_dump_replay_dict(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
CryptoCtxReplayDictWriter writer,
|
||||
void* writer_ctx) {
|
||||
size_t ret = 0;
|
||||
ESubGhzChatReplayDict_it_t i;
|
||||
|
||||
for(ESubGhzChatReplayDict_it(i, ctx->replay_dict); !ESubGhzChatReplayDict_end_p(i);
|
||||
ESubGhzChatReplayDict_next(i), ret++) {
|
||||
ESubGhzChatReplayDict_itref_t* ref = ESubGhzChatReplayDict_ref(i);
|
||||
if(!writer(ref->key, ref->value, writer_ctx)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t crypto_ctx_read_replay_dict(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
CryptoCtxReplayDictReader reader,
|
||||
void* reader_ctx) {
|
||||
size_t ret = 0;
|
||||
|
||||
uint64_t run_id;
|
||||
uint32_t counter;
|
||||
|
||||
while(reader(&run_id, &counter, reader_ctx)) {
|
||||
ESubGhzChatReplayDict_set_at(ctx->replay_dict, run_id, counter);
|
||||
ret++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,18 @@ void crypto_ctx_get_key(ESubGhzChatCryptoCtx* ctx, uint8_t* key);
|
||||
bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out);
|
||||
bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out);
|
||||
|
||||
typedef bool (*CryptoCtxReplayDictWriter)(uint64_t run_id, uint32_t counter, void* context);
|
||||
typedef bool (*CryptoCtxReplayDictReader)(uint64_t* run_id, uint32_t* counter, void* context);
|
||||
|
||||
size_t crypto_ctx_dump_replay_dict(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
CryptoCtxReplayDictWriter writer,
|
||||
void* writer_ctx);
|
||||
size_t crypto_ctx_read_replay_dict(
|
||||
ESubGhzChatCryptoCtx* ctx,
|
||||
CryptoCtxReplayDictReader reader,
|
||||
void* reader_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
#include <gui/gui.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
#include "helpers/radio_device_loader.h"
|
||||
#include "esubghz_chat_i.h"
|
||||
|
||||
#define CHAT_LEAVE_DELAY 10
|
||||
#define TICK_INTERVAL 50
|
||||
#define MESSAGE_COMPLETION_TIMEOUT 500
|
||||
#define TIMEOUT_BETWEEN_MESSAGES 500
|
||||
|
||||
#define KBD_UNLOCK_CNT 3
|
||||
#define KBD_UNLOCK_TIMEOUT 1000
|
||||
@@ -125,15 +124,23 @@ void tx_msg_input(ESubGhzChatState* state) {
|
||||
subghz_tx_rx_worker_write(state->subghz_worker, state->tx_buffer, tx_size);
|
||||
}
|
||||
|
||||
/* Displays whether or not encryption has been enabled in the text box. Also
|
||||
* clears the text input buffer to remove the password and starts the Sub-GHz
|
||||
* worker. After starting the worker a join message is transmitted. */
|
||||
/* Displays information on frequency, encryption and radio type in the text
|
||||
* box. Also clears the text input buffer to remove the password and starts the
|
||||
* Sub-GHz worker. After starting the worker a join message is transmitted. */
|
||||
void enter_chat(ESubGhzChatState* state) {
|
||||
furi_string_cat_printf(state->chat_box_store, "Frequency: %lu", state->frequency);
|
||||
|
||||
furi_string_cat_printf(
|
||||
state->chat_box_store, "\nEncrypted: %s", (state->encrypted ? "yes" : "no"));
|
||||
|
||||
subghz_tx_rx_worker_start(state->subghz_worker, state->subghz_device, state->frequency);
|
||||
|
||||
if(strcmp(state->subghz_device->name, "cc1101_ext") == 0) {
|
||||
furi_string_cat_printf(state->chat_box_store, "\nRadio: External");
|
||||
} else {
|
||||
furi_string_cat_printf(state->chat_box_store, "\nRadio: Internal");
|
||||
}
|
||||
|
||||
/* concatenate the name prefix and join message */
|
||||
furi_string_set(state->msg_input, state->name_prefix);
|
||||
furi_string_cat_str(state->msg_input, " joined chat.");
|
||||
@@ -523,6 +530,9 @@ int32_t esubghz_chat(void) {
|
||||
goto err_alloc_crypto;
|
||||
}
|
||||
|
||||
/* set the default frequency */
|
||||
state->frequency = DEFAULT_FREQ;
|
||||
|
||||
/* set the have_read callback of the Sub-GHz worker */
|
||||
subghz_tx_rx_worker_set_callback_have_read(state->subghz_worker, have_read_cb, state);
|
||||
|
||||
@@ -531,7 +541,12 @@ int32_t esubghz_chat(void) {
|
||||
|
||||
/* init internal device */
|
||||
subghz_devices_init();
|
||||
state->subghz_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
|
||||
state->subghz_device =
|
||||
radio_device_loader_set(state->subghz_device, SubGhzRadioDeviceTypeExternalCC1101);
|
||||
|
||||
subghz_devices_reset(state->subghz_device);
|
||||
subghz_devices_idle(state->subghz_device);
|
||||
|
||||
/* set chat name prefix */
|
||||
furi_string_printf(state->name_prefix, "%s", furi_hal_version_get_name_ptr());
|
||||
@@ -582,8 +597,8 @@ int32_t esubghz_chat(void) {
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
/* switch to the frequency input scene */
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
/* switch to the key menu scene */
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyMenu);
|
||||
|
||||
/* run the view dispatcher, this call only returns when we close the
|
||||
* application */
|
||||
@@ -627,6 +642,8 @@ int32_t esubghz_chat(void) {
|
||||
crypto_explicit_bzero(state->nfc_dev_data, sizeof(NfcDeviceData));
|
||||
|
||||
/* deinit devices */
|
||||
radio_device_loader_end(state->subghz_device);
|
||||
|
||||
subghz_devices_deinit();
|
||||
|
||||
/* exit suppress charge mode */
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
#include "crypto_wrapper.h"
|
||||
#include "scenes/esubghz_chat_scene.h"
|
||||
|
||||
#include <assets_icons.h>
|
||||
#include "esubghz_chat_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define APPLICATION_NAME "ESubGhzChat"
|
||||
|
||||
|
||||
25
applications/external/esubghz_chat/helpers/nfc_helpers.h
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NFC_MAX_BYTES 256
|
||||
#define NFC_CONFIG_PAGES 4
|
||||
|
||||
struct FreqNfcEntry {
|
||||
uint32_t frequency;
|
||||
uint32_t unused1;
|
||||
uint32_t unused2;
|
||||
uint32_t unused3;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ReplayDictNfcEntry {
|
||||
uint64_t run_id;
|
||||
uint32_t counter;
|
||||
uint32_t unused;
|
||||
} __attribute__((packed));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
65
applications/external/esubghz_chat/helpers/radio_device_loader.c
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "radio_device_loader.h"
|
||||
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
static void radio_device_loader_power_on() {
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
//CC1101 power-up time
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void radio_device_loader_power_off() {
|
||||
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
bool radio_device_loader_is_connect_external(const char* name) {
|
||||
bool is_connect = false;
|
||||
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_on();
|
||||
}
|
||||
|
||||
const SubGhzDevice* device = subghz_devices_get_by_name(name);
|
||||
if(device) {
|
||||
is_connect = subghz_devices_is_connect(device);
|
||||
}
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_off();
|
||||
}
|
||||
return is_connect;
|
||||
}
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type) {
|
||||
const SubGhzDevice* radio_device;
|
||||
|
||||
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
|
||||
radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
|
||||
radio_device_loader_power_on();
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
|
||||
subghz_devices_begin(radio_device);
|
||||
} else if(current_radio_device == NULL) {
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
} else {
|
||||
radio_device_loader_end(current_radio_device);
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
}
|
||||
|
||||
return radio_device;
|
||||
}
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device) {
|
||||
furi_assert(radio_device);
|
||||
radio_device_loader_power_off();
|
||||
// Code below is not used (and will cause crash) since its called from tx_rx worker end!
|
||||
//if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) {
|
||||
// subghz_devices_end(radio_device);
|
||||
//}
|
||||
}
|
||||
15
applications/external/esubghz_chat/helpers/radio_device_loader.h
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
|
||||
/** SubGhzRadioDeviceType */
|
||||
typedef enum {
|
||||
SubGhzRadioDeviceTypeInternal,
|
||||
SubGhzRadioDeviceTypeExternalCC1101,
|
||||
} SubGhzRadioDeviceType;
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type);
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device);
|
||||
@@ -1,12 +1,11 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
/* Sends FreqEntered event to scene manager and displays the frequency in the
|
||||
* text box. */
|
||||
/* Sends FreqEntered event to scene manager and enters the chat. */
|
||||
static void freq_input_cb(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
furi_string_cat_printf(state->chat_box_store, "Frequency: %lu", state->frequency);
|
||||
enter_chat(state);
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_FreqEntered);
|
||||
}
|
||||
@@ -49,7 +48,7 @@ void scene_on_enter_freq_input(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
snprintf(state->text_input_store, TEXT_INPUT_STORE_SIZE, "%lu", (uint32_t)DEFAULT_FREQ);
|
||||
snprintf(state->text_input_store, TEXT_INPUT_STORE_SIZE, "%lu", state->frequency);
|
||||
text_input_reset(state->text_input);
|
||||
text_input_set_result_callback(
|
||||
state->text_input,
|
||||
@@ -76,9 +75,9 @@ bool scene_on_event_freq_input(void* context, SceneManagerEvent event) {
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to password input scene */
|
||||
/* switch to message input scene */
|
||||
case ESubGhzChatEvent_FreqEntered:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyMenu);
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
/* Sets the entered bytes as the key, enters the chat and sends a HexKeyEntered
|
||||
* event to the scene manager. */
|
||||
/* Sets the entered bytes as the key and sends a HexKeyEntered event to the
|
||||
* scene manager. */
|
||||
static void hex_key_input_cb(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
@@ -20,8 +20,6 @@ static void hex_key_input_cb(void* context) {
|
||||
|
||||
state->encrypted = true;
|
||||
|
||||
enter_chat(state);
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_HexKeyEntered);
|
||||
}
|
||||
|
||||
@@ -55,9 +53,9 @@ bool scene_on_event_hex_key_input(void* context, SceneManagerEvent event) {
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to message input scene */
|
||||
/* switch to frequency input scene */
|
||||
case ESubGhzChatEvent_HexKeyEntered:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput);
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ static void key_menu_cb(void* context, uint32_t index) {
|
||||
switch(index) {
|
||||
case ESubGhzChatKeyMenuItems_NoEncryption:
|
||||
state->encrypted = false;
|
||||
enter_chat(state);
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyMenuNoEncryption);
|
||||
@@ -49,7 +48,6 @@ static void key_menu_cb(void* context, uint32_t index) {
|
||||
|
||||
/* set encrypted flag and enter the chat */
|
||||
state->encrypted = true;
|
||||
enter_chat(state);
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyMenuGenKey);
|
||||
break;
|
||||
@@ -73,33 +71,37 @@ void scene_on_enter_key_menu(void* context) {
|
||||
|
||||
menu_reset(state->menu);
|
||||
|
||||
/* clear the crypto CTX in case we got back from password or hex key
|
||||
* input */
|
||||
crypto_ctx_clear(state->crypto_ctx);
|
||||
|
||||
menu_add_item(
|
||||
state->menu,
|
||||
"No encryption",
|
||||
&I_chat_10px,
|
||||
&I_chat_14px,
|
||||
ESubGhzChatKeyMenuItems_NoEncryption,
|
||||
key_menu_cb,
|
||||
state);
|
||||
menu_add_item(
|
||||
state->menu,
|
||||
"Password",
|
||||
&I_keyboard_10px,
|
||||
&I_keyboard_14px,
|
||||
ESubGhzChatKeyMenuItems_Password,
|
||||
key_menu_cb,
|
||||
state);
|
||||
menu_add_item(
|
||||
state->menu, "Hex Key", &I_hex_10px, ESubGhzChatKeyMenuItems_HexKey, key_menu_cb, state);
|
||||
state->menu, "Hex Key", &I_hex_14px, ESubGhzChatKeyMenuItems_HexKey, key_menu_cb, state);
|
||||
menu_add_item(
|
||||
state->menu,
|
||||
"Generate Key",
|
||||
&I_u2f_10px,
|
||||
&I_u2f_14px,
|
||||
ESubGhzChatKeyMenuItems_GenKey,
|
||||
key_menu_cb,
|
||||
state);
|
||||
menu_add_item(
|
||||
state->menu,
|
||||
"Read Key from NFC",
|
||||
&I_Nfc_10px,
|
||||
&I_Nfc_14px,
|
||||
ESubGhzChatKeyMenuItems_ReadKeyFromNfc,
|
||||
key_menu_cb,
|
||||
state);
|
||||
@@ -119,10 +121,10 @@ bool scene_on_event_key_menu(void* context, SceneManagerEvent event) {
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to message input scene */
|
||||
/* switch to frequency input scene */
|
||||
case ESubGhzChatEvent_KeyMenuNoEncryption:
|
||||
case ESubGhzChatEvent_KeyMenuGenKey:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput);
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
#include "../helpers/nfc_helpers.h"
|
||||
|
||||
typedef enum {
|
||||
KeyReadPopupState_Idle,
|
||||
@@ -28,18 +29,46 @@ static void key_read_popup_timeout_cb(void* context) {
|
||||
if(cur_state == KeyReadPopupState_Fail) {
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyReadPopupFailed);
|
||||
/* done displaying our success, enter chat */
|
||||
/* done displaying our success */
|
||||
} else if(cur_state == KeyReadPopupState_Success) {
|
||||
enter_chat(state);
|
||||
view_dispatcher_send_custom_event(
|
||||
state->view_dispatcher, ESubGhzChatEvent_KeyReadPopupSucceeded);
|
||||
}
|
||||
}
|
||||
|
||||
struct ReplayDictNfcReaderContext {
|
||||
uint8_t* cur;
|
||||
uint8_t* max;
|
||||
};
|
||||
|
||||
static bool replay_dict_nfc_reader(uint64_t* run_id, uint32_t* counter, void* context) {
|
||||
struct ReplayDictNfcReaderContext* ctx = (struct ReplayDictNfcReaderContext*)context;
|
||||
|
||||
if(ctx->cur + sizeof(struct ReplayDictNfcEntry) > ctx->max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ReplayDictNfcEntry* entry = (struct ReplayDictNfcEntry*)ctx->cur;
|
||||
*run_id = entry->run_id;
|
||||
*counter = __ntohl(entry->counter);
|
||||
|
||||
ctx->cur += sizeof(struct ReplayDictNfcEntry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool key_read_popup_handle_key_read(ESubGhzChatState* state) {
|
||||
NfcDeviceData* dev_data = state->nfc_dev_data;
|
||||
|
||||
if(dev_data->mf_ul_data.data_read < KEY_BITS / 8) {
|
||||
/* check for config pages */
|
||||
if(dev_data->mf_ul_data.data_read < NFC_CONFIG_PAGES * 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t data_read = dev_data->mf_ul_data.data_read - (NFC_CONFIG_PAGES * 4);
|
||||
|
||||
/* check if key was transmitted */
|
||||
if(data_read < KEY_BITS / 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,6 +84,21 @@ static bool key_read_popup_handle_key_read(ESubGhzChatState* state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* read the frequency */
|
||||
if(data_read >= (KEY_BITS / 8) + sizeof(struct FreqNfcEntry)) {
|
||||
struct FreqNfcEntry* freq_entry =
|
||||
(struct FreqNfcEntry*)(dev_data->mf_ul_data.data + (KEY_BITS / 8));
|
||||
state->frequency = __ntohl(freq_entry->frequency);
|
||||
}
|
||||
|
||||
/* read the replay dict */
|
||||
struct ReplayDictNfcReaderContext rd_ctx = {
|
||||
.cur = dev_data->mf_ul_data.data + (KEY_BITS / 8) + sizeof(struct FreqNfcEntry),
|
||||
.max =
|
||||
dev_data->mf_ul_data.data + (data_read < NFC_MAX_BYTES ? data_read : NFC_MAX_BYTES)};
|
||||
|
||||
crypto_ctx_read_replay_dict(state->crypto_ctx, replay_dict_nfc_reader, &rd_ctx);
|
||||
|
||||
/* set encrypted flag */
|
||||
state->encrypted = true;
|
||||
|
||||
@@ -183,9 +227,9 @@ bool scene_on_event_key_read_popup(void* context, SceneManagerEvent event) {
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
/* success, go to chat input */
|
||||
/* success, go to frequency input */
|
||||
case ESubGhzChatEvent_KeyReadPopupSucceeded:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput);
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
#include "../helpers/nfc_helpers.h"
|
||||
|
||||
struct ReplayDictNfcWriterContext {
|
||||
uint8_t* cur;
|
||||
uint8_t* max;
|
||||
};
|
||||
|
||||
static bool replay_dict_nfc_writer(uint64_t run_id, uint32_t counter, void* context) {
|
||||
struct ReplayDictNfcWriterContext* ctx = (struct ReplayDictNfcWriterContext*)context;
|
||||
|
||||
struct ReplayDictNfcEntry entry = {.run_id = run_id, .counter = __htonl(counter), .unused = 0};
|
||||
|
||||
if(ctx->cur + sizeof(entry) > ctx->max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ctx->cur, &entry, sizeof(entry));
|
||||
ctx->cur += sizeof(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prepare_nfc_dev_data(ESubGhzChatState* state) {
|
||||
NfcDeviceData* dev_data = state->nfc_dev_data;
|
||||
@@ -20,9 +41,32 @@ static void prepare_nfc_dev_data(ESubGhzChatState* state) {
|
||||
dev_data->mf_ul_data.version.storage_size = 0x11;
|
||||
dev_data->mf_ul_data.version.protocol_type = 0x03;
|
||||
|
||||
/* Add 16 to the size for config pages */
|
||||
dev_data->mf_ul_data.data_size = (KEY_BITS / 8) + 16;
|
||||
size_t data_written = 0;
|
||||
|
||||
/* write key */
|
||||
crypto_ctx_get_key(state->crypto_ctx, dev_data->mf_ul_data.data);
|
||||
data_written += (KEY_BITS / 8);
|
||||
|
||||
/* write frequency */
|
||||
struct FreqNfcEntry* freq_entry =
|
||||
(struct FreqNfcEntry*)(dev_data->mf_ul_data.data + data_written);
|
||||
freq_entry->frequency = __htonl(state->frequency);
|
||||
freq_entry->unused1 = 0;
|
||||
freq_entry->unused2 = 0;
|
||||
freq_entry->unused3 = 0;
|
||||
data_written += sizeof(struct FreqNfcEntry);
|
||||
|
||||
/* write the replay dict */
|
||||
struct ReplayDictNfcWriterContext wr_ctx = {
|
||||
.cur = dev_data->mf_ul_data.data + data_written,
|
||||
.max = dev_data->mf_ul_data.data + NFC_MAX_BYTES};
|
||||
|
||||
size_t n_entries =
|
||||
crypto_ctx_dump_replay_dict(state->crypto_ctx, replay_dict_nfc_writer, &wr_ctx);
|
||||
data_written += n_entries * sizeof(struct ReplayDictNfcEntry);
|
||||
|
||||
/* calculate size of data, add 16 for config pages */
|
||||
dev_data->mf_ul_data.data_size = data_written + (NFC_CONFIG_PAGES * 4);
|
||||
}
|
||||
|
||||
/* Prepares the key share popup scene. */
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#include "../esubghz_chat_i.h"
|
||||
|
||||
/* Sends PassEntered event to scene manager and enters the chat. */
|
||||
/* Sends PassEntered event to scene manager. */
|
||||
static void pass_input_cb(void* context) {
|
||||
furi_assert(context);
|
||||
ESubGhzChatState* state = context;
|
||||
|
||||
crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store));
|
||||
|
||||
enter_chat(state);
|
||||
|
||||
view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_PassEntered);
|
||||
}
|
||||
|
||||
@@ -83,9 +81,9 @@ bool scene_on_event_pass_input(void* context, SceneManagerEvent event) {
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
switch(event.event) {
|
||||
/* switch to message input scene */
|
||||
/* switch to frequency input scene */
|
||||
case ESubGhzChatEvent_PassEntered:
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput);
|
||||
scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
cdefines=["APP_ETCH_A_SKETCH"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=175,
|
||||
fap_icon="etch-a-sketch-icon.png",
|
||||
fap_category="Media",
|
||||
fap_icon_assets="assets",
|
||||
|
||||
21
applications/external/evil_portal/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 bigbrodude6119
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -6,7 +6,9 @@ App(
|
||||
cdefines=["APP_EVIL_PORTAL"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_author="bigbrodude6119",
|
||||
fap_description="Create an evil captive portal Wi-Fi access point",
|
||||
fap_icon_assets="icons",
|
||||
fap_icon="icons/evil_portal_10px.png",
|
||||
fap_category="WiFi",
|
||||
)
|
||||
|
||||
@@ -32,10 +32,15 @@ Evil_PortalApp* evil_portal_app_alloc() {
|
||||
app->command_index = 0;
|
||||
app->portal_logs = furi_string_alloc();
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
app->file_path = furi_string_alloc();
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
app->loading = loading_alloc();
|
||||
|
||||
app->scene_manager = scene_manager_alloc(&evil_portal_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
@@ -49,12 +54,18 @@ Evil_PortalApp* evil_portal_app_alloc() {
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->view_stack = view_stack_alloc();
|
||||
|
||||
app->var_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
Evil_PortalAppViewVarItemList,
|
||||
variable_item_list_get_view(app->var_item_list));
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, Evil_PortalAppViewTextInput, text_input_get_view(app->text_input));
|
||||
|
||||
for(int i = 0; i < NUM_MENU_ITEMS; ++i) {
|
||||
app->selected_option_index[i] = 0;
|
||||
}
|
||||
@@ -89,6 +100,10 @@ void evil_portal_app_free(Evil_PortalApp* app) {
|
||||
|
||||
text_box_free(app->text_box);
|
||||
furi_string_free(app->text_box_store);
|
||||
text_input_free(app->text_input);
|
||||
|
||||
view_stack_free(app->view_stack);
|
||||
loading_free(app->loading);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
@@ -98,36 +113,32 @@ void evil_portal_app_free(Evil_PortalApp* app) {
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
furi_string_free(app->file_path);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t evil_portal_app(void* p) {
|
||||
UNUSED(p);
|
||||
Evil_PortalApp* evil_portal_app = evil_portal_app_alloc();
|
||||
|
||||
// turn off 5v, so it gets reset on startup
|
||||
if(furi_hal_power_is_otg_enabled()) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
// Enable 5v on startup
|
||||
uint8_t attempts = 0;
|
||||
bool otg_was_enabled = furi_hal_power_is_otg_enabled();
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
furi_delay_ms(200);
|
||||
|
||||
Evil_PortalApp* evil_portal_app = evil_portal_app_alloc();
|
||||
|
||||
evil_portal_app->uart = evil_portal_uart_init(evil_portal_app);
|
||||
|
||||
view_dispatcher_run(evil_portal_app->view_dispatcher);
|
||||
|
||||
evil_portal_app_free(evil_portal_app);
|
||||
|
||||
if(furi_hal_power_is_otg_enabled()) {
|
||||
if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,4 @@ typedef struct Evil_PortalApp Evil_PortalApp;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -4,19 +4,22 @@
|
||||
#include "evil_portal_custom_event.h"
|
||||
#include "evil_portal_uart.h"
|
||||
#include "scenes/evil_portal_scene.h"
|
||||
#include "evil_portal_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/text_box.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
|
||||
#include <assets_icons.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <xtreme.h>
|
||||
|
||||
#define NUM_MENU_ITEMS (4)
|
||||
#define NUM_MENU_ITEMS (6)
|
||||
|
||||
#define EVIL_PORTAL_TEXT_BOX_STORE_SIZE (4096)
|
||||
#define UART_CH \
|
||||
@@ -27,14 +30,13 @@
|
||||
#define SET_AP_CMD "setap"
|
||||
#define RESET_CMD "reset"
|
||||
|
||||
#define EVIL_PORTAL_INDEX_EXTENSION ".html"
|
||||
#define EVIL_PORTAL_BASE_FOLDER STORAGE_APP_DATA_PATH_PREFIX
|
||||
#define HTML_EXTENSION ".html"
|
||||
#define HTML_FOLDER APP_DATA_PATH("html")
|
||||
|
||||
struct Evil_PortalApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
DialogsApp* dialogs;
|
||||
|
||||
FuriString* portal_logs;
|
||||
const char* command_queue[1];
|
||||
@@ -47,6 +49,11 @@ struct Evil_PortalApp {
|
||||
|
||||
VariableItemList* var_item_list;
|
||||
Evil_PortalUart* uart;
|
||||
TextInput* text_input;
|
||||
DialogsApp* dialogs;
|
||||
FuriString* file_path;
|
||||
Loading* loading;
|
||||
ViewStack* view_stack;
|
||||
|
||||
int selected_menu_index;
|
||||
int selected_option_index[NUM_MENU_ITEMS];
|
||||
@@ -59,6 +66,7 @@ struct Evil_PortalApp {
|
||||
bool sent_html;
|
||||
bool sent_reset;
|
||||
int BAUDRATE;
|
||||
char text_store[2][128 + 1];
|
||||
|
||||
uint8_t* index_html;
|
||||
uint8_t* ap_name;
|
||||
@@ -68,4 +76,5 @@ typedef enum {
|
||||
Evil_PortalAppViewVarItemList,
|
||||
Evil_PortalAppViewConsoleOutput,
|
||||
Evil_PortalAppViewStartPortal,
|
||||
Evil_PortalAppViewTextInput,
|
||||
} Evil_PortalAppView;
|
||||
|
||||
@@ -5,4 +5,5 @@ typedef enum {
|
||||
Evil_PortalEventStartConsole,
|
||||
Evil_PortalEventStartKeyboard,
|
||||
Evil_PortalEventStartPortal,
|
||||
Evil_PortalEventTextInput,
|
||||
} Evil_PortalCustomEvent;
|
||||
|
||||
@@ -8,31 +8,21 @@ static void evil_portal_close_storage() {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
bool evil_portal_read_index_html(void* context) {
|
||||
FuriString* file_path = furi_string_alloc_set(EVIL_PORTAL_BASE_FOLDER);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options,
|
||||
EVIL_PORTAL_INDEX_EXTENSION,
|
||||
NULL); // TODO configure icon
|
||||
browser_options.base_path = EVIL_PORTAL_BASE_FOLDER;
|
||||
|
||||
void evil_portal_read_index_html(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
bool res = dialog_file_browser_show(app->dialogs, file_path, file_path, &browser_options);
|
||||
|
||||
if(!res) {
|
||||
furi_string_free(file_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
FileInfo fi;
|
||||
|
||||
if(storage_common_stat(storage, furi_string_get_cstr(file_path), &fi) == FSE_OK) {
|
||||
if(!storage_common_exists(storage, EVIL_PORTAL_INDEX_SAVE_PATH)) {
|
||||
FuriString* tmp = furi_string_alloc_set(EVIL_PORTAL_INDEX_DEFAULT_PATH);
|
||||
evil_portal_replace_index_html(tmp);
|
||||
furi_string_free(tmp);
|
||||
}
|
||||
|
||||
if(storage_common_stat(storage, EVIL_PORTAL_INDEX_SAVE_PATH, &fi) == FSE_OK) {
|
||||
File* index_html = storage_file_alloc(storage);
|
||||
if(storage_file_open(
|
||||
index_html, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
index_html, EVIL_PORTAL_INDEX_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
app->index_html = malloc((size_t)fi.size);
|
||||
uint8_t* buf_ptr = app->index_html;
|
||||
size_t read = 0;
|
||||
@@ -45,7 +35,6 @@ bool evil_portal_read_index_html(void* context) {
|
||||
}
|
||||
free(buf_ptr);
|
||||
}
|
||||
furi_string_free(file_path);
|
||||
storage_file_close(index_html);
|
||||
storage_file_free(index_html);
|
||||
} else {
|
||||
@@ -57,7 +46,33 @@ bool evil_portal_read_index_html(void* context) {
|
||||
}
|
||||
|
||||
evil_portal_close_storage();
|
||||
return true;
|
||||
}
|
||||
|
||||
void evil_portal_replace_index_html(FuriString* path) {
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
FS_Error error;
|
||||
error = storage_common_remove(storage, EVIL_PORTAL_INDEX_SAVE_PATH);
|
||||
if(error != FSE_OK) {
|
||||
FURI_LOG_D("EVIL PORTAL", "Error removing file");
|
||||
} else {
|
||||
FURI_LOG_D("EVIL PORTAL", "Error removed file");
|
||||
}
|
||||
error = storage_common_copy(storage, furi_string_get_cstr(path), EVIL_PORTAL_INDEX_SAVE_PATH);
|
||||
if(error != FSE_OK) {
|
||||
FURI_LOG_D("EVIL PORTAL", "Error copying file");
|
||||
}
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
void evil_portal_create_html_folder_if_not_exists() {
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
if(storage_common_stat(storage, HTML_FOLDER, NULL) == FSE_NOT_EXIST) {
|
||||
FURI_LOG_D("Evil Portal", "Directory %s doesn't exist. Will create new.", HTML_FOLDER);
|
||||
if(!storage_simply_mkdir(storage, HTML_FOLDER)) {
|
||||
FURI_LOG_E("Evil Portal", "Error creating directory %s", HTML_FOLDER);
|
||||
}
|
||||
}
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
void evil_portal_read_ap_name(void* context) {
|
||||
@@ -89,6 +104,19 @@ void evil_portal_read_ap_name(void* context) {
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
void evil_portal_write_ap_name(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
Storage* storage = evil_portal_open_storage();
|
||||
|
||||
File* ap_name = storage_file_alloc(storage);
|
||||
if(storage_file_open(ap_name, EVIL_PORTAL_AP_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
storage_file_write(ap_name, app->text_store[0], strlen(app->text_store[0]));
|
||||
}
|
||||
storage_file_close(ap_name);
|
||||
storage_file_free(ap_name);
|
||||
evil_portal_close_storage();
|
||||
}
|
||||
|
||||
char* sequential_file_resolve_path(
|
||||
Storage* storage,
|
||||
const char* dir,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "../evil_portal_app_i.h"
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <lib/toolbox/stream/file_stream.h>
|
||||
#include <stdlib.h>
|
||||
@@ -8,11 +7,15 @@
|
||||
|
||||
#define PORTAL_FILE_DIRECTORY_PATH EXT_PATH("apps_data/evil_portal")
|
||||
#define EVIL_PORTAL_INDEX_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/index.html"
|
||||
#define EVIL_PORTAL_INDEX_DEFAULT_PATH PORTAL_FILE_DIRECTORY_PATH "/html/xtreme.html"
|
||||
#define EVIL_PORTAL_AP_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/ap.config.txt"
|
||||
#define EVIL_PORTAL_LOG_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/logs"
|
||||
|
||||
bool evil_portal_read_index_html(void* context);
|
||||
void evil_portal_read_index_html(void* context);
|
||||
void evil_portal_read_ap_name(void* context);
|
||||
void evil_portal_write_ap_name(void* context);
|
||||
void evil_portal_replace_index_html(FuriString* path);
|
||||
void evil_portal_create_html_folder_if_not_exists();
|
||||
void write_logs(FuriString* portal_logs);
|
||||
char* sequential_file_resolve_path(
|
||||
Storage* storage,
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
ADD_SCENE(evil_portal, start, Start)
|
||||
ADD_SCENE(evil_portal, console_output, ConsoleOutput)
|
||||
ADD_SCENE(evil_portal, rename, Rename)
|
||||
ADD_SCENE(evil_portal, select_html, SelectHtml)
|
||||
|
||||
@@ -22,8 +22,6 @@ void evil_portal_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void
|
||||
void evil_portal_scene_console_output_on_enter(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
|
||||
bool portal_file_set = false;
|
||||
|
||||
TextBox* text_box = app->text_box;
|
||||
text_box_reset(app->text_box);
|
||||
text_box_set_font(text_box, TextBoxFontText);
|
||||
@@ -64,25 +62,24 @@ void evil_portal_scene_console_output_on_enter(void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) {
|
||||
portal_file_set = evil_portal_read_index_html(context);
|
||||
if(0 == strncmp("setapname", app->selected_tx_string, strlen("setapname"))) {
|
||||
scene_manager_next_scene(app->scene_manager, Evil_PortalSceneRename);
|
||||
return;
|
||||
}
|
||||
|
||||
if(portal_file_set) {
|
||||
app->command_queue[0] = SET_AP_CMD;
|
||||
app->has_command_queue = true;
|
||||
app->command_index = 0;
|
||||
if(app->show_stopscan_tip) {
|
||||
const char* msg = "Starting portal\nIf no response press\nBACK to return\n";
|
||||
furi_string_cat_str(app->text_box_store, msg);
|
||||
app->text_box_store_strlen += strlen(msg);
|
||||
}
|
||||
} else {
|
||||
if(app->show_stopscan_tip) {
|
||||
const char* msg = "No portal selected\nShowing current logs\nPress "
|
||||
"BACK to return\n";
|
||||
furi_string_cat_str(app->text_box_store, msg);
|
||||
app->text_box_store_strlen += strlen(msg);
|
||||
}
|
||||
if(0 == strncmp("selecthtml", app->selected_tx_string, strlen("selecthtml"))) {
|
||||
scene_manager_next_scene(app->scene_manager, Evil_PortalSceneSelectHtml);
|
||||
return;
|
||||
}
|
||||
|
||||
if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) {
|
||||
app->command_queue[0] = SET_AP_CMD;
|
||||
app->has_command_queue = true;
|
||||
app->command_index = 0;
|
||||
if(app->show_stopscan_tip) {
|
||||
const char* msg = "Starting portal\nIf no response press\nBACK to return\n";
|
||||
furi_string_cat_str(app->text_box_store, msg);
|
||||
app->text_box_store_strlen += strlen(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,13 +104,7 @@ void evil_portal_scene_console_output_on_enter(void* context) {
|
||||
|
||||
if(app->is_command && app->selected_tx_string) {
|
||||
if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) {
|
||||
if(!portal_file_set) {
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, Evil_PortalSceneConsoleOutput, 0);
|
||||
view_dispatcher_switch_to_view(
|
||||
app->view_dispatcher, Evil_PortalAppViewConsoleOutput);
|
||||
return;
|
||||
}
|
||||
evil_portal_read_index_html(context);
|
||||
|
||||
FuriString* data = furi_string_alloc();
|
||||
furi_string_cat(data, "sethtml=");
|
||||
|
||||
42
applications/external/evil_portal/scenes/evil_portal_scene_rename.c
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "../evil_portal_app_i.h"
|
||||
#include "../helpers/evil_portal_storage.h"
|
||||
|
||||
void evil_portal_text_input_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Evil_PortalApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, Evil_PortalEventTextInput);
|
||||
}
|
||||
|
||||
void evil_portal_scene_rename_on_enter(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
TextInput* text_input = app->text_input;
|
||||
size_t enter_name_length = 25;
|
||||
evil_portal_read_ap_name(app);
|
||||
text_input_set_header_text(text_input, "AP Name/SSID");
|
||||
strncpy(app->text_store[0], (char*)app->ap_name, enter_name_length);
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
evil_portal_text_input_callback,
|
||||
context,
|
||||
app->text_store[0],
|
||||
enter_name_length,
|
||||
false);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, Evil_PortalAppViewTextInput);
|
||||
}
|
||||
|
||||
bool evil_portal_scene_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
Evil_PortalApp* app = context;
|
||||
SceneManager* scene_manager = app->scene_manager;
|
||||
bool consumed = false;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
evil_portal_write_ap_name(app);
|
||||
scene_manager_search_and_switch_to_previous_scene(scene_manager, Evil_PortalSceneStart);
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void evil_portal_scene_rename_on_exit(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
}
|
||||
54
applications/external/evil_portal/scenes/evil_portal_scene_select_html.c
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "../evil_portal_app_i.h"
|
||||
#include "../helpers/evil_portal_storage.h"
|
||||
|
||||
void evil_portal_show_loading_popup(Evil_PortalApp* app, bool show) {
|
||||
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
|
||||
ViewStack* view_stack = app->view_stack;
|
||||
Loading* loading = app->loading;
|
||||
if(show) {
|
||||
// Raise timer priority so that animations can play
|
||||
vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
|
||||
view_stack_add_view(view_stack, loading_get_view(loading));
|
||||
} else {
|
||||
view_stack_remove_view(view_stack, loading_get_view(loading));
|
||||
// Restore default timer priority
|
||||
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void evil_portal_scene_select_html_on_enter(void* context) {
|
||||
Evil_PortalApp* app = context;
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
evil_portal_create_html_folder_if_not_exists();
|
||||
|
||||
dialog_file_browser_set_basic_options(&browser_options, HTML_EXTENSION, &I_evil_portal_10px);
|
||||
browser_options.base_path = HTML_FOLDER;
|
||||
|
||||
FuriString* path;
|
||||
path = furi_string_alloc();
|
||||
|
||||
furi_string_set(path, HTML_FOLDER);
|
||||
|
||||
bool success = dialog_file_browser_show(app->dialogs, app->file_path, path, &browser_options);
|
||||
furi_string_free(path);
|
||||
|
||||
if(success) {
|
||||
//Replace HTML File
|
||||
evil_portal_show_loading_popup(app, true);
|
||||
evil_portal_replace_index_html(app->file_path);
|
||||
evil_portal_show_loading_popup(app, false);
|
||||
}
|
||||
|
||||
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, Evil_PortalSceneStart);
|
||||
}
|
||||
|
||||
bool evil_portal_scene_select_html_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
bool consumed = true;
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void evil_portal_scene_select_html_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -33,6 +33,12 @@ const Evil_PortalItem items[NUM_MENU_ITEMS] = {
|
||||
// console
|
||||
{"Save logs", {""}, 1, {"savelogs"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP},
|
||||
|
||||
// set AP name
|
||||
{"Set AP name", {""}, 1, {"setapname"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP},
|
||||
|
||||
// select HTML Portal File
|
||||
{"Select HTML", {""}, 1, {"selecthtml"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP},
|
||||
|
||||
// help
|
||||
{"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP},
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="flappy_game_app",
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=100,
|
||||
fap_icon="flappy_10px.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <flappy_bird_icons.h>
|
||||
#include "flappy_bird_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "Flappy"
|
||||
#define DEBUG false
|
||||
@@ -311,6 +313,9 @@ int32_t flappy_game_app(void* p) {
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
// Call dolphin deed on game start
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
|
||||
GameEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
|
||||
@@ -8,7 +8,6 @@ App(
|
||||
"gui",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
fap_icon="flash10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@xMasterX",
|
||||
|
||||
@@ -7,7 +7,6 @@ App(
|
||||
"gui",
|
||||
],
|
||||
stack_size=3 * 1024,
|
||||
order=10,
|
||||
fap_icon="flipbip_10px.png",
|
||||
fap_private_libs=[
|
||||
Lib(
|
||||
|
||||
BIN
applications/external/flipbip/icons/ButtonDown_10x5.png
vendored
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
applications/external/flipbip/icons/ButtonUp_10x5.png
vendored
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="i2ctools_app",
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=175,
|
||||
fap_icon="i2ctools.png",
|
||||
fap_category="GPIO",
|
||||
fap_icon_assets="images",
|
||||
|
||||
|
Before Width: | Height: | Size: 294 B |
@@ -1,7 +1,8 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <i2ctools_icons.h>
|
||||
#include "i2ctools_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define INFOS_TEXT "INFOS"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <i2ctools_icons.h>
|
||||
#include "i2ctools_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#define APP_NAME "I2C Tools"
|
||||
|
||||
@@ -41,4 +41,4 @@ typedef struct {
|
||||
void draw_main_view(Canvas* canvas, i2cMainView* main_view);
|
||||
|
||||
i2cMainView* i2c_main_view_alloc();
|
||||
void i2c_main_view_free(i2cMainView* main_view);
|
||||
void i2c_main_view_free(i2cMainView* main_view);
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <i2ctools_icons.h>
|
||||
#include "i2ctools_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include "../i2cscanner.h"
|
||||
|
||||
#define SCAN_TEXT "SCAN"
|
||||
|
||||
void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner);
|
||||
void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner);
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <i2ctools_icons.h>
|
||||
#include "i2ctools_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include "../i2csender.h"
|
||||
|
||||
#define SEND_TEXT "SEND"
|
||||
|
||||
void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender);
|
||||
void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender);
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <i2ctools_icons.h>
|
||||
#include "i2ctools_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include "../i2csniffer.h"
|
||||
|
||||
#define SNIFF_TEXT "SNIFF"
|
||||
|
||||
void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer);
|
||||
void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer);
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="flizzer_tracker_app",
|
||||
cdefines=["APP_FLIZZER_TRACKER"],
|
||||
stack_size=2 * 1024,
|
||||
order=90,
|
||||
fap_version=(0, 2),
|
||||
fap_description="An advanced Flipper Zero chiptune tracker with 4 channels",
|
||||
fap_author="LTVA",
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#include "view/pattern_editor.h"
|
||||
|
||||
#include "font.h"
|
||||
#include <flizzer_tracker_icons.h>
|
||||
#include "flizzer_tracker_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
void draw_callback(Canvas* canvas, void* ctx) {
|
||||
TrackerViewModel* model = (TrackerViewModel*)ctx;
|
||||
|
||||