neu-rah / ArduinoMenu

Arduino generic menu/interactivity system
GNU Lesser General Public License v2.1
931 stars 189 forks source link

Strange behavior on oled display (xTaskCreate) #347

Closed Astar75 closed 3 years ago

Astar75 commented 3 years ago

Before moving on to the problem itself, I express my deep gratitude for such a great work to the author of the library.

Now the problem itself. Surely this has more to do with the display than with the library, but maybe someone has already encountered this. I am using an ESP 32 microcontroller for my project. If you call the nav.poll() function in the loop() function, then the menu works fine. But I needed to move the work from the menu to another thread and I used the FreeRTOS xTaskCreate() function. After that, strange behavior began to appear on the display. Letters begin to blur when navigating the menu.

display.h `#ifndef DISPLAY_H

define DISPLAY_H

include

include "SSD1306Ascii.h"

include "SSD1306AsciiWire.h"

include

include <menuIO/SSD1306AsciiOut.h>

include <menuIO/serialIO.h>

include <menuIO/altKeyIn.h>

include "version.h"

include "sabertypes.h"

define OLED_SDA 21

define OLED_SDC 22

define BUTTON_DISPLAY 32

define I2C_ADDRESS 0x3C

using namespace Menu;

define menuFont X11fixed7x14

define fontW 7

define fontH 15

define MAX_DEPTH 3

result BatteryAlert(menuOut &o, idleEvent e); result DoBatteryAlert(eventMask e, prompt &item); result DoShowInfo(eventMask e, prompt &item); result DoSetLightness(eventMask e); result DoSetColor(eventMask e); result DoSetSpeed(eventMask e);
result DoSetLightnessCct(eventMask e); result DoSetWhitePreset(eventMask e); result DoSetBrightnessCct(eventMask e); result DoSetFunctions(eventMask e); result DoShowIpAddress(eventMask e); result DoSaveDmxParams(eventMask e); result DoSetSaberMode(eventMask e); result DoSaveModeIndicationParams(eventMask e); result DoSaveStartIndicationParams(eventMask e);

void ShowGreetingDisplay(); void SetMenuDebug(bool debug);
void InitializeMenu(); void UpdateMenu();

uint8_t GetKeyPressed();

endif `

