diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp index 70348e7..4beec1d 100644 --- a/lib/obp60task/OBP60Extensions.cpp +++ b/lib/obp60task/OBP60Extensions.cpp @@ -64,6 +64,12 @@ PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 fr Adafruit_FRAM_I2C fram; bool hasFRAM = false; +// SD Card +#ifdef BOARD_OBP40S3 +sdmmc_card_t *sdcard; +#endif +bool hasSDCard = false; + // Global vars bool blinkingLED = false; // Enable / disable blinking flash LED bool statusLED = false; // Actual status of flash LED on/off @@ -78,6 +84,9 @@ LedTaskData *ledTaskData=nullptr; void hardwareInit(GwApi *api) { + GwLog *logger = api->getLogger(); + GwConfigHandler *config = api->getConfig(); + Wire.begin(); // Init PCF8574 digital outputs Wire.setClock(I2C_SPEED); // Set I2C clock on 10 kHz @@ -87,7 +96,7 @@ void hardwareInit(GwApi *api) fram = Adafruit_FRAM_I2C(); if (esp_reset_reason() == ESP_RST_POWERON) { // help initialize FRAM - api->getLogger()->logDebug(GwLog::LOG,"Delaying I2C init for 250ms due to cold boot"); + logger->logDebug(GwLog::LOG, "Delaying I2C init for 250ms due to cold boot"); delay(250); } // FRAM (e.g. MB85RC256V) @@ -99,11 +108,88 @@ void hardwareInit(GwApi *api) // Boot counter uint8_t framcounter = fram.read(0x0000); fram.write(0x0000, framcounter+1); - api->getLogger()->logDebug(GwLog::LOG,"FRAM detected: 0x%04x/0x%04x (counter=%d)", manufacturerID, productID, framcounter); + logger->logDebug(GwLog::LOG, "FRAM detected: 0x%04x/0x%04x (counter=%d)", manufacturerID, productID, framcounter); } else { hasFRAM = false; - api->getLogger()->logDebug(GwLog::LOG,"NO FRAM detected"); + logger->logDebug(GwLog::LOG, "NO FRAM detected"); + } + // SD Card + hasSDCard = false; +#ifdef BOARD_OBP40S3 + if (config->getBool(config->useSDCard)) { + esp_err_t ret; + sdmmc_host_t host = SDSPI_HOST_DEFAULT(); + host.slot = SPI3_HOST; + logger->logDebug(GwLog::DEBUG, "SDSPI_HOST: max_freq_khz=%d" , host.max_freq_khz); + spi_bus_config_t bus_cfg = { + .mosi_io_num = SD_SPI_MOSI, + .miso_io_num = SD_SPI_MISO, + .sclk_io_num = SD_SPI_CLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 4000, + }; + ret = spi_bus_initialize((spi_host_device_t) host.slot, &bus_cfg, SDSPI_DEFAULT_DMA); + if (ret != ESP_OK) { + logger->logDebug(GwLog::ERROR, "Failed to initialize SPI bus for SD card"); + } else { + sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + slot_config.gpio_cs = SD_SPI_CS; + slot_config.host_id = (spi_host_device_t) host.slot; + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5, + .allocation_unit_size = 16 * 1024 + }; + ret = esp_vfs_fat_sdspi_mount(MOUNT_POINT, &host, &slot_config, &mount_config, &sdcard); + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + logger->logDebug(GwLog::ERROR, "Failed to mount SD card filesystem"); + } else { + // ret == 263 could be not powered up yet + logger->logDebug(GwLog::ERROR, "Failed to initialize SD card (error #%d)", ret); + } + } else { + logger->logDebug(GwLog::LOG, "SD card filesystem mounted at '%s'", MOUNT_POINT); + hasSDCard = true; + } + } + if (hasSDCard) { + // read some stats + String features = ""; + if (sdcard->is_mem) features += "MEM "; // Memory card + if (sdcard->is_sdio) features += "IO "; // IO Card + if (sdcard->is_mmc) features += "MMC "; // MMC Card + if (sdcard->is_ddr) features += "DDR "; + // if (sdcard->is_uhs1) features += "UHS-1 "; + // ext_csd. Extended information + // uint8_t rev, uint8_t power_class + logger->logDebug(GwLog::LOG, "SD card features: %s", features); + logger->logDebug(GwLog::LOG, "SD card size: %lluMB", ((uint64_t) sdcard->csd.capacity) * sdcard->csd.sector_size / (1024 * 1024)); + } + } +#endif +} + +void powerInit(String powermode) { + // Max Power | Only 5.0V | Min Power + if (powermode == "Max Power" || powermode == "Only 5.0V") { +#ifdef HARDWARE_V21 + setPortPin(OBP_POWER_50, true); // Power on 5.0V rail +#endif +#ifdef BOARD_OBP40S3 + setPortPin(OBP_POWER_EPD, true);// Power on ePaper display + setPortPin(OBP_POWER_SD, true); // Power on SD card +#endif + } else { // Min Power +#ifdef HARDWARE_V21 + setPortPin(OBP_POWER_50, false); // Power off 5.0V rail +#endif +#ifdef BOARD_OBP40S3 + setPortPin(OBP_POWER_EPD, false);// Power off ePaper display + setPortPin(OBP_POWER_SD, false); // Power off SD card +#endif } } diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h index ed67716..b773827 100644 --- a/lib/obp60task/OBP60Extensions.h +++ b/lib/obp60task/OBP60Extensions.h @@ -7,6 +7,12 @@ #include // E-paper lib V2 #include // I2C FRAM +#ifdef BOARD_OBP40S3 +#include "esp_vfs_fat.h" +#include "sdmmc_cmd.h" +#define MOUNT_POINT "/sdcard" +#endif + // FRAM address reservations 32kB: 0x0000 - 0x7FFF // 0x0000 - 0x03ff: single variables #define FRAM_PAGE_NO 0x0002 @@ -15,6 +21,7 @@ #define FRAM_VOLTAGE_AVG 0x000A #define FRAM_VOLTAGE_TREND 0x000B #define FRAM_VOLTAGE_MODE 0x000C +// Wind page #define FRAM_WIND_SIZE 0x000D #define FRAM_WIND_SRC 0x000E #define FRAM_WIND_MODE 0x000F @@ -24,6 +31,10 @@ extern Adafruit_FRAM_I2C fram; extern bool hasFRAM; +extern bool hasSDCard; +#ifdef BOARD_OBP40S3 +extern sdmmc_card_t *sdcard; +#endif // Fonts declarations for display (#includes see OBP60Extensions.cpp) extern const GFXfont DSEG7Classic_BoldItalic16pt7b; @@ -75,6 +86,7 @@ void deepSleep(CommonData &common); uint8_t getLastPage(); void hardwareInit(GwApi *api); +void powerInit(String powermode); void setPortPin(uint pin, bool value); // Set port pin for extension port diff --git a/lib/obp60task/OBP60Hardware.h b/lib/obp60task/OBP60Hardware.h index 01f1189..ac366c8 100644 --- a/lib/obp60task/OBP60Hardware.h +++ b/lib/obp60task/OBP60Hardware.h @@ -82,7 +82,7 @@ // Direction pin for RS485 NMEA0183 #define OBP_DIRECTION_PIN 8 // I2C - #define I2C_SPEED 10000UL // 10kHz clock speed on I2C bus + #define I2C_SPEED 100000UL // 100kHz clock speed on I2C bus #define OBP_I2C_SDA 21 #define OBP_I2C_SCL 38 // DS1388 RTC @@ -120,10 +120,10 @@ #define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code #define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function) // SPI SD-Card - #define SD_SPI_CS 10 - #define SD_SPI_MOSI 40 - #define SD_SPI_CLK 39 - #define SD_SPI_MISO 13 + #define SD_SPI_CS GPIO_NUM_10 + #define SD_SPI_MOSI GPIO_NUM_40 + #define SD_SPI_CLK GPIO_NUM_39 + #define SD_SPI_MISO GPIO_NUM_13 // GPS (NEO-6M, NEO-M8N, ATGM336H) #define OBP_GPS_RX 19 diff --git a/lib/obp60task/PageSystem.cpp b/lib/obp60task/PageSystem.cpp index ddff3d4..92af7b2 100644 --- a/lib/obp60task/PageSystem.cpp +++ b/lib/obp60task/PageSystem.cpp @@ -1,5 +1,15 @@ #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 +/* + * Special system page, called directly with fast key sequence 5,4 + * Out of normal page order. + * Consists of some sub-pages with following content: + * 1. Hard and software information + * 2. System settings + * 3. NMEA2000 device list + * 4. SD Card information if available + */ + #include "Pagedata.h" #include "OBP60Extensions.h" #include "images/logo64.xbm" @@ -7,8 +17,7 @@ #include "qrcode.h" #ifdef BOARD_OBP40S3 -#include -#include +#include "dirent.h" #endif #define STRINGIZE_IMPL(x) #x @@ -19,35 +28,27 @@ #define DISPLAYINFO STRINGIZE(EPDTYPE) #define GXEPD2INFO STRINGIZE(GXEPD2VERS) -/* - * Special system page, called directly with fast key sequence 5,4 - * Out of normal page order. - * Consists of some sub-pages with following content: - * 1. Hard and software information - * 2. System settings - * 3. NMEA2000 device list - */ - class PageSystem : public Page { -uint64_t chipid; -bool simulation; -bool sdcard; -String buzzer_mode; -uint8_t buzzer_power; -String cpuspeed; -String rtc_module; -String gps_module; -String env_module; +private: + uint64_t chipid; + bool simulation; + bool use_sdcard; + String buzzer_mode; + uint8_t buzzer_power; + String cpuspeed; + String rtc_module; + String gps_module; + String env_module; -String batt_sensor; -String solar_sensor; -String gen_sensor; -String rot_sensor; -double homelat; -double homelon; + String batt_sensor; + String solar_sensor; + String gen_sensor; + String rot_sensor; + double homelat; + double homelon; -char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard + char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard public: PageSystem(CommonData &common){ @@ -55,11 +56,12 @@ public: common.logger->logDebug(GwLog::LOG,"Instantiate PageSystem"); if (hasFRAM) { mode = fram.read(FRAM_SYSTEM_MODE); + common.logger->logDebug(GwLog::DEBUG, "Loaded mode '%c' from FRAM", mode); } chipid = ESP.getEfuseMac(); simulation = common.config->getBool(common.config->useSimuData); #ifdef BOARD_OBP40S3 - sdcard = common.config->getBool(common.config->useSDCard); + use_sdcard = common.config->getBool(common.config->useSDCard); #endif buzzer_mode = common.config->getString(common.config->buzzerMode); buzzer_mode.toLowerCase(); @@ -76,7 +78,7 @@ public: homelon = common.config->getString(common.config->homeLON).toDouble(); } - virtual void setupKeys(){ + void setupKeys() { commonData->keydata[0].label = "EXIT"; commonData->keydata[1].label = "MODE"; commonData->keydata[2].label = ""; @@ -85,7 +87,7 @@ public: commonData->keydata[5].label = "ILUM"; } - virtual int handleKey(int key){ + int handleKey(int key) { // do *NOT* handle key #1 this handled by obp60task as exit // Switch display mode commonData->logger->logDebug(GwLog::LOG, "System keyboard handler"); @@ -95,7 +97,7 @@ public: } else if (mode == 'S') { mode = 'D'; } else if (mode == 'D') { - if (sdcard) { + if (hasSDCard) { mode = 'C'; } else { mode = 'N'; @@ -117,7 +119,8 @@ public: } // standby / deep sleep if (key == 5) { - deepSleep(*commonData); + commonData->logger->logDebug(GwLog::LOG, "System going into deep sleep mode..."); + deepSleep(*commonData); } // Code for keylock if (key == 11) { @@ -132,6 +135,7 @@ public: } // standby / deep sleep if (key == 12) { + commonData->logger->logDebug(GwLog::LOG, "System going into deep sleep mode..."); deepSleep(*commonData); } #endif @@ -178,7 +182,7 @@ public: } // Logging boat values - LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem"); + logger->logDebug(GwLog::LOG, "Drawing at PageSystem, Mode=%c", mode); // Draw page //*********************************************************** @@ -257,14 +261,37 @@ public: getdisplay().setCursor(8, y0 + 48); getdisplay().print("SD-Card:"); getdisplay().setCursor(90, y0 + 48); - if (sdcard) { - uint64_t cardsize = SD.cardSize() / (1024 * 1024); - getdisplay().print(String(cardsize) + String(" MB")); + if (hasSDCard) { + uint64_t cardsize = ((uint64_t) sdcard->csd.capacity) * sdcard->csd.sector_size / (1024 * 1024); + getdisplay().printf("%llu MB", cardsize); } else { getdisplay().print("off"); } #endif + // Uptime + int64_t uptime = esp_timer_get_time() / 1000000; + String uptime_unit; + if (uptime < 120) { + uptime_unit = " seconds"; + } else { + if (uptime < 2 * 3600) { + uptime /= 60; + uptime_unit = " minutes"; + } else if (uptime < 2 * 3600 * 24) { + uptime /= 3600; + uptime_unit = " hours"; + } else { + uptime /= 86400; + uptime_unit = " days"; + } + } + getdisplay().setCursor(8, y0 + 80); + getdisplay().print("Uptime:"); + getdisplay().setCursor(90, y0 + 80); + getdisplay().print(uptime); + getdisplay().print(uptime_unit); + // CPU speed config / active getdisplay().setCursor(202, y0); getdisplay().print("CPU speed:"); @@ -371,8 +398,61 @@ public: x0 = 20; y0 = 72; getdisplay().setCursor(x0, y0); +#ifdef BOARD_OBP60S3 + // This mode should not be callable by devices without card hardware + // In case of accidential reaching this, display a friendly message + getdisplay().print("This mode is not indended to be reached!\n"); + getdisplay().print("There's nothing to see here. Move on."); +#endif +#ifdef BOARD_OBP40S3 getdisplay().print("Work in progress..."); + /* TODO + this code should go somewhere else. only for testing purposes here + identify card as OBP-Card: + magic.dat + version.dat + readme.txt + IMAGES/ + CHARTS/ + LOGS/ + DATA/ + hint: file access with fopen, fgets, fread, fclose + */ + + // Simple test for magic file in root + getdisplay().setCursor(x0, y0 + 32); + String file_magic = MOUNT_POINT "/magic.dat"; + logger->logDebug(GwLog::LOG, "Test magicfile: %s", file_magic.c_str()); + struct stat st; + if (stat(file_magic.c_str(), &st) == 0) { + getdisplay().printf("File %s exists", file_magic.c_str()); + } else { + getdisplay().printf("File %s not found", file_magic.c_str()); + } + + // Root directory check + DIR* dir = opendir(MOUNT_POINT); + int dy = 0; + if (dir != NULL) { + logger->logDebug(GwLog::LOG, "Root directory: %s", MOUNT_POINT); + struct dirent* entry; + while (((entry = readdir(dir)) != NULL) and (dy < 140)) { + getdisplay().setCursor(x0, y0 + 64 + dy); + getdisplay().print(entry->d_name); + // type 1 is file, type 2 is dir + if (entry->d_type == 2) { + getdisplay().print("/"); + } + dy += 20; + logger->logDebug(GwLog::DEBUG, " %s type %d", entry->d_name, entry->d_type); + } + closedir(dir); + } else { + logger->logDebug(GwLog::LOG, "Failed to open root directory"); + } + +#endif } else { // NMEA2000 device list diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index 26cbcac..85b5bbb 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -18,8 +18,6 @@ #ifdef BOARD_OBP40S3 #include "driver/rtc_io.h" // Needs for weakup from deep sleep -#include // SD-Card access -#include #include #endif @@ -34,7 +32,6 @@ #include "OBP60QRWiFi.h" // Functions lib for WiFi QR code #include "OBPSensorTask.h" // Functions lib for sensor data - // Global vars bool initComplete = false; // Initialization complete int taskRunCounter = 0; // Task couter for loop section @@ -47,63 +44,23 @@ void OBP60Init(GwApi *api){ GwConfigHandler *config = api->getConfig(); // Set a new device name and hidden the original name in the main config - String devicename = api->getConfig()->getConfigItem(api->getConfig()->deviceName,true)->asString(); - api->getConfig()->setValue(GwConfigDefinitions::systemName, devicename, GwConfigInterface::ConfigType::HIDDEN); + String devicename = config->getConfigItem(config->deviceName, true)->asString(); + config->setValue(GwConfigDefinitions::systemName, devicename, GwConfigInterface::ConfigType::HIDDEN); + + logger->prefix = devicename + ":"; + logger->logDebug(GwLog::LOG,"obp60init running"); - api->getLogger()->logDebug(GwLog::LOG,"obp60init running"); - // Check I2C devices - + + // Init power + String powermode = config->getConfigItem(config->powerMode,true)->asString(); + logger->logDebug(GwLog::DEBUG, "Power Mode is: %s", powermode.c_str()); + powerInit(powermode); // Init hardware hardwareInit(api); - // Init power rail 5.0V - String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString(); - api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str()); - if(powermode == "Max Power" || powermode == "Only 5.0V"){ - #ifdef HARDWARE_V21 - setPortPin(OBP_POWER_50, true); // Power on 5.0V rail - #endif - #ifdef BOARD_OBP40S3 - setPortPin(OBP_POWER_EPD, true);// Power on ePaper display - setPortPin(OBP_POWER_SD, true); // Power on SD card - #endif - } - else{ - #ifdef HARDWARE_V21 - setPortPin(OBP_POWER_50, false); // Power off 5.0V rail - #endif - #ifdef BOARD_OBP40S3 - setPortPin(OBP_POWER_EPD, false);// Power off ePaper display - setPortPin(OBP_POWER_SD, false); // Power off SD card - #endif - } - - #ifdef BOARD_OBP40S3 - bool sdcard = config->getBool(config->useSDCard); - if (sdcard) { - SPIClass SD_SPI = SPIClass(HSPI); - SD_SPI.begin(SD_SPI_CLK, SD_SPI_MISO, SD_SPI_MOSI); - if (SD.begin(SD_SPI_CS, SD_SPI, 80000000)) { - String sdtype = "unknown"; - uint8_t cardType = SD.cardType(); - switch (cardType) { - case CARD_MMC: - sdtype = "MMC"; - break; - case CARD_SD: - sdtype = "SDSC"; - break; - case CARD_SDHC: - sdtype = "SDHC"; - break; - } - uint64_t cardSize = SD.cardSize() / (1024 * 1024); - LOG_DEBUG(GwLog::LOG,"SD card type %s of size %d MB detected", sdtype, cardSize); - } - } - +#ifdef BOARD_OBP40S3 // Deep sleep wakeup configuration esp_sleep_enable_ext0_wakeup(OBP_WAKEWUP_PIN, 0); // 1 = High, 0 = Low rtc_gpio_pullup_en(OBP_WAKEWUP_PIN); // Activate pullup resistor @@ -112,7 +69,7 @@ void OBP60Init(GwApi *api){ // Settings for e-paper display String fastrefresh = api->getConfig()->getConfigItem(api->getConfig()->fastRefresh,true)->asString(); - api->getLogger()->logDebug(GwLog::DEBUG,"Fast Refresh Mode is: %s", fastrefresh.c_str()); + logger->logDebug(GwLog::DEBUG, "Fast Refresh Mode is: %s", fastrefresh.c_str()); #ifdef DISPLAY_GDEY042T81 if(fastrefresh == "true"){ static const bool useFastFullUpdate = true; // Enable fast full display update only for GDEY042T81 @@ -131,11 +88,11 @@ void OBP60Init(GwApi *api){ // Get CPU speed int freq = getCpuFrequencyMhz(); - api->getLogger()->logDebug(GwLog::LOG,"CPU speed at boot: %i MHz", freq); + logger->logDebug(GwLog::LOG,"CPU speed at boot: %i MHz", freq); // Settings for backlight String backlightMode = api->getConfig()->getConfigItem(api->getConfig()->backlight,true)->asString(); - api->getLogger()->logDebug(GwLog::DEBUG,"Backlight Mode is: %s", backlightMode.c_str()); + logger->logDebug(GwLog::DEBUG,"Backlight Mode is: %s", backlightMode.c_str()); uint brightness = uint(api->getConfig()->getConfigItem(api->getConfig()->blBrightness,true)->asInt()); String backlightColor = api->getConfig()->getConfigItem(api->getConfig()->blColor,true)->asString(); if(String(backlightMode) == "On"){ @@ -150,7 +107,7 @@ void OBP60Init(GwApi *api){ // Settings flash LED mode String ledMode = api->getConfig()->getConfigItem(api->getConfig()->flashLED,true)->asString(); - api->getLogger()->logDebug(GwLog::DEBUG,"LED Mode is: %s", ledMode.c_str()); + logger->logDebug(GwLog::DEBUG,"LED Mode is: %s", ledMode.c_str()); if(String(ledMode) == "Off"){ setBlinkingLED(false); } @@ -282,7 +239,7 @@ void registerAllPages(PageList &list){ extern PageDescription registerPageWindRose; list.add(®isterPageWindRose); extern PageDescription registerPageWindRoseFlex; - list.add(®isterPageWindRoseFlex); // + list.add(®isterPageWindRoseFlex); extern PageDescription registerPageVoltage; list.add(®isterPageVoltage); extern PageDescription registerPageDST810;