diff --git a/boards/heltec-rcc6.json b/boards/heltec-rcc6.json new file mode 100644 index 00000000000..972d2b8646f --- /dev/null +++ b/boards/heltec-rcc6.json @@ -0,0 +1,29 @@ +{ + "build": { + "arduino": { + "partitions": "default_16MB.csv" + }, + "core": "esp32", + "f_cpu": "160000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32c6", + "variant": "heltec_rcc6" + }, + "connectivity": ["bluetooth", "wifi", "lora"], + "debug": { + "openocd_target": "esp32c6.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "Heltec RCC6", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://heltec.org/", + "vendor": "Heltec" +} diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index f67e35fce8a..8a0b888ce53 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -119,7 +119,7 @@ static void rak14014_tpIntHandle(void) _rak14014_touch_int = true; } -#elif defined(HACKADAY_COMMUNICATOR) +#elif defined(HACKADAY_COMMUNICATOR) || defined(HELTEC_RCC6) #include Arduino_GFX *tft = nullptr; @@ -1307,7 +1307,7 @@ void TFTDisplay::display(bool fromBlank) } } } -#if defined(HACKADAY_COMMUNICATOR) +#if defined(HACKADAY_COMMUNICATOR) || defined(HELTEC_RCC6) tft->draw16bitBeRGBBitmap(0, yStart, repaintChunkBuffer, displayWidth, rowsThisChunk); #else tft->pushImage(0, yStart, displayWidth, rowsThisChunk, repaintChunkBuffer); @@ -1407,7 +1407,7 @@ void TFTDisplay::display(bool fromBlank) linePixelBuffer[x] = isset ? colorTftWhite : colorTftBlack; #endif } -#if defined(HACKADAY_COMMUNICATOR) +#if defined(HACKADAY_COMMUNICATOR) || defined(HELTEC_RCC6) tft->draw16bitBeRGBBitmap(x_FirstPixelUpdate, y, &linePixelBuffer[x_FirstPixelUpdate], (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1); #else @@ -1487,7 +1487,7 @@ void TFTDisplay::sendCommand(uint8_t com) display(true); if (portduino_config.displayBacklight.pin > 0) digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON); -#elif defined(HACKADAY_COMMUNICATOR) +#elif defined(HACKADAY_COMMUNICATOR) || defined(HELTEC_RCC6) tft->displayOn(); #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096) && \ !defined(HELTEC_MESH_NODE_T1) @@ -1502,8 +1502,7 @@ void TFTDisplay::sendCommand(uint8_t com) unphone.backlight(true); // using unPhone library #endif #if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1) -#elif !defined(M5STACK) && !defined(ST7789_CS) && \ - !defined(HACKADAY_COMMUNICATOR) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function +#elif !defined(M5STACK) && !defined(ST7789_CS) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_RCC6) tft->setBrightness(172); #endif break; @@ -1515,7 +1514,7 @@ void TFTDisplay::sendCommand(uint8_t com) tft->clear(); if (portduino_config.displayBacklight.pin > 0) digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON); -#elif defined(HACKADAY_COMMUNICATOR) +#elif defined(HACKADAY_COMMUNICATOR) || defined(HELTEC_RCC6) tft->displayOff(); #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096) && \ !defined(HELTEC_MESH_NODE_T1) @@ -1530,7 +1529,7 @@ void TFTDisplay::sendCommand(uint8_t com) unphone.backlight(false); // using unPhone library #endif #if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1) -#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) +#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_RCC6) tft->setBrightness(0); #endif break; @@ -1546,7 +1545,7 @@ void TFTDisplay::setDisplayBrightness(uint8_t _brightness) { #if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1) // todo -#elif !defined(HACKADAY_COMMUNICATOR) +#elif !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_RCC6) tft->setBrightness(_brightness); LOG_DEBUG("Brightness is set to value: %i ", _brightness); #endif @@ -1564,7 +1563,8 @@ bool TFTDisplay::hasTouch(void) { #ifdef RAK14014 return true; -#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && !defined(HELTEC_MESH_NODE_T1) +#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && \ + !defined(HELTEC_MESH_NODE_T1) && !defined(HELTEC_RCC6) return tft->touch() != nullptr; #else return false; @@ -1583,7 +1583,8 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y) } else { return false; } -#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && !defined(HELTEC_MESH_NODE_T1) +#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && \ + !defined(HELTEC_MESH_NODE_T1) && !defined(HELTEC_RCC6) return tft->getTouch(x, y); #else return false; @@ -1611,6 +1612,9 @@ bool TFTDisplay::connect() tft = new Arduino_NV3007(bus, 40, 0 /* rotation */, false /* IPS */, 142 /* width */, 428 /* height */, 12 /* col offset 1 */, 0 /* row offset 1 */, 14 /* col offset 2 */, 0 /* row offset 2 */, nv3007_279_init_operations, sizeof(nv3007_279_init_operations)); +#elif defined(HELTEC_RCC6) + Arduino_DataBus *bus = new Arduino_SWSPI(TFT_RS, TFT_CS, TFT_SCL, TFT_SDA, GFX_NOT_DEFINED); + tft = new Arduino_NV3001B(bus, TFT_RST, 3, true, TFT_WIDTH, TFT_HEIGHT, 0, 0, 0, 0); #else tft = new LGFX; #endif @@ -1622,7 +1626,7 @@ bool TFTDisplay::connect() #ifdef UNPHONE unphone.backlight(true); // using unPhone library #endif -#ifdef HACKADAY_COMMUNICATOR +#if defined(HACKADAY_COMMUNICATOR) || defined(HELTEC_RCC6) bool beginStatus = tft->begin(); if (beginStatus) LOG_DEBUG("TFT Success!"); @@ -1647,6 +1651,8 @@ bool TFTDisplay::connect() tft->setRotation(2); // T-Watch S3 left-handed orientation #elif ARCH_PORTDUINO || defined(SENSECAP_INDICATOR) || defined(T_LORA_PAGER) tft->setRotation(0); // use config.yaml to set rotation +#elif defined(HELTEC_RCC6) + tft->setRotation(3); #else tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label #endif diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 4c2879adc21..9da6338aba8 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -172,6 +172,138 @@ uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_ #endif +#ifdef HELTEC_RCC6 + +constexpr uint8_t NV3001B_RDDID = 0x04; +constexpr uint8_t NV3001B_RDID1 = 0xDA; +constexpr uint8_t NV3001B_RDID2 = 0xDB; +constexpr uint8_t NV3001B_RDID3 = 0xDC; +constexpr uint32_t NV3001B_EXPECTED_ID = 0x300101; +constexpr uint32_t NV3001B_FLOATING_ID = 0xFFFFFF; +constexpr uint32_t NV3001B_HELD_LOW_ID = 0x000000; + +void nv3001bSpiDelay() +{ + delayMicroseconds(1); +} + +void writeNv3001bCommandForRead(uint8_t command, uint8_t sck, uint8_t mosi, uint8_t dc) +{ + pinMode(mosi, OUTPUT); + digitalWrite(dc, LOW); + for (uint8_t mask = 0x80; mask; mask >>= 1) { + digitalWrite(sck, LOW); + digitalWrite(mosi, (command & mask) ? HIGH : LOW); + nv3001bSpiDelay(); + digitalWrite(sck, HIGH); + nv3001bSpiDelay(); + + if (mask == 0x01) { + digitalWrite(dc, HIGH); + pinMode(mosi, INPUT); + nv3001bSpiDelay(); + } + } + + digitalWrite(sck, LOW); + nv3001bSpiDelay(); +} + +uint8_t readNv3001bBit(uint8_t sck, uint8_t mosi) +{ + digitalWrite(sck, HIGH); + nv3001bSpiDelay(); + uint8_t bit = digitalRead(mosi) ? 1 : 0; + digitalWrite(sck, LOW); + nv3001bSpiDelay(); + return bit; +} + +uint8_t readNv3001bByte(uint8_t sck, uint8_t mosi) +{ + uint8_t data = 0; + for (uint8_t bit = 0; bit < 8; bit++) + data = (data << 1) | readNv3001bBit(sck, mosi); + return data; +} + +void readNv3001bRegister(uint8_t command, uint8_t *data, uint8_t len, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc) +{ + digitalWrite(cs, LOW); + + writeNv3001bCommandForRead(command, sck, mosi, dc); + + if (command == NV3001B_RDDID) + (void)readNv3001bBit(sck, mosi); + + for (uint8_t i = 0; i < len; i++) + data[i] = readNv3001bByte(sck, mosi); + + pinMode(mosi, OUTPUT); + digitalWrite(mosi, HIGH); + digitalWrite(cs, HIGH); + digitalWrite(dc, HIGH); + digitalWrite(sck, LOW); +} + +uint32_t packNv3001bId(const uint8_t *id) +{ + return (static_cast(id[0]) << 16) | (static_cast(id[1]) << 8) | id[2]; +} + +uint32_t get_nv3001b_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst, uint8_t en, uint8_t bl) +{ + pinMode(en, OUTPUT); + digitalWrite(en, TFT_EN_ON); + delay(100); + pinMode(bl, OUTPUT); + digitalWrite(bl, TFT_BACKLIGHT_ON); + delay(100); + + pinMode(cs, OUTPUT); + pinMode(sck, OUTPUT); + pinMode(mosi, OUTPUT); + pinMode(dc, OUTPUT); + pinMode(rst, OUTPUT); + digitalWrite(cs, HIGH); + digitalWrite(dc, HIGH); + digitalWrite(sck, LOW); + digitalWrite(mosi, HIGH); + digitalWrite(rst, HIGH); + delay(100); + digitalWrite(rst, LOW); + delay(120); + digitalWrite(rst, HIGH); + delay(120); + + uint8_t rddidBytes[3] = {}; + uint8_t rdidBytes[3] = {}; + readNv3001bRegister(NV3001B_RDDID, rddidBytes, sizeof(rddidBytes), cs, sck, mosi, dc); + readNv3001bRegister(NV3001B_RDID1, &rdidBytes[0], 1, cs, sck, mosi, dc); + readNv3001bRegister(NV3001B_RDID2, &rdidBytes[1], 1, cs, sck, mosi, dc); + readNv3001bRegister(NV3001B_RDID3, &rdidBytes[2], 1, cs, sck, mosi, dc); + + uint32_t rddid = packNv3001bId(rddidBytes); + uint32_t rdid = packNv3001bId(rdidBytes); + uint32_t id = rddid == NV3001B_EXPECTED_ID ? rddid : rdid; + bool noResponse = (rddid == NV3001B_FLOATING_ID && rdid == NV3001B_FLOATING_ID) || + (rddid == NV3001B_HELD_LOW_ID && rdid == NV3001B_HELD_LOW_ID); + + LOG_INFO("Heltec RCC6 NV3001B RDDID=%06x RDID=%06x", rddid, rdid); + if (id != NV3001B_EXPECTED_ID || noResponse) { + LOG_INFO("Heltec RCC6 NV3001B display not detected"); + digitalWrite(bl, TFT_BACKLIGHT_OFF); + digitalWrite(en, TFT_EN_OFF); + delay(1); + pinMode(en, INPUT); + } else { + LOG_INFO("Heltec RCC6 NV3001B display detected"); + } + return id; +} + +#endif + // When armed by loadFromDisk, the decode callback writes satellite entries // straight into these maps instead of the temp vectors. Nullptr = legacy // push_back-to-vector path for backup/restore and other decoders. @@ -1010,6 +1142,12 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) hasScreen = false; } #endif // HELTEC_MESH_NODE_T114 +#ifdef HELTEC_RCC6 + uint32_t nv3001b_id = get_nv3001b_id(TFT_CS, TFT_SCL, TFT_SDA, TFT_RS, TFT_RST, TFT_EN, TFT_BL); + if (nv3001b_id != NV3001B_EXPECTED_ID) { + hasScreen = false; + } +#endif // HELTEC_RCC6 #elif ARCH_PORTDUINO bool hasScreen = false; if (portduino_config.displayPanel) diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index e58a9b52008..cecd3a65d7d 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -210,6 +210,8 @@ #define HW_VENDOR meshtastic_HardwareModel_M5STACK_CARDPUTER_ADV #elif defined(MESHNOLOGY_W10) #define HW_VENDOR meshtastic_HardwareModel_MESHNOLOGY_W10 +#elif defined(HELTEC_RCC6) +#define HW_VENDOR meshtastic_HardwareModel_HELTEC_RCC6 #else #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW #endif @@ -244,4 +246,4 @@ // Setup flag, which indicates if our device supports dynamic light sleep #if defined(HAS_ESP32_PM_SUPPORT) && defined(CONFIG_FREERTOS_USE_TICKLESS_IDLE) #define HAS_ESP32_DYNAMIC_LIGHT_SLEEP 1 -#endif \ No newline at end of file +#endif diff --git a/variants/esp32c6/heltec_rcc6/pins_arduino.h b/variants/esp32c6/heltec_rcc6/pins_arduino.h new file mode 100644 index 00000000000..91604ac60e3 --- /dev/null +++ b/variants/esp32c6/heltec_rcc6/pins_arduino.h @@ -0,0 +1,20 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x303A +#define USB_PID 0x1001 + +static const uint8_t TX = 16; +static const uint8_t RX = 17; + +static const int8_t SDA = -1; +static const int8_t SCL = -1; + +static const uint8_t MISO = 20; +static const uint8_t SCK = 21; +static const uint8_t MOSI = 22; +static const uint8_t SS = 23; + +#endif /* Pins_Arduino_h */ diff --git a/variants/esp32c6/heltec_rcc6/platformio.ini b/variants/esp32c6/heltec_rcc6/platformio.ini new file mode 100644 index 00000000000..f89a7f87d85 --- /dev/null +++ b/variants/esp32c6/heltec_rcc6/platformio.ini @@ -0,0 +1,33 @@ +[env:heltec-rcc6] +custom_meshtastic_hw_model = 143 +custom_meshtastic_hw_model_slug = HELTEC_RCC6 +custom_meshtastic_architecture = esp32-c6 +custom_meshtastic_actively_supported = true +custom_meshtastic_support_level = 1 +custom_meshtastic_display_name = Heltec RCC6 +custom_meshtastic_images = heltec_rcc6.svg +custom_meshtastic_tags = Heltec +custom_meshtastic_partition_scheme = 16MB +custom_meshtastic_has_mui = false + +extends = esp32c6_base +board = heltec-rcc6 +board_level = pr +board_build.partitions = default_16MB.csv +upload_protocol = esptool + +build_flags = + ${esp32c6_base.build_flags} + -D HELTEC_RCC6 + -I variants/esp32c6/heltec_rcc6 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 + -DMESHTASTIC_EXCLUDE_AUDIO=1 + +lib_deps = + ${esp32c6_base.lib_deps} + https://github.com/Quency-D/Arduino_GFX/archive/4d5afb05ce70a51895fe65efc7838d606b2a95a7.zip + +lib_ignore = + ${esp32c6_base.lib_ignore} + ESP32 Codec2 diff --git a/variants/esp32c6/heltec_rcc6/variant.h b/variants/esp32c6/heltec_rcc6/variant.h new file mode 100644 index 00000000000..f43cf0489f0 --- /dev/null +++ b/variants/esp32c6/heltec_rcc6/variant.h @@ -0,0 +1,56 @@ +#pragma once + +#define _VARIANT_HELTEC_RCC6_ + +#define BUTTON_PIN 9 + +#define HAS_SCREEN 1 +#define HAS_SPI_TFT 1 +#define USE_TFTDISPLAY 1 +#define HAS_GPS 0 +#define HAS_WIRE 0 +#undef GPS_RX_PIN +#undef GPS_TX_PIN + +#define USE_SX1262 +#define LORA_SCK 21 +#define LORA_MISO 20 +#define LORA_MOSI 22 +#define LORA_CS 23 +#define LORA_RESET 8 +#define LORA_DIO1 19 +#define LORA_BUSY 10 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_BUSY +#define SX126X_RESET LORA_RESET +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +#define BATTERY_PIN 6 +#define ADC_CHANNEL ADC_CHANNEL_6 +#define ADC_CTRL 5 +#define ADC_CTRL_ENABLED HIGH +#define ADC_MULTIPLIER 4.95f + +#define TFT_SCL 4 +#define TFT_SDA 15 +#define TFT_CS 18 +#define TFT_RS 3 +#define TFT_DC TFT_RS +#define TFT_RST 0 +#define TFT_EN 2 +#define TFT_EN_ON LOW +#define TFT_EN_OFF HIGH +#define VTFT_CTRL TFT_EN +#define TFT_BL 1 +#define TFT_BLK TFT_BL +#define TFT_BACKLIGHT_ON HIGH +#define TFT_BACKLIGHT_OFF LOW +#define TFT_WIDTH 128 +#define TFT_HEIGHT 220 +#define TFT_BLACK 0 +#define SPI_FREQUENCY 4000000 +#define SCREEN_ROTATE + +#define SERIAL_PRINT_PORT 1