display.cpp `#include "display.h"

SSD1306AsciiWire oled;

bool menuDebug = false;

idx_t serialTops[MAX_DEPTH] = {0}; serialOut outSerial(Serial, serialTops);

constMEM panel panels[] MEMMODE = {{0, 0, 128 / fontW, 64 / fontH}}; navNode nodes[sizeof(panels) / sizeof(panel)]; //navNodes to store navigation status panelsList pList(panels, nodes, 1); //a list of panels and nodes idx_t tops[MAX_DEPTH] = {0, 0}; //store cursor positions for each level SSD1306AsciiOut outOLED(&oled, tops, pList, 8, 1 + ((fontH - 1) >> 3)); //oled output device menu driver menuOut constMEM outputs[] MEMMODE = {&outOLED, &outSerial}; //list of output devices outputsList out(outputs, sizeof(outputs) / sizeof(menuOut *)); //outputs list

uint8_t menuLightness = 0; uint8_t menuLightnessWhite = 0; uint8_t menuRed = 0; uint8_t menuGreen = 0; uint8_t menuBlue = 0; uint8_t menuSaberMode = 0; uint16_t menuSpeed = 0; uint8_t menuPixels = 0; uint32_t menuDmxUniverse = 0; uint32_t menuDmxAddress = 0; uint8_t menuCurrentSaberMode = 0; uint8_t menuStartIndication = 0; uint8_t menuModeIndication = 0;

TOGGLE(menuStartIndication, indicationStart, "Charge level: ", DoSaveStartIndicationParams, noEvent, noStyle, VALUE("Off", 0, DoSaveStartIndicationParams, noEvent), VALUE("On", 1, DoSaveStartIndicationParams, noEvent));

TOGGLE(menuModeIndication, indicationMode, "Mode: ", DoSaveModeIndicationParams, noEvent, noStyle, VALUE("Off", 0, DoSaveModeIndicationParams, noEvent), VALUE("On", 1, DoSaveModeIndicationParams, noEvent));

MENU(indicationMenu, "Indication", doNothing, noEvent, wrapStyle, SUBMENU(indicationStart), SUBMENU(indicationMode));

TOGGLE(menuCurrentSaberMode, saberMode, "Mode: ", doNothing, noEvent, noStyle, VALUE("Bluetooth", SaberMode::BLUETOOTH, DoSetSaberMode, noEvent), VALUE("Configuration", SaberMode::CONFIGURATION, DoSetSaberMode, noEvent), VALUE("Artnet", SaberMode::WIFI_ARTNET, DoSetSaberMode, noEvent));

MENU(settingsMenu, "Settings", doNothing, noEvent, wrapStyle, SUBMENU(saberMode), FIELD(menuPixels, "Pixels: ", "", 1, 24, 1, 2, DoSaveDmxParams, anyEvent, wrapStyle), FIELD(menuDmxUniverse, "Universe: ", "", 0, 100, 1, 10, DoSaveDmxParams, anyEvent, wrapStyle), FIELD(menuDmxAddress, "Address: ", "", 0, 506, 1, 10, DoSaveDmxParams, anyEvent, wrapStyle), SUBMENU(indicationMenu), OP("IP addresses", DoShowIpAddress, enterEvent));

MENU(functionsMenu, "Functions", doNothing, noEvent, wrapStyle, FIELD(menuLightness, "Brightness: ", "", 0, 255, 1, 10, DoSetLightness, anyEvent, wrapStyle), FIELD(menuSpeed, "Speed: ", "%", 1, 100, 10, 1, DoSetSpeed, anyEvent, wrapStyle), OP("Red strobe", DoSetFunctions, enterEvent), OP("Yellow strobe", DoSetFunctions, enterEvent), OP("Green strobe", DoSetFunctions, enterEvent), OP("Cyan strobe", DoSetFunctions, enterEvent), OP("Blue strobe", DoSetFunctions, enterEvent), OP("Purple strobe", DoSetFunctions, enterEvent), OP("Red fade", DoSetFunctions, enterEvent), OP("Yellow fade", DoSetFunctions, enterEvent), OP("Green fade", DoSetFunctions, enterEvent), OP("Cyan fade", DoSetFunctions, enterEvent), OP("Blue fade", DoSetFunctions, enterEvent), OP("Purple fade", DoSetFunctions, enterEvent));

MENU(cctMenu, "CCT", DoSetWhitePreset, noEvent, wrapStyle, FIELD(menuLightnessWhite, "Brightness: ", "", 0, 255, 1, 10, DoSetBrightnessCct, anyEvent, wrapStyle), OP("8000 K", DoSetWhitePreset, enterEvent), OP("7000 K", DoSetWhitePreset, enterEvent), OP("6200 K", DoSetWhitePreset, enterEvent), OP("5600 K", DoSetWhitePreset, enterEvent), OP("4500 K", DoSetWhitePreset, enterEvent), OP("3200 K", DoSetWhitePreset, enterEvent));

MENU(rgbMenu, "RGB", DoSetColor, noEvent, wrapStyle, FIELD(menuLightness, "Lightness: ", "", 0, 255, 10, 1, DoSetLightness, anyEvent, wrapStyle), FIELD(menuRed, "Red: ", "", 0, 255, 10, 1, DoSetColor, anyEvent, wrapStyle), FIELD(menuGreen, "Green: ", "", 0, 255, 10, 1, DoSetColor, anyEvent, wrapStyle), FIELD(menuBlue, "Blue: ", "", 0, 255, 10, 1, DoSetColor, anyEvent, wrapStyle));

MENU(mainMenu, "Main Menu", doNothing, noEvent, wrapStyle, SUBMENU(rgbMenu), SUBMENU(cctMenu), SUBMENU(functionsMenu), SUBMENU(settingsMenu), OP("Battery", DoBatteryAlert, enterEvent));

serialIn serial(Serial); NAVROOT(nav, mainMenu, MAX_DEPTH, serial, out);

result idle(menuOut &o, idleEvent e) { o.clear(); switch (e) { case idleStart: o.println("suspending menu!"); break; case idling: o.println("suspended..."); break; case idleEnd: o.println("resuming menu."); break; } return proceed; }

void SetMenuDebug(bool debug) { menuDebug = debug; }

void ShowGreetingDisplay() { oled.setCursor(0, 2); oled.println(" Osterrig WALS"); oled.print(" "); oled.println(VERSION_BASE); delay(1000); oled.clear(); oled.setCursor(0, 3); oled.println(" Welcome!"); delay(1000); }

void InitializeMenu() { if (menuDebug) { Serial.println("MENU_DISPLAY: Инициализация дисплея..."); } Wire.begin(); oled.begin(&Adafruit128x64, I2C_ADDRESS); oled.setFont(menuFont); ShowGreetingDisplay(); oled.clear(); options->invertFieldKeys = true; nav.idleTask = idle; nav.canExit = false; }

void UpdateMenu() { int key = GetKeyPressed(); switch (key) { case Key::DOWN: nav.doNav(downCmd); break; case Key::UP: nav.doNav(upCmd); break; case Key::BACK: nav.doNav(escCmd); break; case Key::SELECT: nav.doNav(enterCmd); break; } nav.poll(); }

uint8_t GetKeyPressed() { uint16_t keyValue = analogRead(32); if (keyValue > 350 && keyValue < 500) { delay(100); if (menuDebug) Serial.println("MENU_DISPLAY: Нажата клавиша DOWN"); return Key::DOWN; } else if (keyValue > 750 && keyValue < 1000) { delay(100); if (menuDebug) Serial.println("MENU_DISPLAY: Нажата клавиша BACK"); return Key::BACK; } else if (keyValue > 1050 && keyValue < 1300) { delay(100); if (menuDebug) Serial.println("MENU_DISPLAY: Нажата клавиша SELECT"); return Key::SELECT; } else if (keyValue < 100) { delay(100); if (menuDebug) Serial.println("MENU_DISPLAY: Нажата клавиша UP"); return Key::UP; } }

result BatteryAlert(menuOut &o, idleEvent e) { return proceed; }

result DoBatteryAlert(eventMask e, prompt &item) { return proceed; }

result DoShowInfo(eventMask e, prompt &item) { return proceed; }

result DoSetLightness(eventMask e) { return proceed; }

result DoSetColor(eventMask e) { return proceed; }

result DoSetSpeed(eventMask e) { return proceed; }

result DoSetLightnessCct(eventMask e) { return proceed; }

result DoSetWhitePreset(eventMask e) { return proceed; }

result DoSetBrightnessCct(eventMask e) { return proceed; }

result DoSetFunctions(eventMask e) { return proceed; }

result DoShowIpAddress(eventMask e) { return proceed; }

result DoSaveDmxParams(eventMask e) { return proceed; }

result DoSetSaberMode(eventMask e) { return proceed; }

result DoSaveModeIndicationParams(eventMask e) { return proceed; }

result DoSaveStartIndicationParams(eventMask e) { return proceed; }`

Тask creation

`void setup() { // .. if (xTaskCreate(TaskUpdateMenu, "menu", 4096, NULL, 1, NULL) != pdPASS) { Serial.println("Это фиаско братан"); } }

void TaskUpdateMenu(void *pvParam) { for(;;) { UpdateMenu(); vTaskDelay(10); } } `

Astar75 commented 3 years ago

Strange behavior looks like this https://youtu.be/Y8y1guMr1GQ

Astar75 commented 3 years ago

So far I have understood only one thing. Displaying the menu is best done in the loop() function. In another thread, I placed the button poll. It works more stably. It is probable that delay() function does not work correctly with FreRTOS tasks.