diff --git a/Canbus_app/app_user.c b/Canbus_app/app_user.c index 7688914..882d27f 100644 --- a/Canbus_app/app_user.c +++ b/Canbus_app/app_user.c @@ -60,6 +60,14 @@ static App* app_alloc() { app->text = furi_string_alloc(); app->data = furi_string_alloc(); + app->path = furi_string_alloc(); + + furi_string_reset(app->data); + furi_string_cat_printf(app->data, "---"); + + app->file_browser = file_browser_alloc(app->path); + view_dispatcher_add_view( + app->view_dispatcher, FileBrowserView, file_browser_get_view(app->file_browser)); app->mcp_can = mcp_alloc(MCP_NORMAL, MCP_16MHZ, MCP_500KBPS); @@ -69,6 +77,8 @@ static App* app_alloc() { app->frame_to_send = malloc(sizeof(CANFRAME)); + app->obdii.bitrate = app->mcp_can->bitRate; + makePaths(app); return app; @@ -82,6 +92,7 @@ static void app_free(App* app) { view_dispatcher_remove_view(app->view_dispatcher, TextBoxView); view_dispatcher_remove_view(app->view_dispatcher, VarListView); view_dispatcher_remove_view(app->view_dispatcher, InputByteView); + view_dispatcher_remove_view(app->view_dispatcher, FileBrowserView); scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); @@ -90,9 +101,11 @@ static void app_free(App* app) { submenu_free(app->submenu); text_box_free(app->textBox); byte_input_free(app->input_byte_value); + file_browser_free(app->file_browser); furi_string_free(app->text); furi_string_free(app->data); + furi_string_free(app->path); if(app->log_file && storage_file_is_open(app->log_file)) { storage_file_close(app->log_file); diff --git a/Canbus_app/app_user.h b/Canbus_app/app_user.h index d438308..616144d 100644 --- a/Canbus_app/app_user.h +++ b/Canbus_app/app_user.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,6 +18,9 @@ #include "scenes_config/app_scene_functions.h" #include "libraries/mcp_can_2515.h" +#include "libraries/pid_library.h" + +#include "canbus_app_icons.h" #define PATHAPP "apps_data/canbus" #define PATHAPPEXT EXT_PATH(PATHAPP) @@ -23,6 +28,8 @@ #define DEVICE_NO_CONNECTED (0xFF) +#define MESSAGE_ERROR 0xF0 + typedef enum { WorkerflagStop = (1 << 0), WorkerflagReceived = (1 << 1), @@ -36,6 +43,8 @@ typedef struct { CANFRAME* frameArray; CANFRAME* frame_to_send; + OBDII obdii; + uint32_t time; uint32_t times[100]; uint32_t current_time[100]; @@ -48,9 +57,11 @@ typedef struct { VariableItemList* varList; TextBox* textBox; ByteInput* input_byte_value; + FileBrowser* file_browser; FuriString* text; FuriString* data; + FuriString* path; Storage* storage; DialogsApp* dialogs; @@ -62,27 +73,39 @@ typedef struct { uint32_t sniffer_index; uint32_t sniffer_index_aux; + uint8_t config_timing_index; + uint8_t num_of_devices; uint8_t sender_selected_item; uint8_t sender_id_compose[4]; + uint32_t obdii_aux_index; + uint8_t flags; + uint64_t size_of_storage; + + uint8_t request_data; } App; // This is for the menu Options typedef enum { SniffingTestOption, SenderOption, + ObdiiOption, ReadLOGOption, + PlayLOGOption, SettingsOption, AboutUsOption, } MainMenuOptions; +// These are the events on the main menu typedef enum { SniffingOptionEvent, SenderOptionEvent, SettingsOptionEvent, + ObdiiOptionEvent, ReadLOGOptionEvent, + PlayLOGOptionEvent, AboutUsEvent, } MainMenuEvents; @@ -92,16 +115,26 @@ typedef enum { CristyalClkOption, SaveLogsOption } OptionSettings; + +// These are the events on the settings menu typedef enum { BitrateOptionEvent, CristyalClkOptionEvent } SettingsMenuEvent; + +// These are the sender events typedef enum { ChooseIdEvent, SetIdEvent, ReturnEvent } SenderEvents; +// These are the player events +typedef enum { + ChooseTimingEvent, + ReturnTimingEvent +} PlayerEvents; + // These are the options to save typedef enum { NoSave, @@ -129,10 +162,15 @@ typedef enum { TextBoxView, DialogInfoView, InputByteView, + FileBrowserView, } scenesViews; -char* sequential_file_resolve_path( - Storage* storage, - const char* dir, - const char* prefix, - const char* extension); +/** + * These functions works in other scenes and widget + */ + +void draw_in_development(App* app); +void draw_device_no_connected(App* app); +void draw_transmition_failure(App* app); +void draw_send_ok(App* app); +void draw_send_wrong(App* app); diff --git a/Canbus_app/application.fam b/Canbus_app/application.fam index 0a5b47f..e8ea9f2 100644 --- a/Canbus_app/application.fam +++ b/Canbus_app/application.fam @@ -6,5 +6,6 @@ App( requires=["gui"], stack_size=30 * 1024, fap_icon="icon.png", - fap_category="Misc", + fap_category="ElectronicCats", + fap_icon_assets="assets", ) diff --git a/Canbus_app/assets/qrcode.png b/Canbus_app/assets/qrcode.png new file mode 100644 index 0000000..7794779 Binary files /dev/null and b/Canbus_app/assets/qrcode.png differ diff --git a/Canbus_app/dist/canbus_app.fap b/Canbus_app/dist/canbus_app.fap index b22ecf4..57cdd6a 100644 Binary files a/Canbus_app/dist/canbus_app.fap and b/Canbus_app/dist/canbus_app.fap differ diff --git a/Canbus_app/dist/debug/canbus_app_d.elf b/Canbus_app/dist/debug/canbus_app_d.elf index 03e7da3..2595e3f 100644 Binary files a/Canbus_app/dist/debug/canbus_app_d.elf and b/Canbus_app/dist/debug/canbus_app_d.elf differ diff --git a/Canbus_app/draw_functions/drawWidgets.c b/Canbus_app/draw_functions/drawWidgets.c new file mode 100644 index 0000000..06a5023 --- /dev/null +++ b/Canbus_app/draw_functions/drawWidgets.c @@ -0,0 +1,48 @@ +#include "../app_user.h" + +// Draws a developmet +void draw_in_development(App* app) { + widget_reset(app->widget); + + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "SCENE IN"); + + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "DEVELOPMENT"); +} + +// Draws device not connected +void draw_device_no_connected(App* app) { + widget_reset(app->widget); + + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "DEVICE NOT"); + + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "CONNECTED"); +} + +// draw when a message is not recognized +void draw_transmition_failure(App* app) { + widget_reset(app->widget); + + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "TRANSMITION"); + + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "FAILURE"); +} + +// draw when a message is send OK +void draw_send_wrong(App* app) { + widget_reset(app->widget); + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignCenter, FontPrimary, "MESSAGE SEND ERROR"); +} + +// draw when a message is send ok +void draw_send_ok(App* app) { + widget_reset(app->widget); + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignCenter, FontPrimary, "MESSAGE SEND OK"); +} diff --git a/Canbus_app/libraries/mcp_can_2515.c b/Canbus_app/libraries/mcp_can_2515.c index ab0c57e..30fd535 100644 --- a/Canbus_app/libraries/mcp_can_2515.c +++ b/Canbus_app/libraries/mcp_can_2515.c @@ -77,7 +77,14 @@ void read_Id(FuriHalSpiBusHandle* spi, uint8_t addr, uint32_t* id, uint8_t* ext) // get actual mode of the MCP2515 uint8_t get_mode(FuriHalSpiBusHandle* spi) { uint8_t data = 0; - read_register(spi, MCP_CANSTAT, &data); + + uint8_t instruction[] = {INSTRUCTION_READ, MCP_CANSTAT}; + furi_hal_spi_acquire(spi); + furi_hal_spi_bus_tx(spi, instruction, sizeof(instruction), TIMEOUT_SPI); + furi_hal_spi_bus_rx(spi, &data, 1, TIMEOUT_SPI); + + furi_hal_spi_release(spi); + return data & CANSTAT_OPM; } @@ -128,6 +135,8 @@ bool set_new_mode(MCP2515* mcp_can, MCP_MODE new_mode) { read_status &= CANSTAT_OPM; if(read_status == MODE_CONFIG) ret = true; + furi_delay_us(1); + } while((ret != true) && ((furi_get_tick() - time_out) < 50)); time_out = furi_get_tick(); @@ -138,6 +147,9 @@ bool set_new_mode(MCP2515* mcp_can, MCP_MODE new_mode) { read_status &= CANSTAT_OPM; if(read_status == new_mode) return true; + + furi_delay_us(1); + } while((furi_get_tick() - time_out) < 50); return false; @@ -148,8 +160,6 @@ bool set_config_mode(MCP2515* mcp_can) { bool ret = true; ret = set_new_mode(mcp_can, MODE_CONFIG); - if(ret) mcp_can->mode = MODE_CONFIG; - return ret; } @@ -157,9 +167,6 @@ bool set_config_mode(MCP2515* mcp_can) { bool set_normal_mode(MCP2515* mcp_can) { bool ret = true; ret = set_new_mode(mcp_can, MCP_NORMAL); - - if(ret) mcp_can->mode = MCP_NORMAL; - return ret; } @@ -167,9 +174,6 @@ bool set_normal_mode(MCP2515* mcp_can) { bool set_listen_only_mode(MCP2515* mcp_can) { bool ret = true; ret = set_new_mode(mcp_can, MCP_LISTENONLY); - - if(ret) mcp_can->mode = MCP_LISTENONLY; - return ret; } @@ -177,9 +181,6 @@ bool set_listen_only_mode(MCP2515* mcp_can) { bool set_sleep_mode(MCP2515* mcp_can) { bool ret = true; ret = set_new_mode(mcp_can, MCP_SLEEP); - - if(ret) mcp_can->mode = MCP_SLEEP; - return ret; } @@ -322,10 +323,7 @@ void write_mf(FuriHalSpiBusHandle* spi, uint8_t address, uint8_t ext, uint32_t i furi_hal_spi_acquire(spi); furi_hal_spi_bus_tx(spi, instruction, sizeof(instruction), TIMEOUT_SPI); - for(uint8_t i = 0; i < 4; i++) { - furi_hal_spi_bus_tx(spi, &bufData[i], 1, TIMEOUT_SPI); - } - + furi_hal_spi_bus_tx(spi, bufData, 4, TIMEOUT_SPI); furi_hal_spi_release(spi); } @@ -335,8 +333,6 @@ void init_mask(MCP2515* mcp_can, uint8_t num_mask, uint32_t mask) { uint8_t ext = 0; - MCP_MODE last_mode = mcp_can->mode; - set_config_mode(mcp_can); if(num_mask > 1) return; @@ -350,8 +346,8 @@ void init_mask(MCP2515* mcp_can, uint8_t num_mask, uint32_t mask) { if(num_mask == 1) { write_mf(spi, MCP_RXM1SIDH, ext, mask); } - mcp_can->mode = last_mode; - set_new_mode(mcp_can, last_mode); + + set_new_mode(mcp_can, mcp_can->mode); } // To set a Filter @@ -360,8 +356,6 @@ void init_filter(MCP2515* mcp_can, uint8_t num_filter, uint32_t filter) { uint8_t ext = 0; - MCP_MODE last_mode = mcp_can->mode; - set_config_mode(mcp_can); if(num_filter > 6) return; @@ -396,8 +390,7 @@ void init_filter(MCP2515* mcp_can, uint8_t num_filter, uint32_t filter) { break; } - mcp_can->mode = last_mode; - set_new_mode(mcp_can, last_mode); + set_new_mode(mcp_can, mcp_can->mode); } // This function works to know if there is any message waiting @@ -635,7 +628,6 @@ ERROR_CAN send_can_message(FuriHalSpiBusHandle* spi, CANFRAME* frame, uint8_t tx read_register(spi, free_buffer - 1, &is_send_it); if(is_send_it == 0) res = ERROR_OK; - furi_delay_us(1); } while((res != ERROR_OK) && ((furi_get_tick() - time_waiting) < 1)); if(is_send_it) return res; @@ -652,8 +644,6 @@ ERROR_CAN send_can_frame(MCP2515* mcp_can, CANFRAME* frame) { if(free_buffer == 0xFF) return ERROR_ALLTXBUSY; return send_can_message(spi, frame, free_buffer); - - return ERROR_OK; } // This function works to alloc the struct diff --git a/Canbus_app/libraries/pid_library.c b/Canbus_app/libraries/pid_library.c new file mode 100644 index 0000000..a82648a --- /dev/null +++ b/Canbus_app/libraries/pid_library.c @@ -0,0 +1,483 @@ +#include "pid_library.h" + +// PID TYPICAL NAMES + +char* pid_codes_name[] = { + "PIDs supported A", + "Monitor Status since DTCs cleared", + "DTC that caused freeze frame to be store", + "Fuel System Status", + "Calculated engine load", + "Engine Coolant temperature", + "STFT BANK 1", + "LTFT BANK 1", + "STFT BANK 2", + "LFTF BANK 2", + "Fuel pressure", + "Intake manifold absolute pressure", + "Engine speed", + "Vehicle speed", + "Timing advance", + "Intake air temperature", + "Mass air flow sensor (MAF) air flow rate", + "Throttle position", + "Commanded secondary air status", + "Oxygen sensors present (in 2 banks)", + "Oxygen Sensor 1", + "Oxygen Sensor 2", + "Oxygen Sensor 3", + "Oxygen Sensor 4", + "Oxygen Sensor 5", + "Oxygen Sensor 6", + "Oxygen Sensor 7", + "Oxygen Sensor 8", + "OBD standards this vehicle conforms to", + "Oxygen sensors present (in 4 banks)", + "Auxiliary input status", + "Run time since engine start", + "PIDs supported B", +}; + +// Init the obdii +bool pid_init(OBDII* obdii) { + obdii->CAN = mcp_alloc(MCP_NORMAL, MCP_16MHZ, obdii->bitrate); + + if(mcp2515_init(obdii->CAN) != ERROR_OK) return false; + + obdii->codes = (pid_code*)malloc(200 * (sizeof(pid_code))); + + init_mask(obdii->CAN, 0, 0x7FF); + init_filter(obdii->CAN, 0, 0x7E8); + + init_mask(obdii->CAN, 1, 0x7FF); + init_filter(obdii->CAN, 1, 0x7E8); + init_filter(obdii->CAN, 2, 0x7E8); + init_filter(obdii->CAN, 3, 0x7E8); + init_filter(obdii->CAN, 4, 0x7E8); + init_filter(obdii->CAN, 5, 0x7E8); + + obdii->frame_to_send.canId = ECU_REQUEST_ID; + obdii->frame_to_send.data_lenght = 8; + + for(uint8_t i = 0; i < 8; i++) { + obdii->frame_to_send.buffer[i] = 0; + } + + return true; +} + +// It works to get the current data of the requested pid +bool pid_show_data(OBDII* obdii, uint8_t pid, uint8_t* data, uint8_t size) { + MCP2515* CAN = obdii->CAN; + CANFRAME frame = obdii->frame_to_send; + ERROR_CAN ret = ERROR_NOMSG; + + frame.buffer[0] = 2; + frame.buffer[1] = SHOW_DATA; + frame.buffer[2] = pid; + + if(size < 8) return false; + + if(send_can_frame(CAN, &frame) != ERROR_OK) return false; + + uint16_t time_delay = 0; + + do { + read_can_message(CAN, &obdii->frame_to_received); + + if(obdii->frame_to_received.canId == 0x7e8) ret = ERROR_OK; + + furi_delay_us(1); + time_delay++; + + } while((ret != ERROR_OK) && (time_delay < 1500)); + + if(ret != ERROR_OK) return false; + + for(uint8_t i = 0; i < size; i++) { + data[i] = obdii->frame_to_received.buffer[i]; + } + + return true; +} + +// get frames +bool read_frames(MCP2515* CAN, CANFRAME* frame) { + uint32_t time_delay = 0; + + do { + if(read_can_message(CAN, frame)) { + if(frame->canId == 0x7e8) return true; + } + furi_delay_us(1); + time_delay++; + + } while((time_delay < 6000)); + + return false; +} + +// It works to send a mode in the pid +bool pid_manual_request( + OBDII* obdii, + uint32_t id, + pid_services mode, + uint8_t pid, + CANFRAME* frames_to_read, + uint8_t lenght, + uint8_t count_of_bytes) { + MCP2515* CAN = obdii->CAN; + CANFRAME frame = obdii->frame_to_send; + ERROR_CAN ret = ERROR_OK; + + frame.canId = id; + + frame.buffer[0] = count_of_bytes; + frame.buffer[1] = mode; + frame.buffer[2] = pid; + + // uint32_t time_delay = 0; + + ret = send_can_frame(CAN, &frame); + + if(ret != ERROR_OK) return false; + + frame.buffer[0] = 0x30; + frame.buffer[1] = 0; + frame.buffer[2] = 0; + + for(uint8_t i = 0; i < lenght; i++) { + if(i == 1) { + ret = send_can_frame(CAN, &frame); + if(ret != ERROR_OK) return false; + } + + ret = ERROR_FAIL; + + if(!read_frames(CAN, &(frames_to_read[i]))) { + if(i == 0) + return false; + else + break; + } + } + + return true; +} + +// It works to get the supported datas +bool pid_get_supported_pid(OBDII* obdii, uint8_t block) { + uint8_t data[8] = {0, 0, 0, 0, 0, 0}; + + if(!pid_manual_request(obdii, 0x7df, SHOW_DATA, block, &obdii->frame_to_received, 1, 2)) + return false; + + for(uint8_t i = 0; i < 8; i++) { + data[i] = obdii->frame_to_received.buffer[i]; + } + + obdii->codes[block].pid_num = block; + obdii->codes[block].is_supported = true; + obdii->codes[block].name = "Supported"; + + uint32_t codes_available = (data[3] << 24) + (data[4] << 16) + (data[5] << 8) + data[6]; + + for(uint8_t i = 1; i <= 32; i++) { + obdii->codes[block + i].pid_num = block + i; + obdii->codes[block + i].is_supported = codes_available & (1 << (32 - i)); + if(block == BLOCK_A) + obdii->codes[i].name = pid_codes_name[i]; + else + obdii->codes[block + i].name = "UNKNOWN"; + } + + return true; +} + +bool clear_dtc(OBDII* obdii) { + MCP2515* CAN = obdii->CAN; + CANFRAME frame = obdii->frame_to_send; + CANFRAME frame_to_received = obdii->frame_to_received; + + ERROR_CAN ret = ERROR_OK; + + frame.buffer[0] = 1; + frame.buffer[1] = CLEAR_STORAGE_DTC; + frame.buffer[2] = 0; + + ret = send_can_frame(CAN, &frame); + + if(ret != ERROR_OK) return false; + + if(!read_frames(CAN, &frame_to_received)) return false; + + if(frame_to_received.buffer[1] != 0x44) return false; + + return true; +} + +/* + This part works to get the dtc +*/ + +// This separate codes by one frame +void separate_code_by_frame(uint16_t* save_codes, CANFRAME frame, uint8_t frame_position) { + uint16_t position = frame_position * 4; + + for(uint8_t i = 1; i < 8; i = i + 2) { + save_codes[position] = (frame.buffer[i - 1] << 8) + frame.buffer[i]; + position++; + } +} + +// This separate codes by multiple can frames +void separate_codes(CANFRAME* frames, uint16_t* save_codes, uint8_t length) { + for(uint8_t i = 0; i < length; i++) { + separate_code_by_frame(save_codes, frames[i], i); + } +} + +// This is for translate codes +void get_dtc(uint16_t numerical_code, char* dtc_code) { + char* first; + + UNUSED(dtc_code); + + FuriString* text = furi_string_alloc(); + + uint8_t identifier = numerical_code >> 12; // For the second + uint8_t firstNumber = numerical_code >> 8; + uint8_t secondNumber = numerical_code >> 4 & 0xf; + uint8_t thirdNumber = numerical_code & 0xf; + + switch(identifier) // Set the correct type prefix for the code + { + case 0: + first = "P0"; + break; + + case 1: + first = "P1"; + break; + + case 2: + first = "P2"; + break; + + case 3: + first = "P3"; + break; + + case 4: + first = "C0"; + break; + + case 5: + first = "C1"; + break; + + case 6: + first = "C2"; + break; + + case 7: + first = "C3"; + break; + + case 8: + first = "B0"; + break; + + case 9: + first = "B1"; + break; + + case 10: + first = "B2"; + break; + + case 11: + first = "B3"; + break; + + case 12: + first = "U0"; + break; + + case 13: + first = "U1"; + break; + + case 14: + first = "U2"; + break; + + case 15: + first = "U3"; + break; + + default: + break; + } + + furi_string_printf(text, "%s%u%u%u", first, firstNumber, secondNumber, thirdNumber); + + for(uint8_t i = 0; i < 5; i++) { + dtc_code[i] = furi_string_get_char(text, i); + } + + furi_string_free(text); +} + +// Request the codes +bool request_dtc(OBDII* obdii, uint8_t* count, char* codes[]) { + CANFRAME canframes[5]; + uint16_t save_error_codes[20]; + + memset(canframes, 0, sizeof(canframes)); + memset(save_error_codes, 0, sizeof(save_error_codes)); + + if(!pid_manual_request(obdii, 0x7df, SHOW_STORAGE_DTC, 0, canframes, 20, 1)) { + return false; + } + + if(canframes[0].buffer[0] != 0x03 && canframes[0].buffer[1] != 0x43) { + return false; + } + + separate_codes(canframes, save_error_codes, 5); + + for(uint8_t i = 1; i < 20; i++) { + if((save_error_codes[i] == 0xaa) || (save_error_codes[i] == 0)) { + *count = i - 1; + break; + } + } + + uint8_t quantity = *count; + + if(quantity == 0) return true; + + for(uint8_t i = 1; i < (quantity + 1); i++) { + get_dtc(save_error_codes[i], codes[i - 1]); + } + + return true; +} + +// Separate frames in arrays +void separate_VIN_data(CANFRAME frame, uint8_t* vin, uint8_t count) { + count = count * 7; + + for(uint8_t i = 0; i < 7; i++) { + vin[i + count] = frame.buffer[i + 1]; + } +} + +// Get the CAR VIN +bool get_VIN(OBDII* obdii, FuriString* vin_number) { + CANFRAME canframes[5]; + + memset(canframes, 0, sizeof(canframes)); + + if(!pid_manual_request(obdii, 0x7df, REQUEST_VEHICLE_INFORMATION, 0x2, canframes, 5, 2)) + return false; + + if(canframes[0].buffer[2] != 0x49) return false; + + uint32_t extension = canframes[0].buffer[1]; + + uint8_t vin[40]; + + memset(vin, 0, sizeof(vin)); + + for(uint8_t i = 0; i < 5; i++) { + separate_VIN_data(canframes[i], vin, i); + } + + char letters_vin[extension]; + + for(uint8_t i = 0; i < extension; i++) { + if(vin[i + 1] < 32) { + letters_vin[i] = ' '; + } else { + letters_vin[i] = vin[i + 1]; + } + } + + furi_string_reset(vin_number); + + for(uint8_t i = 0; i < extension; i++) { + furi_string_cat_printf(vin_number, "%c", letters_vin[i]); + } + + return true; +} + +// Get the ECU Name +bool get_ECU_name(OBDII* obdii, FuriString* ecu_name) { + CANFRAME canframes[5]; + + memset(canframes, 0, sizeof(canframes)); + + if(!pid_manual_request(obdii, 0x7df, REQUEST_VEHICLE_INFORMATION, 0xA, canframes, 5, 2)) + return false; + + if(canframes[0].buffer[2] != 0x49) return false; + + uint32_t extension = canframes[0].buffer[1]; + + uint8_t vin[40]; + + memset(vin, 0, sizeof(vin)); + + for(uint8_t i = 0; i < 5; i++) { + separate_VIN_data(canframes[i], vin, i); + } + + char letters_vin[extension]; + + for(uint8_t i = 0; i < extension; i++) { + if(vin[i + 1] < 32) { + letters_vin[i] = ' '; + } else { + letters_vin[i] = vin[i + 1]; + } + } + + furi_string_reset(ecu_name); + + for(uint8_t i = 0; i < extension; i++) { + furi_string_cat_printf(ecu_name, "%c", letters_vin[i]); + } + + return true; +} + +// It works to free +void pid_deinit(OBDII* obdii) { + free_mcp2515(obdii->CAN); + free(obdii->codes); +} + +// Calculate the engine speed +uint16_t calculate_engine_speed(uint8_t value_a, uint8_t value_b) { + uint16_t operation_a = 0; + uint16_t operation_b = 0; + + if(value_a != 0) operation_a = value_a * 64; + if(value_b != 0) operation_b = value_b / 4; + + return operation_a + operation_b; +} + +// Calcuate the engine load +float calculate_engine_load(uint8_t value) { + if(value == 0) return 0; + return value / 2.55; +} + +// To add between the A value and B value +uint16_t sum_value(uint8_t A, uint8_t B) { + return (A * 256) + B; +} diff --git a/Canbus_app/libraries/pid_library.h b/Canbus_app/libraries/pid_library.h new file mode 100644 index 0000000..b8a9759 --- /dev/null +++ b/Canbus_app/libraries/pid_library.h @@ -0,0 +1,143 @@ +#ifndef PID_LIBRARY +#define PID_LIBRARY + +#include "mcp_can_2515.h" + +#define ECU_TYPICAL_ID_STANDAR 0x7E8 +#define ECU_STANDAR_ID_2 0x7E9 +#define ECU_STANDAR_ID_3 0x7EA + +#define ECU_REQUEST_ID 0x7DF + +typedef enum { + SHOW_DATA = 0x01, + SHOW_FREEZE_DATA = 0x02, + SHOW_STORAGE_DTC = 0x03, + CLEAR_STORAGE_DTC = 0x04, + OXIGEN_SENSOR_MONITORING = 0x05, + SYSTEM_MONITORING = 0x06, + SHOW_PENDING_DTC = 0x07, + REQUEST_VEHICLE_INFORMATION = 0x09, + PERMANENT_DTC = 0x0A + +} pid_services; + +typedef enum { + BLOCK_A = 0x00, + BLOCK_B = 0x20, + BLOCK_C = 0x40, + BLOCK_D = 0x60, +} pid_request_supported_codes; + +typedef enum { + CODE_NUM_00 = 0x00, /*< PIDs supported 0x01-0x20*/ + CODE_NUM_01 = 0x01, /*< Monitor status since DTCs cleared*/ + CODE_NUM_02 = 0x02, /*< DTC that caused freeze frame to be stored. */ + CODE_NUM_03 = 0x03, /*< Fuel system status */ + CODE_NUM_04 = 0x04, /*< Calculated engine load*/ + CODE_NUM_05 = 0x05, /*< Engine coolant temperature*/ + CODE_NUM_06 = 0x06, /*< Short term fuel trim (STFT)—Bank 1*/ + CODE_NUM_07 = 0x07, /*< Long term fuel trim (LTFT)—Bank 1*/ + CODE_NUM_08 = 0x08, /*< Short term fuel trim (STFT)—Bank 2*/ + CODE_NUM_09 = 0x09, /*< Long term fuel trim (LTFT)—Bank 2*/ + CODE_NUM_0A = 0x0A, /*< Fuel pressure (gauge pressure)*/ + CODE_NUM_0B = 0x0B, /*< Intake manifold absolute pressure*/ + CODE_NUM_0C = 0x0C, /*< Engine speed*/ + CODE_NUM_0D = 0x0D, /*< Vehicle speed*/ + CODE_NUM_0E = 0x0E, /*< Timing advance*/ + CODE_NUM_0F = 0x0F, /*< Intake air temperature*/ + CODE_NUM_10 = 0x10, /*< Mass air flow sensor (MAF) air flow rate*/ + CODE_NUM_11 = 0x11, /*< Throttle position*/ + CODE_NUM_12 = 0x12, /*< Commanded secondary air status*/ + CODE_NUM_13 = 0x13, /*< Oxygen sensors present (in 2 banks)*/ + CODE_NUM_14 = 0x14, /*< Oxygen Sensor 1*/ + CODE_NUM_15 = 0x15, /*< Oxygen Sensor 2*/ + CODE_NUM_16 = 0x16, /*< Oxygen Sensor 3*/ + CODE_NUM_17 = 0x17, /*< Oxygen Sensor 4*/ + CODE_NUM_18 = 0x18, /*< Oxygen Sensor 5*/ + CODE_NUM_19 = 0x19, /*< Oxygen Sensor 6*/ + CODE_NUM_1A = 0x1A, /*< Oxygen Sensor 7*/ + CODE_NUM_1B = 0x1B, /*< Oxygen Sensor 8*/ + CODE_NUM_1C = 0x1C, /*< OBD standards this vehicle conforms to*/ + CODE_NUM_1D = 0x1D, /*< Oxygen sensors present (in 4 banks)*/ + CODE_NUM_1E = 0x1E, /*< Auxiliary input status*/ + CODE_NUM_1F = 0x1F, /*< Run time since engine start*/ +} pid_all_codes; + +typedef enum { + RUN_TIME_SINCE_ENGINE_START = 0x1F, + CALCULATED_ENGINE_LOAD = 0x04, + ENGINE_SPEED = 0x0C, + VEHICLE_SPEED = 0x0D, + THROTTLE_POSITION = 0x11, + FUEL_INPUT_POSITION = 0x2F, + THROTTLE_RELATIVE_POSITION = 0x45 +} pid_typical_codes; + +typedef struct { + uint8_t pid_num; + char* name; + char* unit; + bool is_supported; +} pid_code; + +typedef struct { + MCP2515* CAN; + CANFRAME frame_to_send; + CANFRAME frame_to_received; + + MCP_BITRATE bitrate; + + pid_code* codes; +} OBDII; + +extern char* pid_codes_name[]; + +/* + Convertions +*/ + +// Function to init the obdii scan +bool pid_init(OBDII* obdii); + +// Function to deinit and free obdii +void pid_deinit(OBDII* obdii); + +// request data +bool pid_show_data(OBDII* obdii, uint8_t pid, uint8_t* data, uint8_t size); + +// request a PID in a manual form +bool pid_manual_request( + OBDII* obdii, + uint32_t id, + pid_services mode, + uint8_t pid, + CANFRAME* frames_to_read, + uint8_t lenght, + uint8_t count_of_bytes); + +// get the pid's supported in a block of pid +bool pid_get_supported_pid(OBDII* obdii, uint8_t block); + +// Request DTC +bool request_dtc(OBDII* obdii, uint8_t* count, char* codes[]); + +// Clear DTC +bool clear_dtc(OBDII* obdii); + +// Get VIN +bool get_VIN(OBDII* obdii, FuriString* String); + +// Get ECU Name +bool get_ECU_name(OBDII* obdii, FuriString* ecu_name); + +// Function to calculate the engine speed +uint16_t calculate_engine_speed(uint8_t value_a, uint8_t value_b); + +// Calculate the porcent of calculated load engine +float calculate_engine_load(uint8_t value); + +// Calculate the units that got sum +uint16_t sum_value(uint8_t A, uint8_t B); + +#endif diff --git a/Canbus_app/scenes/AboutUs.c b/Canbus_app/scenes/AboutUs.c index 6f00dee..efc65a7 100644 --- a/Canbus_app/scenes/AboutUs.c +++ b/Canbus_app/scenes/AboutUs.c @@ -32,22 +32,12 @@ static void draw_can_app_view(App* app) { widget_add_string_element( app->widget, 65, 20, AlignCenter, AlignCenter, FontPrimary, "CANBUS APP"); widget_add_string_element( - app->widget, 65, 35, AlignCenter, AlignCenter, FontSecondary, "By: Adonai Diaz"); + app->widget, 65, 35, AlignCenter, AlignCenter, FontSecondary, "v1.2"); widget_add_button_element(app->widget, GuiButtonTypeRight, "Next", button_callback, app); widget_add_button_element(app->widget, GuiButtonTypeLeft, "Prev", button_callback, app); } -static void draw_version_view(App* app) { - widget_reset(app->widget); - widget_add_string_element( - app->widget, 65, 20, AlignCenter, AlignCenter, FontPrimary, "Version:"); - widget_add_string_element( - app->widget, 65, 35, AlignCenter, AlignCenter, FontSecondary, "CANBUS APP V1.1.0.0"); - widget_add_button_element(app->widget, GuiButtonTypeRight, "Next", button_callback, app); - widget_add_button_element(app->widget, GuiButtonTypeLeft, "Prev", button_callback, app); -} - static void draw_license_view(App* app) { widget_reset(app->widget); widget_add_string_element( @@ -61,15 +51,10 @@ static void draw_license_view(App* app) { void draw_more_info_view(App* app) { widget_reset(app->widget); widget_add_string_element( - app->widget, 65, 10, AlignCenter, AlignCenter, FontPrimary, "More info:"); - widget_add_string_element( - app->widget, 65, 25, AlignCenter, AlignCenter, FontSecondary, "https://github.com"); + app->widget, 10, 5, AlignLeft, AlignCenter, FontPrimary, "More info:"); - widget_add_string_element( - app->widget, 65, 35, AlignCenter, AlignCenter, FontSecondary, "/ElectronicCats"); + widget_add_icon_element(app->widget, 48, 15, &I_qrcode); - widget_add_string_element( - app->widget, 65, 45, AlignCenter, AlignCenter, FontSecondary, "/flipper-MCP2515-CANBUS"); widget_add_button_element(app->widget, GuiButtonTypeLeft, "Prev", button_callback, app); } @@ -95,12 +80,9 @@ bool app_scene_about_us_on_event(void* context, SceneManagerEvent event) { draw_can_app_view(app); break; case 2: - draw_version_view(app); - break; - case 3: draw_license_view(app); break; - case 4: + case 3: draw_more_info_view(app); break; default: diff --git a/Canbus_app/scenes/Obd2MenuOption.c b/Canbus_app/scenes/Obd2MenuOption.c new file mode 100644 index 0000000..5b46d1b --- /dev/null +++ b/Canbus_app/scenes/Obd2MenuOption.c @@ -0,0 +1,75 @@ +#include "../app_user.h" + +// odbii menu casllback +void obdii_menu_callback(void* context, uint32_t index) { + App* app = context; + + app->obdii_aux_index = index; + + switch(index) { + case 0: + scene_manager_next_scene(app->scene_manager, app_scene_supported_pid_option); + break; + + case 1: + scene_manager_next_scene(app->scene_manager, app_scene_obdii_typical_codes_option); + break; + + case 2: + app->request_data = 1; + scene_manager_next_scene(app->scene_manager, app_scene_car_data_option); + break; + + case 3: + app->request_data = 2; + scene_manager_next_scene(app->scene_manager, app_scene_car_data_option); + break; + + case 4: + scene_manager_next_scene(app->scene_manager, app_scene_obdii_get_errors_option); + break; + + case 5: + scene_manager_next_scene(app->scene_manager, app_scene_obdii_delete_errors_option); + break; + + case 6: + scene_manager_next_scene(app->scene_manager, app_scene_manual_sender_pid_option); + break; + + default: + break; + } +} + +void app_scene_obdii_menu_on_enter(void* context) { + App* app = context; + + submenu_reset(app->submenu); + + submenu_set_header(app->submenu, "OBDII SCANNER"); + + // Examples + submenu_add_item(app->submenu, "Get Supported PID Codes", 0, obdii_menu_callback, app); + submenu_add_item(app->submenu, "Show Typical Data", 1, obdii_menu_callback, app); + submenu_add_item(app->submenu, "Get VIN number", 2, obdii_menu_callback, app); + submenu_add_item(app->submenu, "Get ECU name", 3, obdii_menu_callback, app); + submenu_add_item(app->submenu, "Show DTC", 4, obdii_menu_callback, app); + submenu_add_item(app->submenu, "Delete DTC", 5, obdii_menu_callback, app); + submenu_add_item(app->submenu, "Manual Sender PID", 6, obdii_menu_callback, app); + + submenu_set_selected_item(app->submenu, app->obdii_aux_index); + + view_dispatcher_switch_to_view(app->view_dispatcher, SubmenuView); +} + +bool app_scene_obdii_menu_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void app_scene_obdii_menu_on_exit(void* context) { + App* app = context; + submenu_reset(app->submenu); +} diff --git a/Canbus_app/scenes/Obd2Options/dtcPIDOption.c b/Canbus_app/scenes/Obd2Options/dtcPIDOption.c new file mode 100644 index 0000000..aff0d67 --- /dev/null +++ b/Canbus_app/scenes/Obd2Options/dtcPIDOption.c @@ -0,0 +1,199 @@ +#include "../../app_user.h" + +// This variable works to know if it wishes to delete the DTC storage +static bool delete_dtc = false; + +// Function of the thread +static int32_t obdii_thread_dtc_on_work(void* context); + +/* + Scene to watch the errors in the car +*/ + +void app_scene_obdii_get_errors_on_enter(void* context) { + App* app = context; + widget_reset(app->widget); + app->thread = furi_thread_alloc_ex("ShowDTC", 1024, obdii_thread_dtc_on_work, app); + furi_thread_start(app->thread); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewWidget); +} + +bool app_scene_obdii_get_errors_on_event(void* context, SceneManagerEvent event) { + App* app = context; + UNUSED(app); + UNUSED(event); + return false; +} + +void app_scene_obdii_get_errors_on_exit(void* context) { + App* app = context; + furi_thread_join(app->thread); + furi_thread_free(app->thread); + widget_reset(app->widget); +} + +/* + Scene to Delete DTC en car +*/ + +void app_scene_obdii_delete_dtc_on_enter(void* context) { + App* app = context; + delete_dtc = true; + widget_reset(app->widget); + app->thread = furi_thread_alloc_ex("ShowDTC", 1024, obdii_thread_dtc_on_work, app); + furi_thread_start(app->thread); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewWidget); +} + +bool app_scene_obdii_delete_dtc_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void app_scene_obdii_delete_dtc_on_exit(void* context) { + App* app = context; + furi_thread_join(app->thread); + furi_thread_free(app->thread); + delete_dtc = false; + widget_reset(app->widget); +} + +/* + Thread to request the DTC (Diagnostic Trouble Codes) +*/ + +static int32_t obdii_thread_dtc_on_work(void* context) { + App* app = context; + + OBDII scanner; + + FuriString* text = app->text; + + UNUSED(text); + + scanner.bitrate = app->mcp_can->bitRate; + + bool loop = false; + + char* codes[20]; + + for(uint8_t i = 0; i < 20; i++) { + codes[i] = (char*)malloc(5 * sizeof(char)); + } + + uint8_t dtc_selector = 0, count_dtc = 0; + + bool run = pid_init(&scanner); + + if(run) { + furi_delay_ms(500); // Time added to initialize + } + + if(delete_dtc && run) { + if(clear_dtc(&scanner)) { + widget_reset(app->widget); + + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "ALL DTC"); + + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "CLEARED"); + } else { + widget_reset(app->widget); + + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "ERROR"); + + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "CLEARING"); + } + } else if(!delete_dtc && run) { + if(request_dtc(&scanner, &(count_dtc), codes)) { + if(count_dtc == 0) { + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "NO DTC"); + + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "DETECTED"); + + } else + loop = true; + + } else { + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "REQUESTED"); + + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "ERROR"); + } + + } else { + draw_device_no_connected(app); + } + + uint8_t past_selector = 1; + + while(loop) { + if(!furi_hal_gpio_read(&gpio_button_right)) { + dtc_selector++; + if(dtc_selector > (count_dtc - 1)) dtc_selector = 0; + furi_delay_ms(500); + } + + if(!furi_hal_gpio_read(&gpio_button_left)) { + dtc_selector--; + if(dtc_selector > count_dtc) dtc_selector = (count_dtc - 1); + furi_delay_ms(500); + } + + if(past_selector != dtc_selector) { + widget_reset(app->widget); + furi_string_reset(text); + + furi_string_printf(text, "%u of %u DTC", dtc_selector + 1, count_dtc); + + widget_add_string_element( + app->widget, + 65, + 20, + AlignCenter, + AlignBottom, + FontSecondary, + furi_string_get_cstr(text)); + + furi_string_reset(text); + + furi_string_printf(text, "%s", codes[dtc_selector]); + + widget_add_string_element( + app->widget, + 65, + 35, + AlignCenter, + AlignBottom, + FontPrimary, + furi_string_get_cstr(text)); + + past_selector = dtc_selector; + } + + if(!furi_hal_gpio_read(&gpio_button_back)) { + break; + } + furi_delay_ms(1); + } + + for(uint8_t i = 0; i < 20; i++) { + free(codes[i]); + } + + furi_string_reset(text); + + pid_deinit(&scanner); + + return 0; +} diff --git a/Canbus_app/scenes/Obd2Options/getCarDataOption.c b/Canbus_app/scenes/Obd2Options/getCarDataOption.c new file mode 100644 index 0000000..4e2b325 --- /dev/null +++ b/Canbus_app/scenes/Obd2Options/getCarDataOption.c @@ -0,0 +1,92 @@ +#include "../../app_user.h" + +// Function for the thread +static int32_t obdii_get_car_data(void* context); + +/* + Scene to get the VIN and ECU Name +*/ +// Scene on enter +void app_scene_get_car_data_on_enter(void* context) { + App* app = context; + widget_reset(app->widget); + + app->thread = furi_thread_alloc_ex("CarDatas", 1024, obdii_get_car_data, app); + furi_thread_start(app->thread); + + view_dispatcher_switch_to_view(app->view_dispatcher, ViewWidget); +} + +// Scene on event +bool app_scene_get_car_data_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + + return false; +} + +// Scene on exit +void app_scene_get_car_data_on_exit(void* context) { + App* app = context; + furi_thread_join(app->thread); + furi_thread_free(app->thread); + widget_reset(app->widget); +} + +/* + Thread to get the VIN number +*/ + +static int32_t obdii_get_car_data(void* context) { + App* app = context; + OBDII scanner; + + scanner.bitrate = app->mcp_can->bitRate; + + bool run = pid_init(&scanner); + + if(run) { + // Time delay added to initialize + furi_delay_ms(500); + + if(app->request_data == 1) { + if(get_VIN(&scanner, app->text)) { + widget_add_string_element( + app->widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, "VIN Number:"); + + widget_add_string_element( + app->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + furi_string_get_cstr(app->text)); + } else { + draw_transmition_failure(app); + } + } + if(app->request_data == 2) { + if(get_ECU_name(&scanner, app->text)) { + widget_add_string_element( + app->widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, "ECU Name:"); + + widget_add_string_element( + app->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + furi_string_get_cstr(app->text)); + } else { + draw_transmition_failure(app); + } + } + } else + draw_device_no_connected(app); + + pid_deinit(&scanner); + + return 0; +} diff --git a/Canbus_app/scenes/Obd2Options/manualPIDSenderOption.c b/Canbus_app/scenes/Obd2Options/manualPIDSenderOption.c new file mode 100644 index 0000000..4ec626b --- /dev/null +++ b/Canbus_app/scenes/Obd2Options/manualPIDSenderOption.c @@ -0,0 +1,386 @@ +#include "../../app_user.h" + +// These arrays are used in the manual sender PID +static uint32_t can_id = 0x7DF; +static uint8_t byte_values[4] = {0, 0, 0, 0}; +static uint8_t service_to_send = 0x1; +static uint8_t lenght_pid = 0x01; +static uint8_t code_to_send = 0x00; +static uint8_t count_of_bytes = 2; + +// Thread +static int32_t obdii_thread_response_manual_sender_on_work(void* context); + +/* + Manual Sender Scene +*/ + +// Callback for the input options +void callback_manual_input_pid_options(void* context, uint32_t index) { + App* app = context; + app->sender_selected_item = index; + + switch(index) { + case 0: + scene_manager_set_scene_state( + app->scene_manager, app_scene_input_manual_pid_option, index); + scene_manager_next_scene(app->scene_manager, app_scene_input_manual_pid_option); + + break; + + case 1: + scene_manager_set_scene_state( + app->scene_manager, app_scene_input_manual_pid_option, index); + scene_manager_next_scene(app->scene_manager, app_scene_input_manual_pid_option); + + break; + + case 3: + scene_manager_set_scene_state( + app->scene_manager, app_scene_input_manual_pid_option, index); + scene_manager_next_scene(app->scene_manager, app_scene_input_manual_pid_option); + + break; + + case 5: + scene_manager_next_scene(app->scene_manager, app_scene_response_pid_option); + + default: + break; + } +} + +// Callback for the options +void callback_manual_pid_sender_options(VariableItem* item) { + App* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + uint8_t selected_index = variable_item_list_get_selected_item_index(app->varList); + FuriString* text = app->text; + + furi_string_reset(text); + + switch(selected_index) { + // Service + case 1: + service_to_send = index; + furi_string_cat_printf(text, "0x%x", service_to_send); + variable_item_set_current_value_text(item, furi_string_get_cstr(text)); + + break; + + // count of bytes + case 2: + count_of_bytes = index; + furi_string_cat_printf(text, "%u", count_of_bytes); + variable_item_set_current_value_text(item, furi_string_get_cstr(text)); + + break; + + // Lenght + case 4: + lenght_pid = index; + furi_string_cat_printf(text, "%u", lenght_pid); + variable_item_set_current_value_text(item, furi_string_get_cstr(text)); + break; + + default: + break; + } +} + +// Scene on enter +void app_scene_manual_sender_pid_on_enter(void* context) { + App* app = context; + FuriString* text = app->text; + VariableItem* item; + + variable_item_list_reset(app->varList); + + // First item to set (CAN ID) [0] + furi_string_reset(text); + furi_string_cat_printf(text, "0x%lx", can_id); + + item = + variable_item_list_add(app->varList, "CAN ID", 0, callback_manual_pid_sender_options, app); + variable_item_set_current_value_text(item, furi_string_get_cstr(text)); + + // Second Item to set (Service) [1] + furi_string_reset(text); + furi_string_cat_printf(text, "0x%x", service_to_send); + item = variable_item_list_add( + app->varList, "Service", 96, callback_manual_pid_sender_options, app); + variable_item_set_current_value_index(item, service_to_send); + variable_item_set_current_value_text(item, furi_string_get_cstr(text)); + + // Third item COUNT OF BYTES [2] + furi_string_reset(text); + furi_string_cat_printf(text, "%u", count_of_bytes); + item = variable_item_list_add( + app->varList, "Count Bytes", 7, callback_manual_pid_sender_options, app); + variable_item_set_current_value_index(item, 0); + variable_item_set_current_value_text(item, furi_string_get_cstr(text)); + variable_item_set_current_value_index(item, count_of_bytes); + + // Fourth item [3] + furi_string_reset(text); + furi_string_cat_printf(text, "0x%x", code_to_send); + item = variable_item_list_add(app->varList, "Pid", 0, callback_manual_pid_sender_options, app); + variable_item_set_current_value_text(item, furi_string_get_cstr(text)); + + // Fifth Element to set (message lenght) [4] + furi_string_reset(text); + furi_string_cat_printf(text, "%u", lenght_pid); + + item = variable_item_list_add( + app->varList, "Lenght", 255, callback_manual_pid_sender_options, app); + variable_item_set_current_value_text(item, furi_string_get_cstr(text)); + variable_item_set_current_value_index(item, lenght_pid); + + // Item to request the data [5] + item = variable_item_list_add( + app->varList, "Send Request", 0, callback_manual_pid_sender_options, app); + variable_item_list_set_enter_callback(app->varList, callback_manual_input_pid_options, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, VarListView); +} + +bool app_scene_manual_sender_pid_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void app_scene_manual_sender_pid_on_exit(void* context) { + App* app = context; + variable_item_list_reset(app->varList); +} + +/* + Scene to set the pid code +*/ + +void input_manual_pid(void* context) { + App* app = context; + uint32_t state = + scene_manager_get_scene_state(app->scene_manager, app_scene_input_manual_pid_option); + + switch(state) { + case 0: // can id + + can_id = byte_values[3] | (byte_values[2] << 8) | (byte_values[1] << 16) | + (byte_values[0] << 24); + break; + + case 1: // service + service_to_send = byte_values[0]; + break; + + case 3: // pid + code_to_send = byte_values[0]; + break; + + default: + break; + } + + view_dispatcher_send_custom_event(app->view_dispatcher, ReturnEvent); +} + +void app_scene_input_manual_set_pid_on_enter(void* context) { + App* app = context; + ByteInput* scene = app->input_byte_value; + uint8_t count_bytes = 0; + + uint32_t scene_state = + scene_manager_get_scene_state(app->scene_manager, app_scene_input_manual_pid_option); + + switch(scene_state) { + case 0: + count_bytes = 4; + + byte_values[3] = can_id; + byte_values[2] = can_id >> 8; + byte_values[1] = can_id >> 16; + byte_values[0] = can_id >> 24; + break; + + case 1: + count_bytes = 1; + byte_values[0] = service_to_send; + break; + + case 3: + count_bytes = 1; + byte_values[0] = code_to_send; + + break; + + default: + break; + } + + byte_input_set_result_callback(scene, input_manual_pid, NULL, app, byte_values, count_bytes); + byte_input_set_header_text(scene, "SET VALUE"); + + view_dispatcher_switch_to_view(app->view_dispatcher, InputByteView); +} + +bool app_scene_input_manual_set_pid_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case ReturnEvent: + scene_manager_previous_scene(app->scene_manager); + break; + + default: + break; + } + } + return consumed; +} + +void app_scene_input_manual_set_pid_on_exit(void* context) { + UNUSED(context); +} + +/* + Scene to show the response in the scene +*/ + +void app_scene_response_manual_pid_on_enter(void* context) { + App* app = context; + text_box_reset(app->textBox); + app->thread = + furi_thread_alloc_ex("ManualPID", 1024, obdii_thread_response_manual_sender_on_work, app); + furi_thread_start(app->thread); + text_box_set_focus(app->textBox, TextBoxFocusEnd); + + view_dispatcher_switch_to_view(app->view_dispatcher, TextBoxView); +} + +bool app_scene_response_manual_pid_on_event(void* context, SceneManagerEvent event) { + bool consumed = false; + UNUSED(context); + UNUSED(event); + return consumed; +} + +void app_scene_response_manual_pid_on_exit(void* context) { + App* app = context; + furi_thread_join(app->thread); + furi_thread_free(app->thread); + text_box_reset(app->textBox); +} + +/* + Scene to set if the device wasnt sent okay or the device is not connected +*/ + +void app_scene_obdii_warnings_on_enter(void* context) { + App* app = context; + + uint32_t state = + scene_manager_get_scene_state(app->scene_manager, app_scene_obdii_warning_scenes); + + if(state == 0) draw_device_no_connected(app); + if(state == 1) draw_transmition_failure(app); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewWidget); +} + +bool app_scene_obdii_warnings_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + + return false; +} + +void app_scene_obdii_warnings_on_exit(void* context) { + App* app = context; + widget_reset(app->widget); +} + +/* + Thread to Send and Received the PID message +*/ + +static int32_t obdii_thread_response_manual_sender_on_work(void* context) { + App* app = context; + + OBDII scanner; + + CANFRAME canframes[lenght_pid]; + + memset(canframes, 0, sizeof(canframes)); + + FuriString* text = app->text; + + furi_string_reset(text); + + scanner.bitrate = app->mcp_can->bitRate; + + bool run = pid_init(&scanner); + + if(run) { + // Time delay added to initialize + furi_delay_ms(500); + + furi_string_printf(text, "DEVICE CONNECTED!...\n"); + furi_string_cat_printf( + text, + "-> %lx %u %x %x 0 0 0 0 0\n", + can_id, + count_of_bytes, + service_to_send, + code_to_send); + + text_box_set_text(app->textBox, furi_string_get_cstr(text)); + + if(pid_manual_request( + &scanner, + can_id, + service_to_send, + code_to_send, + canframes, + lenght_pid, + count_of_bytes)) { + for(uint8_t i = 0; i < lenght_pid; i++) { + CANFRAME frame_received = canframes[i]; + if(frame_received.canId == 0x00) break; + furi_string_cat_printf( + text, + "<-(%u) %lx %x %x %x %x %x %x %x %x\n", + i, + frame_received.canId, + frame_received.buffer[0], + frame_received.buffer[1], + frame_received.buffer[2], + frame_received.buffer[3], + frame_received.buffer[4], + frame_received.buffer[5], + frame_received.buffer[6], + frame_received.buffer[7]); + } + + text_box_reset(app->textBox); + text_box_set_text(app->textBox, furi_string_get_cstr(text)); + } else { + text_box_reset(app->textBox); + furi_string_cat_printf(text, "FAILURE TRANSMITION!!\n"); + text_box_set_text(app->textBox, furi_string_get_cstr(text)); + } + + } else { + furi_string_printf(text, "DEVICE NOT CONNECTED!"); + text_box_set_text(app->textBox, furi_string_get_cstr(text)); + } + + pid_deinit(&scanner); + + return 0; +} diff --git a/Canbus_app/scenes/Obd2Options/supportedPIDOption.c b/Canbus_app/scenes/Obd2Options/supportedPIDOption.c new file mode 100644 index 0000000..4d3cd3f --- /dev/null +++ b/Canbus_app/scenes/Obd2Options/supportedPIDOption.c @@ -0,0 +1,182 @@ +#include "../../app_user.h" + +// Thread +static int32_t obdii_thread_getting_pid_supported_on_work(void* context); + +// Callback for the supported menu +void supported_pid_callback(void* context, uint32_t index) { + App* app = context; + + UNUSED(app); + + switch(index) { + case 0x100: + app->flags = BLOCK_A; + scene_manager_next_scene(app->scene_manager, app_scene_list_supported_pid_option); + break; + + case 0x101: + app->flags = BLOCK_B; + scene_manager_next_scene(app->scene_manager, app_scene_list_supported_pid_option); + break; + + case 0x102: + app->flags = BLOCK_C; + scene_manager_next_scene(app->scene_manager, app_scene_list_supported_pid_option); + break; + + case 0x103: + app->flags = BLOCK_D; + scene_manager_next_scene(app->scene_manager, app_scene_list_supported_pid_option); + break; + + default: + break; + } +} + +// Scene on enter +void app_scene_menu_supported_pid_on_enter(void* context) { + App* app = context; + submenu_reset(app->submenu); + submenu_set_header(app->submenu, "Get Supported PID"); + submenu_add_item(app->submenu, "Block A", 0x100, supported_pid_callback, app); + submenu_add_item(app->submenu, "Block B", 0x101, supported_pid_callback, app); + submenu_add_item(app->submenu, "Block C", 0x102, supported_pid_callback, app); + submenu_add_item(app->submenu, "Block D", 0x103, supported_pid_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, SubmenuView); +} + +// Scene on Event +bool app_scene_menu_supported_pid_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + return consumed; +} + +// Scene on exit +void app_scene_menu_supported_pid_on_exit(void* context) { + App* app = context; + submenu_reset(app->submenu); +} + +/* + Scene of the pid list +*/ + +// Scene on enter +void app_scene_list_supported_pid_on_enter(void* context) { + App* app = context; + submenu_reset(app->submenu); + + if(scene_manager_get_scene_state(app->scene_manager, app_scene_list_supported_pid_option) == + 1) { + scene_manager_set_scene_state(app->scene_manager, app_scene_list_supported_pid_option, 0); + scene_manager_previous_scene(app->scene_manager); + } else { + app->thread = furi_thread_alloc_ex( + "GetSupportedPID", 1024, obdii_thread_getting_pid_supported_on_work, app); + furi_thread_start(app->thread); + } + + view_dispatcher_switch_to_view(app->view_dispatcher, SubmenuView); +} + +// Scene on event +bool app_scene_list_supported_pid_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + if(event.event == DEVICE_NO_CONNECTED) { + consumed = true; + + // Go to the scene + scene_manager_set_scene_state(app->scene_manager, app_scene_list_supported_pid_option, 1); + + // Set the scene to know the error + scene_manager_set_scene_state(app->scene_manager, app_scene_obdii_warning_scenes, 0); + scene_manager_next_scene(app->scene_manager, app_scene_obdii_warning_scenes); + } + if(event.event == MESSAGE_ERROR) { + scene_manager_set_scene_state(app->scene_manager, app_scene_list_supported_pid_option, 0); + + // Set the scene to know the error + scene_manager_set_scene_state(app->scene_manager, app_scene_list_supported_pid_option, 1); + scene_manager_set_scene_state(app->scene_manager, app_scene_obdii_warning_scenes, 1); + scene_manager_next_scene(app->scene_manager, app_scene_obdii_warning_scenes); + } + return consumed; +} + +// Scene on exit +void app_scene_list_supported_pid_on_exit(void* context) { + App* app = context; + if(scene_manager_get_scene_state(app->scene_manager, app_scene_list_supported_pid_option) == + 0) { + furi_thread_join(app->thread); + furi_thread_free(app->thread); + } + + submenu_reset(app->submenu); +} + +/* + Thread to get the supported PID +*/ + +static int32_t obdii_thread_getting_pid_supported_on_work(void* context) { + App* app = context; + + OBDII scanner; + + scanner.bitrate = app->mcp_can->bitRate; + + bool run = pid_init(&scanner); + + bool draw_list = false; + + FuriString* text = app->text; + + // if the device is not detected the thread send an custom event + if(!run) { + scene_manager_set_scene_state(app->scene_manager, app_scene_supported_pid_option, 1); + view_dispatcher_send_custom_event(app->view_dispatcher, DEVICE_NO_CONNECTED); + } + + // if it runs + if(run) { + // Time delay added to initialize + furi_delay_ms(500); + + uint32_t flag = app->flags; + + if(pid_get_supported_pid(&scanner, flag)) { + draw_list = true; + + } else { + view_dispatcher_send_custom_event(app->view_dispatcher, MESSAGE_ERROR); + } + + if(draw_list) { + for(uint8_t i = 1; i <= 32; i++) { + furi_string_reset(text); + + if(scanner.codes[i + flag].is_supported) { + furi_string_cat_printf(text, "0x%lx Supported", i + flag); + } + + if(!scanner.codes[i + flag].is_supported) { + furi_string_cat_printf(text, "0x%lx Not Supported", i + flag); + } + + submenu_add_item( + app->submenu, furi_string_get_cstr(text), i, supported_pid_callback, app); + } + } + } + + pid_deinit(&scanner); + + return 0; +} diff --git a/Canbus_app/scenes/Obd2Options/typicalPIDOption.c b/Canbus_app/scenes/Obd2Options/typicalPIDOption.c new file mode 100644 index 0000000..869a9f3 --- /dev/null +++ b/Canbus_app/scenes/Obd2Options/typicalPIDOption.c @@ -0,0 +1,278 @@ +#include "../../app_user.h" + +static int32_t obdii_thread_on_work(void* context); + +/* + + SHOW TYPICAL DATA ON PID CODES + +*/ + +// Selector of option +void typical_menu_callback(void* context, uint32_t index) { + App* app = context; + + app->obdii_aux_index = index; + scene_manager_set_scene_state(app->scene_manager, app_scene_obdii_typical_codes_option, index); + scene_manager_next_scene( + app->scene_manager, + app_scene_draw_obii_option); // It will go directly to the respective scene +} + +// Scene on enter +void app_scene_obdii_typical_codes_on_enter(void* context) { + App* app = context; + submenu_reset(app->submenu); + submenu_add_item(app->submenu, "Engine Speed", 0, typical_menu_callback, app); + submenu_add_item(app->submenu, "Vehicle Speed", 1, typical_menu_callback, app); + submenu_add_item(app->submenu, "Calculated Engine Load", 2, typical_menu_callback, app); + submenu_add_item(app->submenu, "Thortle Position", 3, typical_menu_callback, app); + submenu_add_item(app->submenu, "Fuel Tank Input Level", 4, typical_menu_callback, app); + submenu_add_item(app->submenu, "Thortle Relative Position", 5, typical_menu_callback, app); + submenu_add_item(app->submenu, "Time Engine Running", 6, typical_menu_callback, app); + + submenu_set_selected_item( + app->submenu, + scene_manager_get_scene_state(app->scene_manager, app_scene_obdii_typical_codes_option)); + view_dispatcher_switch_to_view(app->view_dispatcher, SubmenuView); + scene_manager_set_scene_state(app->scene_manager, app_scene_obdii_typical_codes_option, 0); +} + +// Scene on Event +bool app_scene_obdii_typical_codes_on_event(void* context, SceneManagerEvent event) { + App* app = context; + + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +// Scene on exit +void app_scene_obdii_typical_codes_on_exit(void* context) { + App* app = context; + submenu_reset(app->submenu); +} + +/* + + THIS SCENE WORKS TO DISPLAY THE RESPECTIVE DATA ON THE FLIPPER + SHOWING THE TYPICAL DATA + +*/ + +// Draw the unit medition +void draw_value(App* app, const char* text) { + widget_add_string_element(app->widget, 65, 45, AlignCenter, AlignBottom, FontPrimary, text); +} + +// Draws the data +void draw_scene(App* app, uint8_t selector, uint16_t variable) { + FuriString* text_label = app->text; + + char* text = ""; + + furi_string_reset(text_label); + widget_reset(app->widget); + + if(selector == 0) { // Engine Speed + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "ENGINE SPEED"); + text = "RPM"; + } else if(selector == 1) { // Vehicle Speed + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "VEHICLE SPEED"); + text = "KM/H"; + + } else if(selector == 2) { // CALCULATED ENGINE LOAD + widget_add_string_element( + app->widget, 65, 15, AlignCenter, AlignBottom, FontPrimary, "CALCULATED"); + + widget_add_string_element( + app->widget, 65, 30, AlignCenter, AlignBottom, FontPrimary, "ENGINE LOAD"); + + text = "%"; + + } else if(selector == 3) { // Thortle Position + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "THORLE POSITION"); + + text = "%"; + + } else if(selector == 4) { // Fuel Tank Input Level + widget_add_string_element( + app->widget, 65, 15, AlignCenter, AlignBottom, FontPrimary, "FUEL TANK"); + widget_add_string_element( + app->widget, 65, 30, AlignCenter, AlignBottom, FontPrimary, "INPUT LEVEL"); + + text = "%"; + + } else if(selector == 5) { // Thortle Relative Position + widget_add_string_element( + app->widget, 65, 15, AlignCenter, AlignBottom, FontPrimary, "THORTLE"); + widget_add_string_element( + app->widget, 65, 30, AlignCenter, AlignBottom, FontPrimary, "RELATIVE POSITION"); + + text = "%"; + } else if(selector == 6) { + widget_add_string_element( + app->widget, 65, 15, AlignCenter, AlignBottom, FontPrimary, "TIME SINCE"); + widget_add_string_element( + app->widget, 65, 30, AlignCenter, AlignBottom, FontPrimary, "ENGINE START"); + + text = "seg"; + } + + furi_string_cat_printf(text_label, "%u %s", variable, text); + + draw_value(app, furi_string_get_cstr(text_label)); +} + +// Draw the waiting data +void draw_waiting_data(App* app) { + widget_reset(app->widget); + widget_add_string_element( + app->widget, 64, 32, AlignCenter, AlignCenter, FontPrimary, "Waiting Data"); +} + +// Scene on enter +void app_scene_draw_obdii_on_enter(void* context) { + App* app = context; + + app->thread = furi_thread_alloc_ex("GetDataPID", 1024, obdii_thread_on_work, app); + furi_thread_start(app->thread); + + widget_reset(app->widget); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewWidget); +} + +// Scene on Event +bool app_scene_draw_obdii_on_event(void* context, SceneManagerEvent event) { + App* app = context; + + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +// Scene on exit +void app_scene_draw_obdii_on_exit(void* context) { + App* app = context; + furi_thread_join(app->thread); + furi_thread_free(app->thread); + + widget_reset(app->widget); +} + +/* + Thread To Show DATA +*/ + +static int32_t obdii_thread_on_work(void* context) { + App* app = context; + OBDII scanner = app->obdii; + + scanner.bitrate = app->mcp_can->bitRate; + + uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t request = 0; + + bool state; + bool message; + + uint32_t option = + scene_manager_get_scene_state(app->scene_manager, app_scene_obdii_typical_codes_option); + + switch(option) { + case 0: + request = ENGINE_SPEED; + break; + + case 1: + request = VEHICLE_SPEED; + break; + + case 2: + request = CALCULATED_ENGINE_LOAD; + break; + + case 3: + request = THROTTLE_POSITION; + break; + + case 4: + request = FUEL_INPUT_POSITION; + break; + + case 5: + request = THROTTLE_RELATIVE_POSITION; + break; + + case 6: + request = RUN_TIME_SINCE_ENGINE_START; + break; + + default: + break; + } + + state = pid_init(&scanner); + + uint32_t time_delay = furi_get_tick(); + + if(!state) + draw_device_no_connected(app); + else + draw_waiting_data(app); + + while(state && furi_hal_gpio_read(&gpio_button_back)) { + if((furi_get_tick() - time_delay) > 10) { + message = pid_show_data(&scanner, request, data, 8); + + if(message) { + switch(option) { + case 0: // Engine Speed RPM + draw_scene(app, option, calculate_engine_speed(data[3], data[4])); + break; + + case 1: // Vehcile Speed + draw_scene(app, option, data[3]); + break; + + case 2: // Calculated Engine Load + draw_scene(app, option, calculate_engine_load(data[3])); + break; + + case 3: // Thortle Position + draw_scene(app, option, calculate_engine_load(data[3])); + break; + + case 4: // Fuel Tank Input Level + draw_scene(app, option, calculate_engine_load(data[3])); + break; + + case 5: // Thortle Relative Position + draw_scene(app, option, calculate_engine_load(data[3])); + break; + + case 6: // Time Engine Running + draw_scene(app, option, sum_value(data[3], data[4])); + break; + + case 7: + + default: + draw_in_development(app); + break; + } + } + + time_delay = furi_get_tick(); + } + } + pid_deinit(&scanner); + return 0; +} diff --git a/Canbus_app/scenes/SnifferOption.c b/Canbus_app/scenes/SnifferOption.c index a3cbf26..75170e9 100644 --- a/Canbus_app/scenes/SnifferOption.c +++ b/Canbus_app/scenes/SnifferOption.c @@ -2,8 +2,7 @@ bool condition = true; -// --------------------- Functons to save the logs -// -------------------------------------------------- +// Function to save logs char* sequential_file_resolve_path( Storage* storage, const char* dir, @@ -105,11 +104,12 @@ void close_file_on_data_log(App* app) { static void write_data_on_file(CANFRAME frame, File* file, uint32_t time) { FuriString* text_file = furi_string_alloc(); - furi_string_cat_printf(text_file, "id:%lx \t\tlen: %u \t", frame.canId, frame.data_lenght); + + furi_string_cat_printf(text_file, "(%li) %lx %u", time, frame.canId, frame.data_lenght); for(uint8_t i = 0; i < (frame.data_lenght); i++) { - furi_string_cat_printf(text_file, "[%u]:%u \t\t", i, frame.buffer[i]); + furi_string_cat_printf(text_file, " %x", frame.buffer[i]); } - furi_string_cat_printf(text_file, " Time(ms): %li\n", time); + furi_string_cat_printf(text_file, "\n"); storage_file_write(file, furi_string_get_cstr(text_file), furi_string_size(text_file)); furi_string_reset(text_file); furi_string_free(text_file); @@ -141,9 +141,6 @@ void draw_box_text(App* app) { text_box_set_focus(app->textBox, TextBoxFocusEnd); } -// --------------------------- Thread on work -// ------------------------------------------------------ - static int32_t worker_sniffing(void* context) { App* app = context; MCP2515* mcp_can = app->mcp_can; @@ -151,8 +148,7 @@ static int32_t worker_sniffing(void* context) { FuriString* text_label = furi_string_alloc(); uint8_t num_of_devices = 0; - uint32_t time_select = 0; - UNUSED(time_select); + uint32_t current_time = 0; bool run = true; bool first_address = true; @@ -169,6 +165,8 @@ static int32_t worker_sniffing(void* context) { bool new = true; if(read_can_message(mcp_can, &frame) == ERROR_OK) { + current_time = furi_get_tick(); + if(first_address) { app->frameArray[num_of_devices] = frame; app->times[num_of_devices] = 0; @@ -230,7 +228,7 @@ static int32_t worker_sniffing(void* context) { if(app->log_file_ready && (app->save_logs == SaveAll)) { app->can_frame = frame; - write_data_on_file(app->can_frame, app->log_file, app->time); + write_data_on_file(app->can_frame, app->log_file, current_time); } } @@ -246,7 +244,7 @@ static int32_t worker_sniffing(void* context) { write_data_on_file( app->frameArray[app->sniffer_index], app->log_file, - app->times[app->sniffer_index]); + app->current_time[app->sniffer_index]); } } } @@ -270,9 +268,6 @@ static int32_t worker_sniffing(void* context) { return 0; } -// ------------------------------------------------------ SNIFFING MENU SCENE -// --------------------------- - void app_scene_sniffing_on_enter(void* context) { App* app = context; @@ -306,6 +301,7 @@ bool app_scene_sniffing_on_event(void* context, SceneManagerEvent event) { break; case DEVICE_NO_CONNECTED: + scene_manager_set_scene_state(app->scene_manager, app_scene_sniffing_option, 1); scene_manager_next_scene(app->scene_manager, app_scene_device_no_connected); consumed = true; break; @@ -328,9 +324,6 @@ void app_scene_sniffing_on_exit(void* context) { } } -//-------------------------- FOR THE SNIFFING BOX -//-------------------------------------------------------- - void app_scene_box_sniffing_on_enter(void* context) { App* app = context; @@ -357,33 +350,3 @@ void app_scene_box_sniffing_on_exit(void* context) { furi_string_reset(app->text); text_box_reset(app->textBox); } - -//-------------------------- FOR THE UNPLUG DEVICE -//-------------------------------------------------------- - -void app_scene_device_no_connected_on_enter(void* context) { - App* app = context; - widget_reset(app->widget); - - widget_add_string_element( - app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "DEVICE NO"); - - widget_add_string_element( - app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "CONNECTED"); - - view_dispatcher_switch_to_view(app->view_dispatcher, ViewWidget); -} - -bool app_scene_device_no_connected_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - bool consumed = false; - - return consumed; -} - -void app_scene_device_no_connected_on_exit(void* context) { - App* app = context; - widget_reset(app->widget); - scene_manager_set_scene_state(app->scene_manager, app_scene_sniffing_option, 1); -} diff --git a/Canbus_app/scenes/mainMenu.c b/Canbus_app/scenes/mainMenu.c index 02c4bc8..c8a1c93 100644 --- a/Canbus_app/scenes/mainMenu.c +++ b/Canbus_app/scenes/mainMenu.c @@ -70,10 +70,21 @@ void basic_scenes_menu_callback(void* context, uint32_t index) { case SenderOption: scene_manager_handle_custom_event(app->scene_manager, SenderOptionEvent); break; + + case PlayLOGOption: + + scene_manager_handle_custom_event(app->scene_manager, PlayLOGOptionEvent); + + break; + case SettingsOption: scene_manager_handle_custom_event(app->scene_manager, SettingsOptionEvent); break; + case ObdiiOption: + scene_manager_handle_custom_event(app->scene_manager, ObdiiOptionEvent); + break; + case ReadLOGOption: if(OpenLogFile(app)) { scene_manager_next_scene(app->scene_manager, app_scene_read_logs); @@ -100,6 +111,10 @@ void app_scene_menu_on_enter(void* context) { submenu_add_item(app->submenu, "Sender", SenderOption, basic_scenes_menu_callback, app); + submenu_add_item(app->submenu, "Player", PlayLOGOption, basic_scenes_menu_callback, app); + + submenu_add_item(app->submenu, "Scanner OBD2", ObdiiOption, basic_scenes_menu_callback, app); + submenu_add_item(app->submenu, "Read LOG", ReadLOGOption, basic_scenes_menu_callback, app); submenu_add_item(app->submenu, "Settings", SettingsOption, basic_scenes_menu_callback, app); @@ -111,6 +126,7 @@ void app_scene_menu_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, SubmenuView); reset_sender_values(app); + app->obdii_aux_index = 0; } bool app_scene_menu_on_event(void* context, SceneManagerEvent event) { @@ -129,11 +145,19 @@ bool app_scene_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, app_scene_sender_option); break; + case PlayLOGOptionEvent: + scene_manager_next_scene(app->scene_manager, app_scene_play_logs); + break; + case SettingsOptionEvent: scene_manager_next_scene(app->scene_manager, app_scene_settings_option); consumed = true; break; + case ObdiiOptionEvent: + scene_manager_next_scene(app->scene_manager, app_scene_obdii_option); + break; + case ReadLOGOptionEvent: scene_manager_next_scene(app->scene_manager, app_scene_read_logs); consumed = true; diff --git a/Canbus_app/scenes/playLogs.c b/Canbus_app/scenes/playLogs.c new file mode 100644 index 0000000..c651fb3 --- /dev/null +++ b/Canbus_app/scenes/playLogs.c @@ -0,0 +1,538 @@ +#include "../app_user.h" +#define TAG "PLAY " +#define MAX_UNIQUE_IDS 100 + +typedef struct { + uint32_t id; + bool enabled; +} UniqueId; + +typedef enum { + PLAY_OK, + PLAY_ERROR, +} player_status; + +typedef enum { + TIMING_DEFAULT, + TIMING_CUSTOM, + TIMING_TIMESTAMP, +} timing; + +// Timing configuration +const uint8_t config_timing_values[] = { + 0x00, + 0x01, + 0x02, +}; +const char* const config_timing_names[] = { + "DEFAULT", + "CUSTOM", + "TIMESTAMP", +}; + +const char* multiply_timing[] = {"x1", "x10", "x100", "x1000"}; + +// Costum time +uint32_t costum_timing = 1; +uint32_t multiply = 0; + +uint32_t hex_to_int(const char* hex_str) { + unsigned int result = 0; + sscanf(hex_str, "%x", &result); + return (uint32_t)result; +} + +char* custom_strtok_r(char* str, const char* delim, char** saveptr) { + if(str) { + *saveptr = str; + } + if(!*saveptr) { + return NULL; + } + + char* start = *saveptr; + while(*start && strchr(delim, *start)) { + ++start; + } + if(*start == '\0') { + *saveptr = NULL; + return NULL; + } + + char* end = start; + while(*end && !strchr(delim, *end)) { + ++end; + } + + if(*end) { + *end = '\0'; + *saveptr = end + 1; + } else { + *saveptr = NULL; + } + + return start; +} + +void path_file_name(const char* path, FuriString* file_name) { + uint8_t last_pos = 0; + for(uint8_t i = 0; path[i] != '\0'; i++) { + if(path[i] == '/') last_pos = i + 1; + } + + furi_string_reset(file_name); + + for(uint8_t i = last_pos; path[i] != '\0'; i++) { + furi_string_cat_printf(file_name, "%c", path[i]); + } +} + +// For the device no connected +void draw_device_fail_connected(App* app) { + widget_reset(app->widget); + + widget_add_string_element( + app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "DEVICE NO"); + + widget_add_string_element( + app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "CONNECTED"); +} + +// For the message +void draw_message_send(App* app) { + widget_reset(app->widget); + widget_add_string_element( + app->widget, 62, 30, AlignCenter, AlignBottom, FontPrimary, "Sending Ok..."); + + widget_add_string_element( + app->widget, + 62, + 50, + AlignCenter, + AlignBottom, + FontSecondary, + furi_string_get_cstr(app->text)); +} + +// Message sent fail +void draw_message_fail(App* app) { + widget_reset(app->widget); + widget_add_string_element( + app->widget, 62, 30, AlignCenter, AlignBottom, FontPrimary, "Sending Fail..."); + + widget_add_string_element( + app->widget, + 62, + 50, + AlignCenter, + AlignBottom, + FontSecondary, + furi_string_get_cstr(app->text)); +} + +void draw_finished(App* app) { + widget_reset(app->widget); + widget_add_string_element( + app->widget, 62, 32, AlignCenter, AlignBottom, FontPrimary, "Replay Finished"); +} + +void draw_file_no_opened(App* app) { + widget_reset(app->widget); + widget_add_string_element( + app->widget, 62, 32, AlignCenter, AlignBottom, FontPrimary, "File cannot be opened"); +} + +void draw_starting_transmition(App* app) { + widget_reset(app->widget); + widget_add_string_element( + app->widget, 62, 32, AlignCenter, AlignBottom, FontPrimary, "Starting Transmition"); +} + +void play_data_frames_bk(void* context, int frame_interval) { + App* app = context; + + app->mcp_can->mode = MCP_NORMAL; + ERROR_CAN debug = ERROR_OK; + debug = mcp2515_init(app->mcp_can); + + if(debug != ERROR_OK) { + draw_device_fail_connected(app); + return; + } + + if(!storage_file_open( + app->log_file, furi_string_get_cstr(app->path), FSAM_READ, FSOM_OPEN_EXISTING)) { + draw_file_no_opened(app); + return; + } + + draw_starting_transmition(app); + + char buffer[256]; + size_t buffer_index = 0; + size_t bytes_read; + char c; + + // [Code for collecting unique IDs remains the same...] + buffer_index = 0; + + // Read first character for next frame + bytes_read = storage_file_read(app->log_file, &c, 1); + + // To have the time + uint32_t delay = 0; + uint32_t offset_time = 0; + bool first_frame = true; + + while(bytes_read > 0) { + // Read current frame into buffer + while((bytes_read = storage_file_read(app->log_file, &c, 1)) > 0) { + if(c == '\n' || buffer_index >= 255) { + buffer[buffer_index] = '\0'; + break; + } + buffer[buffer_index++] = c; + } + buffer[buffer_index] = '\0'; + + // Process current frame + CANFRAME frame_to_send = {0}; + char* saveptr; + char* endptr; + char* token; + + // Get current timestamp + token = custom_strtok_r(buffer, "() ", &saveptr); + if(!token) break; + + // Get next timestamp if available + uint32_t delay_ms = strtod(token, &endptr); + + // CAN bus ID + token = custom_strtok_r(NULL, " ", &saveptr); + if(!token) break; + frame_to_send.canId = hex_to_int(token); + + // Data length + token = custom_strtok_r(NULL, " ", &saveptr); + if(!token) break; + frame_to_send.data_lenght = (uint8_t)atoi(token); + + // Fill the data buffer + for(int i = 0; i < frame_to_send.data_lenght && i < MAX_LEN; i++) { + token = custom_strtok_r(NULL, " ", &saveptr); + if(!token) break; + frame_to_send.buffer[i] = (uint8_t)hex_to_int(token); + } + + // To get the offset time + if(first_frame) { + offset_time = delay_ms; + first_frame = false; + } + + // Apply timing based on frame_interval mode + switch(frame_interval) { + case TIMING_CUSTOM: + if(costum_timing == 0) { + costum_timing = 1; + } + + delay = costum_timing * pow(10, multiply); + + if(first_frame) { + delay = 0; + first_frame = false; + } + + break; + + case TIMING_TIMESTAMP: + + delay = (delay_ms - offset_time); + + break; + case TIMING_DEFAULT: + delay = 1000; + break; + } + + uint32_t current_time = furi_get_tick(); + + // Delay with real time it works to exit from the while + while((furi_get_tick() - current_time) < delay) { + if(!furi_hal_gpio_read(&(gpio_button_back))) { + storage_file_close(app->log_file); + free_mcp2515(app->mcp_can); + return; + } + } + + furi_string_reset(app->text); + furi_string_cat_printf( + app->text, "%lx %u", frame_to_send.canId, frame_to_send.data_lenght); + + for(uint8_t i = 0; i < frame_to_send.data_lenght; i++) { + furi_string_cat_printf(app->text, " %x", frame_to_send.buffer[i]); + } + + if(send_can_frame(app->mcp_can, &frame_to_send) != ERROR_OK) { + draw_message_fail(app); + } else { + draw_message_send(app); + } + + // Set the last time + offset_time = delay_ms; + buffer_index = 0; + } + + furi_delay_ms(1000); + draw_finished(app); + + storage_file_close(app->log_file); + free_mcp2515(app->mcp_can); +} + +// Thread work +int32_t thread_play_logs(void* context) { + App* app = context; + play_data_frames_bk(app, app->config_timing_index); + + return 0; +} + +void draw_list(App* app); +void draw_list_costum(App* app); + +// Option callback using button OK +void callback_input_player_options(void* context, uint32_t index) { + App* app = context; + + UNUSED(app); + + switch(index) { + case 0: + scene_manager_next_scene(app->scene_manager, app_scene_play_logs_widget); + break; + + case 1: + scene_manager_next_scene(app->scene_manager, app_scene_file_browser_option); + break; + + default: + break; + } +} + +// Options Callback +void callback_player_timing_options(VariableItem* item) { + App* app = variable_item_get_context(item); + + uint8_t selected_index = variable_item_list_get_selected_item_index(app->varList); + + switch(selected_index) { + case 2: + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, config_timing_names[index]); + + app->config_timing_index = index; + + if(app->config_timing_index == TIMING_CUSTOM) { + draw_list_costum(app); + } else { + draw_list(app); + } + + break; + + case 3: + costum_timing = variable_item_get_current_value_index(item); + + variable_item_set_current_value_index(item, costum_timing); + + furi_string_reset(app->text); + furi_string_cat_printf(app->text, "%lu", costum_timing); + + variable_item_set_current_value_text(item, furi_string_get_cstr(app->text)); + + break; + + case 4: + multiply = variable_item_get_current_value_index(item); + + variable_item_set_current_value_index(item, multiply); + + variable_item_set_current_value_text(item, multiply_timing[multiply]); + break; + + default: + break; + } +} + +void draw_list(App* app) { + VariableItem* item; + + // reset list + variable_item_list_reset(app->varList); + + // Play the logs + item = variable_item_list_add(app->varList, "Play", 0, NULL, app); + + // Choose File + item = variable_item_list_add(app->varList, "File", 0, NULL, app); + variable_item_set_current_value_text(item, furi_string_get_cstr(app->data)); + + // Timing options + item = variable_item_list_add( + app->varList, + "Timing", + COUNT_OF(config_timing_values), + callback_player_timing_options, + app); + + variable_item_set_current_value_index(item, app->config_timing_index); + variable_item_set_current_value_text(item, config_timing_names[app->config_timing_index]); + + // Set the enter callback + variable_item_list_set_enter_callback(app->varList, callback_input_player_options, app); +} + +void draw_list_costum(App* app) { + VariableItem* item; + + // reset list + variable_item_list_reset(app->varList); + + // Play the logs + item = variable_item_list_add(app->varList, "Play", 0, NULL, app); + + // Choose File + item = variable_item_list_add(app->varList, "File", 0, NULL, app); + variable_item_set_current_value_text(item, furi_string_get_cstr(app->data)); + + // Timing options + item = variable_item_list_add( + app->varList, + "Timing", + COUNT_OF(config_timing_values), + callback_player_timing_options, + app); + + variable_item_set_current_value_index(item, app->config_timing_index); + variable_item_set_current_value_text(item, config_timing_names[app->config_timing_index]); + + // Timing options + item = + variable_item_list_add(app->varList, "Time(ms)", 99, callback_player_timing_options, app); + variable_item_set_current_value_index(item, costum_timing); + + furi_string_reset(app->text); + furi_string_cat_printf(app->text, "%lu", costum_timing); + + variable_item_set_current_value_text(item, furi_string_get_cstr(app->text)); + + // Set the enter callback + variable_item_list_set_enter_callback(app->varList, callback_input_player_options, app); + + item = variable_item_list_add( + app->varList, "Time(ms)", COUNT_OF(multiply_timing), callback_player_timing_options, app); + variable_item_set_current_value_index(item, multiply); + variable_item_set_current_value_text(item, multiply_timing[multiply]); +} + +// The function to enter a Scene +void app_scene_play_logs_on_enter(void* context) { + App* app = context; + + if(app->config_timing_index == TIMING_CUSTOM) { + draw_list_costum(app); + } else { + draw_list(app); + } + + variable_item_list_set_selected_item(app->varList, 0); + + // Switch View + view_dispatcher_switch_to_view(app->view_dispatcher, VarListView); +} + +// The function to exit the scene +void app_scene_play_logs_on_exit(void* context) { + App* app = context; + variable_item_list_reset(app->varList); +} + +// The function for the events +bool app_scene_play_logs_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + + UNUSED(event); + UNUSED(app); + return consumed; +} + +// File browser callback +void file_browser_callback(void* context) { + App* app = context; + path_file_name(furi_string_get_cstr(app->path), app->data); + scene_manager_previous_scene(app->scene_manager); +} + +// File Browser scene on enter +void app_scene_file_browser_on_enter(void* context) { + App* app = context; + file_browser_configure(app->file_browser, ".log", PATHLOGS, true, true, NULL, true); + file_browser_set_callback(app->file_browser, file_browser_callback, app); + + furi_string_reset(app->text); + furi_string_cat(app->text, PATHLOGS); + + file_browser_start(app->file_browser, app->text); + view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserView); +} + +// File browser scene on event +bool app_scene_file_browser_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + + UNUSED(event); + UNUSED(app); + return consumed; +} + +// File browser on exit +void app_scene_file_browser_on_exit(void* context) { + App* app = context; + UNUSED(app); + file_browser_stop(app->file_browser); +} + +// Test widget +void app_scene_play_logs_widget_on_enter(void* context) { + App* app = context; + app->thread = furi_thread_alloc_ex("PlayLogs", 10 * 1024, thread_play_logs, app); + furi_thread_start(app->thread); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewWidget); +} + +bool app_scene_play_logs_widget_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + + UNUSED(event); + UNUSED(app); + return consumed; +} + +void app_scene_play_logs_widget_on_exit(void* context) { + App* app = context; + + furi_thread_join(app->thread); + furi_thread_free(app->thread); + + widget_reset(app->widget); +} diff --git a/Canbus_app/scenes/senderOption.c b/Canbus_app/scenes/senderOption.c index c28a2aa..6231078 100644 --- a/Canbus_app/scenes/senderOption.c +++ b/Canbus_app/scenes/senderOption.c @@ -23,9 +23,6 @@ typedef enum { SEND_ERROR, } sender_status; -// Threads To work -// ------------------------------------------------------------------------------------ - static int32_t sender_on_work(void* context) { App* app = context; app->mcp_can->mode = MCP_NORMAL; @@ -51,9 +48,6 @@ static int32_t sender_on_work(void* context) { return 0; } -// Scenes -// --------------------------------------------------------------------------------------------- - // Option callback using button OK void callback_input_sender_options(void* context, uint32_t index) { App* app = context; @@ -301,8 +295,6 @@ void app_scene_sender_on_exit(void* context) { variable_item_list_reset(app->varList); } -// ----------------------------- SCENE TO SEND THE FRAME --------------------- - void app_scene_send_message_on_enter(void* context) { App* app = context; @@ -323,25 +315,15 @@ bool app_scene_send_message_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case SEND_OK: - widget_reset(app->widget); - widget_add_string_element( - app->widget, 65, 20, AlignCenter, AlignCenter, FontPrimary, "MESSAGE SEND OK"); + draw_send_ok(app); break; case SEND_ERROR: - widget_reset(app->widget); - widget_add_string_element( - app->widget, 65, 20, AlignCenter, AlignCenter, FontPrimary, "MESSAGE SEND ERROR"); + draw_send_wrong(app); break; case DEVICE_NO_CONNECTED: - widget_reset(app->widget); - - widget_add_string_element( - app->widget, 65, 20, AlignCenter, AlignBottom, FontPrimary, "DEVICE NO"); - - widget_add_string_element( - app->widget, 65, 35, AlignCenter, AlignBottom, FontPrimary, "CONNECTED"); + draw_device_no_connected(app); break; default: @@ -360,8 +342,6 @@ void app_scene_send_message_on_exit(void* context) { widget_reset(app->widget); } -// ---------------------------- WARNING TO GET THE ID'S ------------------------ - void app_scene_warning_log_on_enter(void* context) { App* app = context; widget_reset(app->widget); @@ -448,8 +428,6 @@ void app_scene_id_list_on_exit(void* context) { submenu_reset(app->submenu); } -// ---------------------------- TO SET THE VALUE OF THE FRAME ------------------ - void input_byte_sender_callback(void* context) { App* app = context; app->frame_to_send->canId = app->sender_id_compose[3] | (app->sender_id_compose[2] << 8) | diff --git a/Canbus_app/scenes/warningScene.c b/Canbus_app/scenes/warningScene.c new file mode 100644 index 0000000..f11e495 --- /dev/null +++ b/Canbus_app/scenes/warningScene.c @@ -0,0 +1,21 @@ +#include "../app_user.h" + +void app_scene_device_no_connected_on_enter(void* context) { + App* app = context; + draw_device_no_connected(app); + + view_dispatcher_switch_to_view(app->view_dispatcher, ViewWidget); +} + +bool app_scene_device_no_connected_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + + return consumed; +} + +void app_scene_device_no_connected_on_exit(void* context) { + App* app = context; + widget_reset(app->widget); +} diff --git a/Canbus_app/scenes_config/app_scene_config.h b/Canbus_app/scenes_config/app_scene_config.h index 23e92a8..fde3005 100644 --- a/Canbus_app/scenes_config/app_scene_config.h +++ b/Canbus_app/scenes_config/app_scene_config.h @@ -22,3 +22,20 @@ ADD_SCENE(app, input_text, input_text_option) ADD_SCENE(app, read_logs, read_logs) ADD_SCENE(app, settings, settings_option) ADD_SCENE(app, about_us, about_us) + +// On development +ADD_SCENE(app, obdii_menu, obdii_option) +ADD_SCENE(app, obdii_typical_codes, obdii_typical_codes_option) +ADD_SCENE(app, draw_obdii, draw_obii_option) +ADD_SCENE(app, menu_supported_pid, supported_pid_option) +ADD_SCENE(app, list_supported_pid, list_supported_pid_option) +ADD_SCENE(app, obdii_warnings, obdii_warning_scenes) +ADD_SCENE(app, obdii_get_errors, obdii_get_errors_option) +ADD_SCENE(app, obdii_delete_dtc, obdii_delete_errors_option) +ADD_SCENE(app, manual_sender_pid, manual_sender_pid_option) +ADD_SCENE(app, response_manual_pid, response_pid_option) +ADD_SCENE(app, input_manual_set_pid, input_manual_pid_option) +ADD_SCENE(app, get_car_data, car_data_option) +ADD_SCENE(app, play_logs, play_logs) +ADD_SCENE(app, file_browser, file_browser_option) +ADD_SCENE(app, play_logs_widget, play_logs_widget)