Explorar o código

Add CoffeeEEPROM fap

wh00hw %!s(int64=2) %!d(string=hai) anos
pai
achega
f1ba4ccdf7

+ 14 - 0
base_pack/coffee_eeprom/application.fam

@@ -0,0 +1,14 @@
+App(
+    appid="coffee_eeprom",
+    name="Coffee EEPROM",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="coffee_eeprom_main",
+    cdefines=["APP_COFFEE_EEPROM"],
+    requires=[
+        "gui",
+    ],
+    stack_size=4 * 1024,
+    order=1,
+    fap_category="GPIO",
+    fap_icon="coffee_10px.png"
+)

+ 134 - 0
base_pack/coffee_eeprom/coffee.c

@@ -0,0 +1,134 @@
+#include "coffee.h"
+#include "math.h"
+#include <furi.h>
+#include <stdio.h>
+
+#define EEPROM_I2C_ADDR (0x50 << 1)
+
+uint8_t data_buffer[4];
+uint8_t address_54_buffer[4];
+uint8_t address_44_buffer[4];
+
+bool write_buffer(uint8_t *buffer, size_t buffer_size, uint8_t start_address){
+	furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
+	bool result = false;
+	if(furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, EEPROM_I2C_ADDR, (uint32_t) 1000)){
+		FURI_LOG_E("COFFEE", "WRITE READY");
+		for (size_t i = 0; i < buffer_size; i++){	
+			result = false;
+			while(!result){
+				result = furi_hal_i2c_write_reg_8(&furi_hal_i2c_handle_external, EEPROM_I2C_ADDR, start_address + i, buffer[i], (uint32_t) 2000);
+            	FURI_LOG_E("COFFEE", "Write %.2X, byte %d/%d at address %.2X, result %d", buffer[i], i + 1, buffer_size, start_address + i, result);
+			}
+		}
+	}
+	else{
+        FURI_LOG_D("COFFEE", "VIRGIN: EEPROM not ready %x (8-bit)", EEPROM_I2C_ADDR);
+    }
+	furi_hal_i2c_release(&furi_hal_i2c_handle_external);
+	return result;
+}
+void write_dump(uint8_t* buffer, size_t size){    
+	write_buffer(buffer, size, 0x00);
+}
+
+void write_credit(float value){
+	calc_credit(value, address_44_buffer);
+	memcpy(address_54_buffer, address_44_buffer, 4 * sizeof(uint8_t));
+    address_54_buffer[1] -= 0x40;
+	write_buffer(address_44_buffer, sizeof(address_44_buffer), 0x44);
+	write_buffer(address_54_buffer, sizeof(address_54_buffer), 0x54);
+}
+
+void dump(uint8_t* out){
+	FuriString* dump_str = furi_string_alloc();
+	furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
+	if(furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, EEPROM_I2C_ADDR, (uint32_t) 1000)){
+		uint8_t temp[1];
+		FURI_LOG_E("COFFEE_eeprom", "Start dump");
+		for (size_t i=0; i<256; i++){
+			furi_hal_i2c_read_reg_8(&furi_hal_i2c_handle_external, EEPROM_I2C_ADDR, 0x00 + i, (uint8_t *) temp, (uint32_t) 500);
+			furi_string_cat_printf(dump_str, "%.2X", temp[0]);
+			out[i] = temp[0];
+		}
+		FURI_LOG_E("COFFEE_eeprom", furi_string_get_cstr(dump_str));
+		FURI_LOG_E("COFFEE_eeprom", "End dump");
+	}else{
+        FURI_LOG_D("COFFEE", "DUMP: EEPROM not ready %x (8-bit)", EEPROM_I2C_ADDR);
+    }
+	furi_hal_i2c_release(&furi_hal_i2c_handle_external);
+}
+
+float read_credit(){
+    memset(data_buffer, 0, sizeof(data_buffer)); //reset array
+	furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
+	bool is_ready = false;
+	is_ready = furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, EEPROM_I2C_ADDR, (uint32_t) 1000);
+	if(is_ready){
+		furi_hal_i2c_read_mem(&furi_hal_i2c_handle_external, EEPROM_I2C_ADDR, 0x44, data_buffer, sizeof(data_buffer), (uint32_t) 1000);
+		int credit = 0;
+		int exponent = 14;
+		int hi, lo = 0;
+		for (size_t i = 0; i < sizeof(data_buffer); i++)
+		{  //iterate 2 bit at times
+			hi = 0;
+			lo = 0;
+			for (int j = 3; j >= 0; j--) {
+				int k = (data_buffer[i] % 16) >> j; // right shift
+				if (k & 1){
+					if(j>=2)
+						hi += pow(2, j-2);
+					else
+						lo += pow(2, j);
+				}
+			}
+			credit += hi * pow(2, exponent) + lo * pow(2, exponent-8);
+			exponent -= 2;
+		}
+		furi_hal_i2c_release(&furi_hal_i2c_handle_external);
+		return credit / 100.00;
+	}
+	else{
+		furi_hal_i2c_release(&furi_hal_i2c_handle_external);
+		FURI_LOG_D("COFFEE", "READ CREDIT: EEPROM not ready %x (8-bit)", EEPROM_I2C_ADDR);
+		return -1.0;
+	}
+}
+void calc_credit(float value, uint8_t* result){
+	//credit
+	uint8_t coeff[8];
+	uint16_t n = value * 100;
+    for (size_t i = 0; i < 8; i++) {
+        coeff[i] = n % 4;
+        n /= 4;
+    }
+	uint8_t credit[4];
+	for (size_t i = 0; i < 4; i++) {
+		uint8_t bit3 = (coeff[7 - i] >> 1) & 1;
+		uint8_t bit2 = coeff[7 - i] & 1;
+		uint8_t bit1 = (coeff[3 - i] >> 1) & 1;
+		uint8_t bit0 = coeff[3 - i] & 1;
+		credit[i] = (bit3 << 3) | (bit2 << 2) | (bit1 << 1) | bit0;
+    }
+	//checksum
+	uint8_t exponents[] = {12, 8, 4, 0};
+	uint16_t numerator = value * 100;
+	uint8_t sub_factor = 0;
+
+	for (size_t i = 0; i < 4; i++) {
+		sub_factor += numerator / (1 << exponents[i]);
+		numerator = numerator % (1 << exponents[i]);
+	}
+	
+	uint8_t subbed = 187 - sub_factor;
+	
+	uint8_t checksum[4];
+	for (size_t i = 0; i < 4; i++) {
+        checksum[3 - i] = (subbed % 4) * 4;
+        subbed /= 4;
+    }
+    //concatnate
+    for (size_t i = 0; i < 4; i++) {
+        result[i] = (checksum[i] << 4) | credit[i];
+    }
+}

