|
@@ -59,15 +59,100 @@ static void camera_suite_view_camera_draw(Canvas* canvas, void* model) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Draw the guide if the camera is not initialized.
|
|
|
|
|
|
|
+ // Draw the pinout guide if the camera is not initialized.
|
|
|
if(!uartDumpModel->is_initialized) {
|
|
if(!uartDumpModel->is_initialized) {
|
|
|
- canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48);
|
|
|
|
|
|
|
+ // Clear the screen.
|
|
|
|
|
+ canvas_clear(canvas);
|
|
|
|
|
+
|
|
|
|
|
+ // Draw the ESP32-CAM module.
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 47, 50, "ESP32");
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 52, 58, "CAM");
|
|
|
|
|
+ canvas_draw_dot(canvas, 84, 3);
|
|
|
|
|
+ canvas_draw_box(canvas, 50, 35, 23, 7);
|
|
|
|
|
+ canvas_draw_circle(canvas, 42, 12, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 42, 16, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 42, 20, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 42, 24, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 42, 28, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 42, 32, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 42, 36, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 42, 8, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 59, 15, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 61, 17, 5);
|
|
|
|
|
+ canvas_draw_circle(canvas, 61, 17, 9);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 12, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 16, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 20, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 24, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 28, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 32, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 36, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 42, 1);
|
|
|
|
|
+ canvas_draw_circle(canvas, 80, 8, 1);
|
|
|
|
|
+ canvas_draw_line(canvas, 38, 4, 38, 58);
|
|
|
|
|
+ canvas_draw_line(canvas, 39, 3, 83, 3);
|
|
|
|
|
+ canvas_draw_line(canvas, 40, 2, 84, 2);
|
|
|
|
|
+ canvas_draw_line(canvas, 48, 4, 74, 4);
|
|
|
|
|
+ canvas_draw_line(canvas, 48, 5, 48, 26);
|
|
|
|
|
+ canvas_draw_line(canvas, 55, 27, 49, 27);
|
|
|
|
|
+ canvas_draw_line(canvas, 56, 25, 56, 36);
|
|
|
|
|
+ canvas_draw_line(canvas, 64, 21, 63, 21);
|
|
|
|
|
+ canvas_draw_line(canvas, 65, 15, 65, 17);
|
|
|
|
|
+ canvas_draw_line(canvas, 66, 15, 64, 18);
|
|
|
|
|
+ canvas_draw_line(canvas, 66, 16, 64, 19);
|
|
|
|
|
+ canvas_draw_line(canvas, 66, 18, 60, 21);
|
|
|
|
|
+ canvas_draw_line(canvas, 66, 19, 61, 21);
|
|
|
|
|
+ canvas_draw_line(canvas, 66, 25, 66, 36);
|
|
|
|
|
+ canvas_draw_line(canvas, 73, 27, 67, 27);
|
|
|
|
|
+ canvas_draw_line(canvas, 74, 5, 74, 26);
|
|
|
|
|
+ canvas_draw_line(canvas, 75, 4, 75, 25);
|
|
|
|
|
+ canvas_draw_line(canvas, 83, 59, 39, 59);
|
|
|
|
|
+ canvas_draw_line(canvas, 84, 4, 84, 58);
|
|
|
|
|
+ canvas_draw_line(canvas, 85, 2, 85, 57);
|
|
|
|
|
+ canvas_draw_frame(canvas, 78, 40, 5, 5);
|
|
|
|
|
+
|
|
|
|
|
+ // Draw the pinout lines.
|
|
|
|
|
+ canvas_draw_line(canvas, 39, 8, 21, 8);
|
|
|
|
|
+ canvas_draw_line(canvas, 87, 24, 83, 24);
|
|
|
|
|
+ canvas_draw_line(canvas, 87, 32, 83, 32);
|
|
|
|
|
+ canvas_draw_line(canvas, 88, 23, 88, 13);
|
|
|
|
|
+ canvas_draw_line(canvas, 88, 33, 88, 43);
|
|
|
|
|
+ canvas_draw_line(canvas, 89, 12, 126, 12);
|
|
|
|
|
+ canvas_draw_line(canvas, 126, 28, 83, 28);
|
|
|
|
|
+ canvas_draw_line(canvas, 126, 44, 89, 44);
|
|
|
|
|
+
|
|
|
|
|
+ // Draw the pinout labels.
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 91, 11, "VCC-3V");
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 91, 27, "U0R-TX");
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 91, 43, "U0T-RX");
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 2, 12, "GND");
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 12, 21, "-GND");
|
|
|
|
|
+
|
|
|
|
|
+ // Draw the "Please Connect Module!" text.
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 2, 40, "Please");
|
|
|
canvas_set_font(canvas, FontSecondary);
|
|
canvas_set_font(canvas, FontSecondary);
|
|
|
- canvas_draw_str(canvas, 8, 12, "Connect the ESP32-CAM");
|
|
|
|
|
- canvas_draw_str(canvas, 20, 24, "VCC - 3V3");
|
|
|
|
|
- canvas_draw_str(canvas, 20, 34, "GND - GND");
|
|
|
|
|
- canvas_draw_str(canvas, 20, 44, "U0R - TX");
|
|
|
|
|
- canvas_draw_str(canvas, 20, 54, "U0T - RX");
|
|
|
|
|
|
|
+ canvas_draw_str(canvas, 2, 49, "Connect");
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 2, 58, "Module!");
|
|
|
|
|
+
|
|
|
|
|
+ // Draw the "Back" text and button logo.
|
|
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
+ canvas_draw_str(canvas, 92, 57, "Back");
|
|
|
|
|
+ canvas_draw_line(canvas, 116, 49, 116, 53);
|
|
|
|
|
+ canvas_draw_line(canvas, 115, 50, 115, 52);
|
|
|
|
|
+ canvas_draw_dot(canvas, 114, 51);
|
|
|
|
|
+ canvas_draw_line(canvas, 117, 51, 121, 51);
|
|
|
|
|
+ canvas_draw_line(canvas, 122, 52, 123, 53);
|
|
|
|
|
+ canvas_draw_line(canvas, 123, 54, 122, 55);
|
|
|
|
|
+ canvas_draw_line(canvas, 121, 56, 117, 56);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -94,10 +179,20 @@ static void save_image_to_flipper_sd_card(void* model) {
|
|
|
FuriString* file_name = furi_string_alloc();
|
|
FuriString* file_name = furi_string_alloc();
|
|
|
|
|
|
|
|
// Get the current date and time.
|
|
// Get the current date and time.
|
|
|
|
|
+
|
|
|
|
|
+ // Not supported in "Release" F0 build.
|
|
|
|
|
+ // TODO: Remove when DateTime is supported in "Release" F0 build.
|
|
|
|
|
+ // FuriHalRtcDateTime datetime = {0};
|
|
|
|
|
+
|
|
|
|
|
+ // Only supported in "RC" & "Dev" builds.
|
|
|
|
|
+ // TODO: Uncomment when DateTime is supported in "Release" F0 build.
|
|
|
DateTime datetime = {0};
|
|
DateTime datetime = {0};
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: Uncomment when DateTime is supported in "Release" F0 build.
|
|
|
furi_hal_rtc_get_datetime(&datetime);
|
|
furi_hal_rtc_get_datetime(&datetime);
|
|
|
|
|
|
|
|
- // Create the file name.
|
|
|
|
|
|
|
+ // Create the file name using DateTime.
|
|
|
|
|
+ // TODO: Uncomment when DateTime is supported in "Release" F0 build.
|
|
|
furi_string_printf(
|
|
furi_string_printf(
|
|
|
file_name,
|
|
file_name,
|
|
|
EXT_PATH("DCIM/%.4d%.2d%.2d-%.2d%.2d%.2d.bmp"),
|
|
EXT_PATH("DCIM/%.4d%.2d%.2d-%.2d%.2d%.2d.bmp"),
|
|
@@ -108,6 +203,10 @@ static void save_image_to_flipper_sd_card(void* model) {
|
|
|
datetime.minute,
|
|
datetime.minute,
|
|
|
datetime.second);
|
|
datetime.second);
|
|
|
|
|
|
|
|
|
|
+ // Just use a random number for now instead of DateTime.
|
|
|
|
|
+ // int random_number = rand();
|
|
|
|
|
+ // furi_string_printf(file_name, EXT_PATH("DCIM/%d.bmp"), random_number);
|
|
|
|
|
+
|
|
|
// Open the file for writing. If the file does not exist (it shouldn't),
|
|
// Open the file for writing. If the file does not exist (it shouldn't),
|
|
|
// create it.
|
|
// create it.
|
|
|
bool result =
|
|
bool result =
|
|
@@ -332,6 +431,7 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -382,12 +482,10 @@ static void
|
|
|
// Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`.
|
|
// Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`.
|
|
|
CameraSuiteViewCamera* instance = context;
|
|
CameraSuiteViewCamera* instance = context;
|
|
|
|
|
|
|
|
- // If `uartIrqEvent` is `UartIrqEventRXNE`, send the data to the
|
|
|
|
|
- // `rx_stream` and set the `WorkerEventRx` flag.
|
|
|
|
|
if(event == FuriHalSerialRxEventData) {
|
|
if(event == FuriHalSerialRxEventData) {
|
|
|
uint8_t data = furi_hal_serial_async_rx(handle);
|
|
uint8_t data = furi_hal_serial_async_rx(handle);
|
|
|
- furi_stream_buffer_send(instance->rx_stream, &data, 1, 0);
|
|
|
|
|
- furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), WorkerEventRx);
|
|
|
|
|
|
|
+ furi_stream_buffer_send(instance->camera_rx_stream, &data, 1, 0);
|
|
|
|
|
+ furi_thread_flags_set(furi_thread_get_id(instance->camera_worker_thread), WorkerEventRx);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -439,31 +537,39 @@ static void process_ringbuffer(UartDumpModel* model, uint8_t const byte) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static int32_t camera_worker(void* context) {
|
|
|
|
|
|
|
+static int32_t camera_suite_camera_worker(void* context) {
|
|
|
furi_assert(context);
|
|
furi_assert(context);
|
|
|
|
|
|
|
|
CameraSuiteViewCamera* instance = context;
|
|
CameraSuiteViewCamera* instance = context;
|
|
|
|
|
|
|
|
while(1) {
|
|
while(1) {
|
|
|
|
|
+ // Wait for any event on the worker thread.
|
|
|
uint32_t events =
|
|
uint32_t events =
|
|
|
- furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever);
|
|
|
|
|
|
|
+ furi_thread_flags_wait(CAMERA_WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever);
|
|
|
|
|
+
|
|
|
|
|
+ // Check if an error occurred.
|
|
|
furi_check((events & FuriFlagError) == 0);
|
|
furi_check((events & FuriFlagError) == 0);
|
|
|
|
|
|
|
|
|
|
+ // Check if the thread should stop.
|
|
|
if(events & WorkerEventStop) {
|
|
if(events & WorkerEventStop) {
|
|
|
break;
|
|
break;
|
|
|
} else if(events & WorkerEventRx) {
|
|
} else if(events & WorkerEventRx) {
|
|
|
size_t length = 0;
|
|
size_t length = 0;
|
|
|
|
|
+ // Read all available data from the stream buffer.
|
|
|
do {
|
|
do {
|
|
|
- size_t intended_data_size = 64;
|
|
|
|
|
- uint8_t data[intended_data_size];
|
|
|
|
|
|
|
+ // Read up to 64 bytes from the stream buffer.
|
|
|
|
|
+ size_t buffer_size = 64;
|
|
|
|
|
+ // Allocate a buffer for the data.
|
|
|
|
|
+ uint8_t data[buffer_size];
|
|
|
|
|
+ // Read the data from the stream buffer.
|
|
|
length =
|
|
length =
|
|
|
- furi_stream_buffer_receive(instance->rx_stream, data, intended_data_size, 0);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ furi_stream_buffer_receive(instance->camera_rx_stream, data, buffer_size, 0);
|
|
|
if(length > 0) {
|
|
if(length > 0) {
|
|
|
with_view_model(
|
|
with_view_model(
|
|
|
instance->view,
|
|
instance->view,
|
|
|
UartDumpModel * model,
|
|
UartDumpModel * model,
|
|
|
{
|
|
{
|
|
|
|
|
+ // Process the data.
|
|
|
for(size_t i = 0; i < length; i++) {
|
|
for(size_t i = 0; i < length; i++) {
|
|
|
process_ringbuffer(model, data[i]);
|
|
process_ringbuffer(model, data[i]);
|
|
|
}
|
|
}
|
|
@@ -488,7 +594,7 @@ CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
|
|
|
instance->view = view_alloc();
|
|
instance->view = view_alloc();
|
|
|
|
|
|
|
|
// Allocate a stream buffer
|
|
// Allocate a stream buffer
|
|
|
- instance->rx_stream = furi_stream_buffer_alloc(2048, 1);
|
|
|
|
|
|
|
+ instance->camera_rx_stream = furi_stream_buffer_alloc(2048, 1);
|
|
|
|
|
|
|
|
// Allocate model
|
|
// Allocate model
|
|
|
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel));
|
|
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel));
|
|
@@ -509,16 +615,17 @@ CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
|
|
|
view_set_exit_callback(instance->view, camera_suite_view_camera_exit);
|
|
view_set_exit_callback(instance->view, camera_suite_view_camera_exit);
|
|
|
|
|
|
|
|
// Allocate a thread for this camera to run on.
|
|
// Allocate a thread for this camera to run on.
|
|
|
- FuriThread* thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance);
|
|
|
|
|
- instance->worker_thread = thread;
|
|
|
|
|
- furi_thread_start(instance->worker_thread);
|
|
|
|
|
|
|
+ FuriThread* thread = furi_thread_alloc_ex(
|
|
|
|
|
+ "Camera_Suite_Camera_Rx_Thread", 2048, camera_suite_camera_worker, instance);
|
|
|
|
|
+ instance->camera_worker_thread = thread;
|
|
|
|
|
+ furi_thread_start(instance->camera_worker_thread);
|
|
|
|
|
|
|
|
- // 115200 is the default baud rate for the ESP32-CAM.
|
|
|
|
|
|
|
+ // Allocate the serial handle for the camera.
|
|
|
instance->serial_handle = furi_hal_serial_control_acquire(UART_CH);
|
|
instance->serial_handle = furi_hal_serial_control_acquire(UART_CH);
|
|
|
furi_check(instance->serial_handle);
|
|
furi_check(instance->serial_handle);
|
|
|
furi_hal_serial_init(instance->serial_handle, 230400);
|
|
furi_hal_serial_init(instance->serial_handle, 230400);
|
|
|
|
|
|
|
|
- // Enable UART1 and set the IRQ callback.
|
|
|
|
|
|
|
+ // Start the asynchronous receive.
|
|
|
furi_hal_serial_async_rx_start(instance->serial_handle, camera_on_irq_cb, instance, false);
|
|
furi_hal_serial_async_rx_start(instance->serial_handle, camera_on_irq_cb, instance, false);
|
|
|
|
|
|
|
|
return instance;
|
|
return instance;
|
|
@@ -528,14 +635,14 @@ void camera_suite_view_camera_free(CameraSuiteViewCamera* instance) {
|
|
|
furi_assert(instance);
|
|
furi_assert(instance);
|
|
|
|
|
|
|
|
// Free the worker thread.
|
|
// Free the worker thread.
|
|
|
- furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), WorkerEventStop);
|
|
|
|
|
- furi_thread_join(instance->worker_thread);
|
|
|
|
|
- furi_thread_free(instance->worker_thread);
|
|
|
|
|
|
|
+ furi_thread_flags_set(furi_thread_get_id(instance->camera_worker_thread), WorkerEventStop);
|
|
|
|
|
+ furi_thread_join(instance->camera_worker_thread);
|
|
|
|
|
+ furi_thread_free(instance->camera_worker_thread);
|
|
|
|
|
|
|
|
// Free the allocated stream buffer.
|
|
// Free the allocated stream buffer.
|
|
|
- furi_stream_buffer_free(instance->rx_stream);
|
|
|
|
|
|
|
+ furi_stream_buffer_free(instance->camera_rx_stream);
|
|
|
|
|
|
|
|
- // Re-enable the console.
|
|
|
|
|
|
|
+ // Deinitialize the serial handle and release the control.
|
|
|
furi_hal_serial_deinit(instance->serial_handle);
|
|
furi_hal_serial_deinit(instance->serial_handle);
|
|
|
furi_hal_serial_control_release(instance->serial_handle);
|
|
furi_hal_serial_control_release(instance->serial_handle);
|
|
|
|
|
|