diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a05c1c --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +.pytest_cache/ +__pycache__/ + +# esp-idf built binaries +build/ +build_*_*/ +#sdkconfig + +# idf-ci build run output +build_summary_*.xml +app_info_*.txt +size_info_*.txt + +# pytest-embedded log folder +pytest_embedded_log/ + +# idf-component-manager output +dependencies.lock + +.devcontainer + +managed_components +components \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..5610178 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,23 @@ +{ + "configurations": [ + { + "name": "ESP-IDF", + "compilerPath": "${config:idf.toolsPathWin}\\tools\\xtensa-esp-elf\\esp-13.2.0_20240530\\xtensa-esp-elf\\bin\\xtensa-esp32-elf-gcc.exe", + "compileCommands": "${config:idf.buildPath}/compile_commands.json", + "includePath": [ + "${config:idf.espIdfPath}/components/**", + "${config:idf.espIdfPathWin}/components/**", + "${workspaceFolder}/**" + ], + "browse": { + "path": [ + "${config:idf.espIdfPath}/components", + "${config:idf.espIdfPathWin}/components", + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true + } + } + ], + "version": 4 +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2511a38 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "gdbtarget", + "request": "attach", + "name": "Eclipse CDT GDB Adapter" + }, + { + "type": "espidf", + "name": "Launch", + "request": "launch" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0de0370 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,41 @@ +{ + "C_Cpp.intelliSenseEngine": "default", + "idf.espIdfPathWin": "C:\\Users\\brent.RPX\\esp\\v5.3.1\\esp-idf", + "idf.openOcdConfigs": [ + "board/esp32-wrover-kit-3.3v.cfg" + ], + "idf.portWin": "COM4", + "idf.toolsPathWin": "C:\\Users\\brent.RPX\\.espressif\\tools", + "idf.flashType": "UART", + "files.associations": { + "esp_system.h": "c", + "task.h": "c", + "bt_app_core.h": "c", + "math.h": "c", + "esp_log.h": "c", + "freertos.h": "c", + "queue.h": "c", + "unistd.h": "c", + "inttypes.h": "c", + "esp_idf_version.h": "c", + "timers.h": "c", + "lvgl.h": "c", + "esp_timer.h": "c", + "esp_bt_device.h": "c", + "esp_lvgl_port.h": "c", + "lv_global.h": "c", + "stdio.h": "c", + "compare": "c", + "cstdint": "c", + "spi_master.h": "c", + "bitset": "c", + "format": "c", + "system.h": "c", + "esp_lcd_panel_ops.h": "c", + "semphr.h": "c", + "lv_spinbox.h": "c", + "lv_slider.h": "c", + "lv_menu.h": "c" + }, + "git.ignoreLimitWarning": true +} diff --git a/main/gui.c b/main/gui.c index a2aff65..ca0b7bb 100644 --- a/main/gui.c +++ b/main/gui.c @@ -25,6 +25,9 @@ static const char *TAG = "gui"; +#define UNLOCK() lvgl_port_unlock() +#define LOCK() lvgl_port_lock(0) + // LCD Pin Configuration #define LCD_PIXEL_CLOCK_HZ (5 * 1000 * 1000) #define LCD_SPI_HOST SPI2_HOST @@ -48,8 +51,17 @@ static lv_obj_t *imu_label = NULL; // Label for IMU data static lv_style_t style_mono8; +typedef struct +{ + int selected; + int count; + lv_obj_t *obj; +} menu_context_t; + static void gui_task(void); static void createBubble(lv_obj_t * scr); +static void build_scrollable_menu(void); +static void currentFocusIndex(menu_context_t *ctx); #define MAX_ITEMS 10 #define VISIBLE_ITEMS 3 @@ -65,72 +77,19 @@ static lv_obj_t *list; static lv_obj_t *buttons[MAX_ITEMS]; static int selected_index = 0; static lv_obj_t *_bubble = NULL; +static lv_obj_t * _menu = NULL; +static lv_obj_t *_currentPage = NULL; + +static GuiMode_t _mode = GUI_BUBBLE; + +/* 1. Prepare a default (unfocused) style */ +static lv_style_t _styleUnfocusedBtn; +/* 2. Prepare a focus style */ +static lv_style_t _styleFocusedBtn; -// Update highlight based on selected_index -void update_highlight() { - - lvgl_port_lock(0); - for (int i = 0; i < MAX_ITEMS; i++) { - if (buttons[i]) { - lv_obj_remove_state(buttons[i], LV_STATE_FOCUSED); - } - } - //if (buttons[selected_index]) - { - lv_obj_add_state(buttons[selected_index], LV_STATE_FOCUSED); - lv_obj_scroll_to_view(buttons[selected_index], LV_ANIM_ON); - } - - lvgl_port_unlock(); -} - -// Called when the GPIO button is pressed -void scroll_down() { - if (selected_index < MAX_ITEMS - 1) { - selected_index++; - update_highlight(); - } -} - -void scroll_up() { - if (selected_index > 0) { - selected_index--; - update_highlight(); - } -} - -static void create_menu(lv_obj_t *parent) -{ - lvgl_port_lock(0); - - list = lv_list_create(parent); - lv_obj_set_size(list, 160, 80); - lv_obj_center(list); - lv_obj_set_style_pad_row(list, 2, 0); - lv_obj_set_scroll_dir(list, LV_DIR_VER); - lv_obj_set_scrollbar_mode(list, LV_SCROLLBAR_MODE_AUTO); - - for (int i = 0; i < MAX_ITEMS; i++) { - buttons[i] = lv_list_add_btn(list, NULL, menu_items[i]); - } - - // Apply style to show selection highlight - static lv_style_t style_focus; - lv_style_init(&style_focus); - lv_style_set_bg_color(&style_focus, lv_palette_main(LV_PALETTE_RED)); - lv_style_set_bg_opa(&style_focus, LV_OPA_COVER); - lv_style_set_text_color(&style_focus, lv_color_white()); - - for (int i = 0; i < MAX_ITEMS; i++) { - lv_obj_add_style(buttons[i], &style_focus, LV_STATE_FOCUSED); - } - - update_highlight(); - - lvgl_port_unlock(); -} +static menu_context_t _menuContext; static bool notify_lvgl_flush_ready(void *user_ctx) { if (disp) { @@ -146,40 +105,13 @@ static void create_lvgl_demo(void) lvgl_port_lock(0); // Create a screen with black background lv_obj_t *scr = lv_scr_act(); - //lv_obj_set_style_bg_color(scr, lv_color_black(), LV_PART_MAIN); - //lv_obj_set_style_bg_color(scr, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN); - - //lv_style_init(&style_mono8); - //lv_style_set_text_font(&style_mono8, &lv_font_unscii_8); - //create_menu(scr); createBubble(scr); + build_scrollable_menu(); lvgl_port_unlock(); -#if 0 - // Create a label for IMU data - imu_label = lv_label_create(scr); - lv_obj_set_style_text_font(imu_label, &lv_font_montserrat_20, LV_PART_MAIN); - lv_obj_set_style_text_color(imu_label, lv_color_white(), LV_PART_MAIN); - lv_obj_align(imu_label, LV_ALIGN_CENTER, 0, 0); - lv_label_set_text(imu_label, "SOUNDSHOT!"); - // // Create a button - // lv_obj_t *btn = lv_btn_create(scr); - // lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0); - // lv_obj_set_size(btn, 70, 30); - - // // Add label to button - // label = lv_label_create(btn); - // lv_label_set_text(label, "Button"); - // lv_obj_center(label); - - // // Create a slider - // lv_obj_t *slider = lv_slider_create(scr); - // lv_obj_align(slider, LV_ALIGN_BOTTOM_MID, 0, -20); - // lv_obj_set_width(slider, 70); -#endif } static void lcd_init(void) @@ -206,7 +138,6 @@ static void lcd_init(void) .lcd_param_bits = LCD_PARAM_BITS, .spi_mode = 3, .trans_queue_depth = 10, - //.on_color_trans_done = notify_lvgl_flush_ready, .user_ctx = NULL, }; ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &io_config, &io_handle)); @@ -313,8 +244,6 @@ void gui_start(void) xTaskCreate(gui_task, "gui_task", 4096, NULL, 5, NULL); - system_notifyAll(EM_EVENT_NEW_DATA); - } static uint32_t waitForKeyPress(void) @@ -341,6 +270,299 @@ static void handleMainMenu(void) lvgl_port_unlock(); } + +// ───── MENU CONFIG ───── +#define ROW_H 20 // height of each row +static const char * items[] = { // your menu entries + "VMode Crossmade", + "Second Choice", + "Another Option", + "Yet Another", + "Yet Another", + "Last One" +}; +#define ITEM_COUNT (sizeof(items)/sizeof(items[0])) + +// ───── STATE & HELPERS ───── +static lv_obj_t * btn_array[ITEM_COUNT]; +static int selected_idx = 0; + +// Called whenever a button is “activated” (Enter/Click) +static void btn_click_cb(lv_event_t * e) { + lv_obj_t * btn = lv_event_get_target(e); + const char * txt = lv_label_get_text(lv_obj_get_child(btn, 0)); + ESP_LOGI(TAG, "Activated: %s\n", txt); +} + +// Repaint all rows so only btn_array[selected_idx] is highlighted +static void refresh_highlight(void) { + + return; + lvgl_port_lock(0); + + int index = 0; + + lv_obj_t *page = _currentPage; + + lv_obj_t * child = NULL; + lv_obj_t * next = lv_obj_get_child(page, index); + lv_obj_t * selected = NULL; + while(next) { + child = next; + + ESP_LOGI(TAG, "Child: %p", child); + + + if (lv_obj_has_flag(child, LV_OBJ_FLAG_USER_1)) + { + lv_obj_set_style_bg_color(child, lv_color_hex(0xFF8800), 0); + lv_obj_set_style_text_color(lv_obj_get_child(child,0), + lv_color_white(), 0); + + selected = child; + ESP_LOGI(TAG, "Selected"); + } + else + { + lv_obj_set_style_bg_color(child, lv_color_white(), 0); + lv_obj_set_style_text_color(lv_obj_get_child(child,0), + lv_color_black(), 0); + } + + index++; + next = lv_obj_get_child(page, index); + } + + // for(int i = 0; i < ITEM_COUNT; i++) { + // if(i == selected_idx) { + // lv_obj_set_style_bg_color(btn_array[i], lv_color_hex(0xFF8800), 0); + // lv_obj_set_style_text_color(lv_obj_get_child(btn_array[i],0), + // lv_color_white(), 0); + // } else { + // lv_obj_set_style_bg_color(btn_array[i], lv_color_white(), 0); + // lv_obj_set_style_text_color(lv_obj_get_child(btn_array[i],0), + // lv_color_black(), 0); + // } + // } + + if (selected) + { + lv_obj_scroll_to_view(selected, LV_ANIM_ON); + } + + lvgl_port_unlock(); +} + + +static void menuInc(int inc) +{ + LOCK(); + + currentFocusIndex(&_menuContext); + + ESP_LOGI(TAG, "Current Index: %d", _menuContext.selected); + + lv_obj_t *next = NULL; + + // check if we are at the first or last in the page + lv_obj_t *test = lv_obj_get_child(_currentPage, (inc > 0 ? -1 : 0)); + + if (_menuContext.obj != test) + { + next = lv_obj_get_child(_currentPage, _menuContext.selected + inc); + lv_obj_add_state(next, LV_STATE_FOCUSED); + lv_obj_clear_state(_menuContext.obj, LV_STATE_FOCUSED); + lv_obj_scroll_to_view(next, LV_ANIM_ON); + + _menuContext.obj = next; + _menuContext.selected += inc; + } + + UNLOCK(); +} + +static void menuNext(void) +{ + menuInc(1); +} + + +static void menuPrevious(void) +{ + menuInc(-1); +} + +// Fire the “clicked” event on the selected row +static void activate_selected(void) +{ + LOCK(); + lv_obj_send_event(_menuContext.obj, LV_EVENT_CLICKED, NULL); + UNLOCK(); +} + +static lv_obj_t * addMenuItem(lv_obj_t *page, const char *text) +{ + lv_obj_t * btn = lv_btn_create(page); + lv_obj_set_size(btn, LV_PCT(100), ROW_H); + + + lv_obj_add_style(btn, &_styleUnfocusedBtn, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_style(btn, &_styleFocusedBtn, LV_PART_MAIN | LV_STATE_FOCUSED); + + lv_obj_add_state(btn, LV_STATE_DEFAULT); + + + // style it just like your old list +// lv_obj_set_style_bg_color(btn, lv_color_white(), 0); + lv_obj_set_style_radius(btn, 0, LV_PART_MAIN); + lv_obj_set_style_border_width(btn, 0, LV_PART_MAIN); + lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE); + + // label & center + lv_obj_t * lbl = lv_label_create(btn); + lv_label_set_text(lbl, text); + lv_obj_set_style_radius(lbl, 0, LV_PART_MAIN | LV_STATE_ANY); + lv_obj_set_style_border_width(lbl, 0, LV_PART_MAIN | LV_STATE_ANY); + + // click callback + lv_obj_add_event_cb(btn, btn_click_cb, LV_EVENT_CLICKED, NULL); + + return btn; +} + +static int getSelectedIndex(lv_obj_t *page) +{ + return -1; +} + + + +static void currentFocusIndex(menu_context_t *ctx) +{ + ctx->count = lv_obj_get_child_cnt(_currentPage); + ctx->obj = NULL; + ctx->selected = -1; + + // return the index of the currently focused object + for(int i = 0; i < ctx->count; i++) { + lv_obj_t * child = lv_obj_get_child(_currentPage, i); + + if (lv_obj_has_state(child, LV_STATE_FOCUSED)) + { + ctx->obj = child; + ctx->selected = i; + + return; + } + } + + + +} +// ───── BUILD THE MENU ───── +static void build_scrollable_menu(void) { + + + lv_style_init(&_styleFocusedBtn); + lv_style_init(&_styleUnfocusedBtn); + + lv_style_set_bg_color(&_styleUnfocusedBtn, lv_color_make(0xff,0xff,0xff)); // gray bg + lv_style_set_text_color(&_styleUnfocusedBtn, lv_color_hex(0xFF8800)); + + lv_style_set_bg_color(&_styleFocusedBtn, lv_color_make(0x33,0x99,0xFF)); // blue bg + lv_style_set_text_color(&_styleUnfocusedBtn,lv_color_black()); + + + // 2) Inside it, create the lv_menu and hide its sidebar/header + lv_obj_t *menu = lv_menu_create(lv_scr_act()); + _menu = menu; + + lv_obj_set_style_radius(menu, 0, LV_PART_MAIN | LV_STATE_ANY); + lv_obj_set_style_border_width(menu, 0, LV_PART_MAIN | LV_STATE_ANY); + lv_obj_set_size(menu, lv_pct(100), lv_pct(100)); + lv_obj_center(menu); + + lv_obj_set_scrollbar_mode(menu, LV_SCROLLBAR_MODE_AUTO); + + + lv_obj_t * main = lv_menu_page_create(menu, NULL); + lv_obj_set_style_radius(main, 0, LV_PART_MAIN | LV_STATE_ANY); + lv_obj_set_style_border_width(main, 0, LV_PART_MAIN | LV_STATE_ANY); + lv_obj_set_scrollbar_mode(main, LV_SCROLLBAR_MODE_AUTO); + lv_obj_set_size(main, lv_pct(100), lv_pct(100)); + + lv_menu_set_page(menu, main); + + lv_obj_t * tmpObj; + + + + lv_obj_t * calMenu = lv_menu_page_create(menu, NULL); + lv_obj_set_style_radius(calMenu, 0, LV_PART_MAIN | LV_STATE_ANY); + lv_obj_set_style_border_width(calMenu, 0, LV_PART_MAIN | LV_STATE_ANY); + lv_obj_set_scrollbar_mode(calMenu, LV_SCROLLBAR_MODE_AUTO); + + tmpObj = addMenuItem(main, "Bluetooth"); + lv_obj_add_state(tmpObj, LV_STATE_FOCUSED); + //lv_obj_add_flag(tmpObj, LV_OBJ_FLAG_USER_1); + + tmpObj = addMenuItem(main, "Calibration"); + lv_menu_set_load_page_event(menu, tmpObj, calMenu); + + + tmpObj = addMenuItem(main, "Volume"); + + addMenuItem(main, "About"); + addMenuItem(main, "Exit"); + + + addMenuItem(calMenu, "Calibrate Level"); + addMenuItem(calMenu, "Reset Calibration"); + addMenuItem(calMenu, "Exit"); + + _currentPage = main; + + + +#if 0 + for(int i = 0; i < ITEM_COUNT; i++) { + // create a full-width button in that section + lv_obj_t * btn = lv_btn_create(main); + lv_obj_set_size(btn, LV_PCT(100), ROW_H); + + // style it just like your old list + lv_obj_set_style_bg_color(btn, lv_color_white(), 0); + lv_obj_set_style_radius(btn, 0, LV_PART_MAIN); + lv_obj_set_style_border_width(btn, 0, LV_PART_MAIN); + lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE); + + + // label & center + lv_obj_t * lbl = lv_label_create(btn); + lv_label_set_text(lbl, items[i]); + lv_obj_set_style_radius(lbl, 0, LV_PART_MAIN | LV_STATE_ANY); + lv_obj_set_style_border_width(lbl, 0, LV_PART_MAIN | LV_STATE_ANY); + //lv_obj_center(lbl); + + // click callback + lv_obj_add_event_cb(btn, btn_click_cb, LV_EVENT_CLICKED, NULL); + + // save for highlight/scroll + btn_array[i] = btn; + } + +// lv_obj_t * content = lv_obj_get_child(page, 0); +// lv_obj_set_style_radius(content, 0, LV_PART_MAIN | LV_STATE_ANY); +// lv_obj_set_style_border_width(content, 0, LV_PART_MAIN | LV_STATE_ANY); +#endif + // 6) Initial highlight + selected_idx = 0; + refresh_highlight(); + + + +} + static void gui_task(void) { system_subscribe(xTaskGetCurrentTaskHandle()); @@ -349,24 +571,50 @@ static void gui_task(void) QueueHandle_t q = keypad_getQueue(); uint32_t ev = 0; + lv_obj_remove_flag(_bubble, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN); while (1) { + if (xQueueReceive(q, &ev, pdMS_TO_TICKS(10)) == pdTRUE) { switch (ev) { case (KEY0 << KEY_SHORT_PRESS): - scroll_down(); + + if (_mode == GUI_MENU) + { + menuNext(); + } ESP_LOGI(TAG, "MAIN: Button 1 SHORT"); break; case (KEY0 << KEY_LONG_PRESS): - ESP_LOGI(TAG, "MAIN: Button 1 LONG"); + + if (_mode != GUI_MENU) + { + _mode = GUI_MENU; + lv_obj_remove_flag(_menu, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN); + } + else + if (_mode == GUI_MENU) + { + activate_selected(); + _mode = GUI_BUBBLE; + // lv_obj_remove_flag(_bubble, LV_OBJ_FLAG_HIDDEN); + // lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN); + } + break; case (KEY1 << KEY_SHORT_PRESS): - scroll_up(); - ESP_LOGI(TAG, "MAIN: Button 2 SHORT"); + { + if (_mode == GUI_MENU) + { + menuPrevious(); + } break; + } case (KEY1 << KEY_LONG_PRESS): ESP_LOGI(TAG, "MAIN: Button 2 LONG"); gpio_set_level(PIN_NUM_nON, 0); @@ -376,25 +624,26 @@ static void gui_task(void) } } - - uint32_t notifiedBits = 0; - // clear on entry (first param), wait for any bit, block forever - xTaskNotifyWait( - 0xFFFFFFFF, // clear any old bits on entry - 0xFFFFFFFF, // clear bits on exit - ¬ifiedBits, - portMAX_DELAY); - - if (notifiedBits & EM_EVENT_NEW_DATA) + + if (_mode == GUI_BUBBLE) { - ImuData_t d = system_getImuData(); - - //ESP_LOGI(TAG, "Angle: %.2f", a); - bubble_setValue(_bubble, -d.filtered[ANGLE_XY]); + uint32_t notifiedBits = 0; + // clear on entry (first param), wait for any bit, block forever + xTaskNotifyWait( + 0xFFFFFFFF, // clear any old bits on entry + 0xFFFFFFFF, // clear bits on exit + ¬ifiedBits, + pdMS_TO_TICKS(10)); + if (notifiedBits & EM_EVENT_NEW_DATA) + { + ImuData_t d = system_getImuData(); + bubble_setValue(_bubble, -d.angle); + } } } + } \ No newline at end of file diff --git a/main/gui.h b/main/gui.h index 62d9316..a04e31d 100644 --- a/main/gui.h +++ b/main/gui.h @@ -1,6 +1,12 @@ #ifndef GUI_H #define GUI_H +typedef enum +{ + GUI_MAIN = 0, + GUI_MENU, + GUI_BUBBLE +} GuiMode_t; void gui_start(void); diff --git a/main/main.c b/main/main.c index 40a003c..983e333 100644 --- a/main/main.c +++ b/main/main.c @@ -168,8 +168,25 @@ static void imu_task(void *pvParameters) { imu.raw[ANGLE_XY] = atan2f((float)imu_data.acc_x, (float)imu_data.acc_y) * 180 / M_PI; + float angle; - imu.filtered[ANGLE_XY] = LPF_Update(&lpf, imu.raw[ANGLE_XY]); + if (imu.raw[ANGLE_XY] > MAX_INDICATION_ANGLE) + { + angle = MAX_INDICATION_ANGLE; + } + else + if (imu.raw[ANGLE_XY] > MAX_INDICATION_ANGLE) + { + angle = -MAX_INDICATION_ANGLE; + } + else + { + angle = imu.raw[ANGLE_XY]; + } + + imu.angle = LPF_Update(&lpf, angle); + + //imu.filtered[ANGLE_XY] = LPF_Update(&lpf, imu.raw[ANGLE_XY]); system_setImuData(imu); diff --git a/main/system.c b/main/system.c index 21e8ac6..c9f85ea 100644 --- a/main/system.c +++ b/main/system.c @@ -94,7 +94,7 @@ void system_notifyAll(uint32_t eventBits) { if (xSemaphoreTake(em->mutex, portMAX_DELAY) == pdTRUE) { for (size_t i = 0; i < em->count; ++i) { // set the bits in each task's notification value - ESP_LOGI("g", "Notify: %p", em->subscribers[i]); + //ESP_LOGI("g", "Notify: %p", em->subscribers[i]); xTaskNotify(em->subscribers[i], eventBits, eSetBits); diff --git a/main/system.h b/main/system.h index aceac8f..7f11f52 100644 --- a/main/system.h +++ b/main/system.h @@ -12,10 +12,13 @@ enum ANGLE_YZ, }; + + typedef struct { float raw[3]; float filtered[3]; + float angle; } ImuData_t; typedef struct SystemState_s @@ -23,9 +26,12 @@ typedef struct SystemState_s ImuData_t imu; EventGroupHandle_t event; + + } SystemState_t; +#define MAX_INDICATION_ANGLE 10.0f #define EM_MAX_SUBSCRIBERS 8 // tweak as needed #define EM_EVENT_NEW_DATA (1UL<<0)