+ 10 - 0
base_pack/coffee_eeprom/coffee.h

@@ -0,0 +1,10 @@
+#include "stdint.h"
+#include "furi_hal_i2c.h"
+#include <stddef.h>
+#include <stdint.h>
+
+void write_dump(uint8_t* buffer, size_t size);
+void dump(uint8_t* out);
+void write_credit(float value);
+float read_credit();
+void calc_credit(float value, uint8_t* result);

BIN=BIN
base_pack/coffee_eeprom/coffee_10px.png


+ 270 - 0
base_pack/coffee_eeprom/coffee_eeprom.c

@@ -0,0 +1,270 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include <gui/elements.h>
+#include <gui/gui.h>
+#include <gui/view_port.h>
+#include <stdlib.h>
+#include "coffee.h"
+#include <math.h>
+#include <storage/storage.h>
+#include <dialogs/dialogs.h>
+#include <lib/toolbox/name_generator.h>
+
+#define TAG "COFFEE EEPROM"
+#define MAX_CREDIT 655.35
+#define MIN_CREDIT 0.01
+
+typedef struct {
+    Gui* gui;
+    ViewPort* view_port;
+    FuriMessageQueue* event_queue;
+    bool editor_mode;
+    float digit_editor;
+    float credit;
+    FuriString* msg;
+    FuriString* status;
+} CoffeeContext;
+
+
+static void coffee_render_callback(Canvas* const canvas, void* ctx) {
+    CoffeeContext* context = ctx;
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontSecondary);
+     if(context->credit >= 0.0){
+        furi_string_printf(context->msg, "Credit: %.2f EUR", (double) context->credit);
+        elements_button_left(canvas, "Load");
+        elements_button_right(canvas, "Save");
+        elements_button_center(canvas, "Edit (Hold)");
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, furi_string_get_cstr(context->msg));
+
+    }else{
+        furi_string_printf(context->status, "EEPROM not connected!");
+    }   
+    canvas_set_font(canvas, FontKeyboard);
+    canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignCenter, furi_string_get_cstr(context->status));
+}
+
+/* This function is called from the GUI thread. All it does is put the event
+   into the application's queue so it can be processed later. */
+static void coffee_input_callback(InputEvent* event, void* ctx) {
+    CoffeeContext* context = ctx;
+    furi_message_queue_put(context->event_queue, event, FuriWaitForever);
+}
+/* Allocate the memory and initialise the variables */
+static CoffeeContext* coffee_context_alloc() {
+    CoffeeContext* context = malloc(sizeof(CoffeeContext));
+
+    context->view_port = view_port_alloc();
+    view_port_draw_callback_set(context->view_port, coffee_render_callback, context);
+    view_port_input_callback_set(context->view_port, coffee_input_callback, context);
+
+    context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+    context->gui = furi_record_open(RECORD_GUI);
+    gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen);
+    context->msg = furi_string_alloc();
+    context->status = furi_string_alloc();
+    return context;
+}
+
+
+void load_file_dump(){
+
+    FuriString* file_path = furi_string_alloc();
+
+    do {
+        DialogsFileBrowserOptions browser_options;
+        dialog_file_browser_set_basic_options(
+            &browser_options, ".bin", NULL);
+        browser_options.hide_ext = false;
+        browser_options.base_path = "/ext";
+
+        DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
+        bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
+
+        furi_record_close(RECORD_DIALOGS);
+        if(!res) {
+            FURI_LOG_E(TAG, "No file selected");
+            break;
+        }
+        // Open storage
+        Storage* storage = furi_record_open(RECORD_STORAGE);
+        // Allocate file
+        File* file = storage_file_alloc(storage);
+        // Open file, write data and close it
+        if(!storage_file_open(file, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+            FURI_LOG_E(TAG, "Failed to open file");
+        }
+        uint8_t buffer[256] = {0};
+
+        uint16_t read = 0;
+        uint16_t ret = 0;
+        do {
+            uint8_t temp[128] = {0};
+            read += ret;
+            ret = storage_file_read(file, temp, sizeof(temp) - 1);
+            for(size_t i = 0; i < ret; i++) {
+                buffer[i+read] = temp[i];
+            }
+        } while(ret > 0);
+        storage_file_close(file);
+
+        // Deallocate file
+        storage_file_free(file);
+
+        // Close storage
+        furi_record_close(RECORD_STORAGE);
+        if (read % 128 == 0){
+            FuriString* dump = furi_string_alloc();
+            FURI_LOG_E(TAG, "START READ DUMP");
+            for (size_t i = 0; i < read; i++){
+                furi_string_cat_printf(dump, "%.2X", buffer[i]);
+            }
+            FURI_LOG_E(TAG, "%s", furi_string_get_cstr(dump));
+            FURI_LOG_E(TAG, "END READ DUMP");
+            write_dump(buffer, (size_t) read);
+            break;
+        }
+    } while(1);
+}
+
+FuriString* save_file_dump(float credit){
+    char file_name_buf[64];
+    name_generator_make_random(file_name_buf, 64);
+        // Open storag
+    char* file_path = APP_DATA_PATH("Dump");
+
+    FuriString* file_name = furi_string_alloc_printf("%s_%s_%d.bin", file_path, file_name_buf, (int) (credit * 100));
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    // Allocate file
+    File* file = storage_file_alloc(storage);
+
+    // Get the path to the current application data folder
+    // That is: /ext/apps_data/<app_name>
+    // And it will create folders in the path if they don't exist
+    // In this example it will create /ext/apps_data/example_apps_data
+    // And file will be /ext/apps_data/example_apps_data/test.txt
+
+    // Open file, write data and close it
+    if(!storage_file_open(file, furi_string_get_cstr(file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+        FURI_LOG_E(TAG, "Failed to open file");
+    }
+    uint8_t out[256];
+    dump(out);
+    if(!storage_file_write(file, out, 256)) {
+        FURI_LOG_E(TAG, "Failed to write to file");
+    }
+    storage_file_close(file);
+
+    // Deallocate file
+    storage_file_free(file);
+
+    // Close storage
+    furi_record_close(RECORD_STORAGE);
+
+    return file_name;
+}
+
+/* Starts the reader thread and handles the input */
+static void coffee_run(CoffeeContext* context) {
+    /* Start the reader thread. It will talk to the thermometer in the background. */
+    context->credit = read_credit();
+    context->digit_editor = 0.01;
+    /* An endless loop which handles the input*/
+    for(bool is_running = true; is_running;) {
+        InputEvent event;
+        /* Wait for an input event. Input events come from the GUI thread via a callback. */
+        const FuriStatus status =
+            furi_message_queue_get(context->event_queue, &event, FuriWaitForever);
+
+        if(status == FuriStatusOk) {
+           if(event.type == InputTypePress) {
+                    switch(event.key) {
+                    case InputKeyUp:
+                        if(context->editor_mode && context->credit + context->digit_editor <= MAX_CREDIT){
+                            context->credit += context->digit_editor;
+                            FURI_LOG_E(TAG, "%.2f   %.2f", (double) context->credit, (double) context->digit_editor);
+                        }
+                        break;
+                    case InputKeyDown:
+                        if(context->editor_mode && context->credit - context->digit_editor >= MIN_CREDIT){
+                            context->credit -= context->digit_editor;
+                            FURI_LOG_E(TAG, "%.2f   %.2f", (double) context->credit, (double) context->digit_editor);
+                        }
+                        break;
+                    case InputKeyMAX:
+                        break;
+                    case InputKeyRight:
+                         if(context->editor_mode && context->digit_editor >= 0.01){
+                            context->digit_editor /= 10;
+                            FURI_LOG_E(TAG, "%.2f   %.2f", (double) context->credit, (double) context->digit_editor);
+                        }else {
+                            save_file_dump(context->credit);
+                            furi_string_printf(context->status, "Dump saved!");
+                        }
+                        break;
+                    case InputKeyLeft:
+                        if(context->editor_mode && context->digit_editor <= 100){
+                            context->digit_editor *= 10;
+                            FURI_LOG_E(TAG, "%.2f   %.2f", (double) context->credit, (double) context->digit_editor);
+                        }else{
+                            //virgin();
+                            load_file_dump();
+                            context->credit = read_credit();
+                            furi_string_printf(context->status, "Dump write done!");
+                        }
+                        break;
+                    case InputKeyOk:
+                        if(context->editor_mode){
+                            write_credit(context->credit);
+                            context->credit = read_credit();
+                            furi_string_printf(context->status, "Write done!");
+                            context->editor_mode = false;
+                        }
+                        break;
+                    case InputKeyBack:
+                        if(context->editor_mode){
+                            furi_string_reset(context->status);
+                            context->editor_mode = false;
+                        }else{
+                            is_running = false;
+                        }
+                        break;
+                    }
+                }else if(event.type == InputTypeLong && event.key == InputKeyOk){
+                        furi_string_printf(context->status, "Editor Mode");
+                        context->editor_mode = true;
+                }
+        }
+    }
+
+}
+
+/* Release the unused resources and deallocate memory */
+static void coffee_context_free(CoffeeContext* context) {
+    furi_string_free(context->msg);
+    furi_string_free(context->status);
+    view_port_enabled_set(context->view_port, false);
+    gui_remove_view_port(context->gui, context->view_port);
+    furi_message_queue_free(context->event_queue);
+    view_port_free(context->view_port);
+    furi_record_close(RECORD_GUI);
+}
+
+/* The application's entry point. Execution starts from here. */
+int32_t coffee_eeprom_main(void* p) {
+    UNUSED(p);
+
+    /* Allocate all of the necessary structures */
+    CoffeeContext* context = coffee_context_alloc();
+
+    /* Start the applicaton's main loop. It won't return until the application was requested to exit. */
+    coffee_run(context);
+
+    /* Release all unneeded resources */
+    coffee_context_free(context);
+
+    return 0;
+}

BIN=BIN
base_pack/coffee_eeprom/img/1.png