diff --git a/arch/esp32/esp32c3.ini b/arch/esp32/esp32c3.ini
new file mode 100644
index 000000000..cca95f48a
--- /dev/null
+++ b/arch/esp32/esp32c3.ini
@@ -0,0 +1,45 @@
+[esp32c3_base]
+extends = arduino_base
+platform = platformio/espressif32@^6.1.0
+build_src_filter =
+ ${arduino_base.build_src_filter} - - - -
+upload_speed = 961200
+monitor_speed = 115200
+debug_init_break = tbreak setup
+monitor_filters = esp32_c3_exception_decoder
+board_build.filesystem = littlefs
+
+# Remove -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL for low level BLE logging.
+# See library directory for BLE logging possible values: .pio/libdeps/tbeam/NimBLE-Arduino/src/log_common/log_common.h
+# This overrides the BLE logging default of LOG_LEVEL_INFO (1) from: .pio/libdeps/tbeam/NimBLE-Arduino/src/esp_nimble_cfg.h
+build_flags =
+ ${arduino_base.build_flags}
+ -Wall
+ -Wextra
+ -Isrc/platform/esp32
+ -std=c++11
+ -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG
+ -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
+ -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
+ -DCONFIG_BT_NIMBLE_ENABLED
+ -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
+ -DCONFIG_BT_NIMBLE_MAX_CCCDS=20
+ -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
+ ;-DDEBUG_HEAP
+
+lib_deps =
+ ${arduino_base.lib_deps}
+ ${networking_base.lib_deps}
+ ${environmental_base.lib_deps}
+ https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
+ h2zero/NimBLE-Arduino@^1.4.0
+ https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
+ caveman99/ESP32 Codec2@^1.0.1
+
+lib_ignore =
+ segger_rtt
+ ESP32 BLE Arduino
+
+; customize the partition table
+; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
+board_build.partitions = partition-table.csv
\ No newline at end of file
diff --git a/monitor/filter_c3_exception_decoder.py b/monitor/filter_c3_exception_decoder.py
new file mode 100644
index 000000000..e59b0be2a
--- /dev/null
+++ b/monitor/filter_c3_exception_decoder.py
@@ -0,0 +1,155 @@
+# Copyright (c) 2014-present PlatformIO
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import re
+import subprocess
+import sys
+
+from platformio.project.exception import PlatformioException
+from platformio.public import (
+ DeviceMonitorFilterBase,
+ load_build_metadata,
+)
+
+# By design, __init__ is called inside miniterm and we can't pass context to it.
+# pylint: disable=attribute-defined-outside-init
+
+IS_WINDOWS = sys.platform.startswith("win")
+
+
+class Esp32C3ExceptionDecoder(DeviceMonitorFilterBase):
+ NAME = "esp32_c3_exception_decoder"
+
+ PCADDR_PATTERN = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE)
+
+ def __call__(self):
+ self.buffer = ""
+ self.pcaddr_re = self.PCADDR_PATTERN
+
+ self.firmware_path = None
+ self.addr2line_path = None
+ self.enabled = self.setup_paths()
+
+ if self.config.get("env:" + self.environment, "build_type") != "debug":
+ print(
+ """
+Please build project in debug configuration to get more details about an exception.
+See https://docs.platformio.org/page/projectconf/build_configurations.html
+
+"""
+ )
+
+ return self
+
+ def setup_paths(self):
+ self.project_dir = os.path.abspath(self.project_dir)
+ try:
+ data = load_build_metadata(self.project_dir, self.environment)
+ self.firmware_path = data["prog_path"]
+ if not os.path.isfile(self.firmware_path):
+ sys.stderr.write(
+ "%s: disabling, firmware at %s does not exist, rebuild the project?\n"
+ % (self.__class__.__name__, self.firmware_path)
+ )
+ return False
+
+ if self.addr2line_path is None:
+ cc_path = data.get("cc_path", "")
+ if "-gcc" in cc_path:
+ self.addr2line_path = cc_path.replace("-gcc", "-addr2line")
+ else:
+ sys.stderr.write(
+ "%s: disabling, failed to find addr2line.\n"
+ % self.__class__.__name__
+ )
+ return False
+
+ if not os.path.isfile(self.addr2line_path):
+ sys.stderr.write(
+ "%s: disabling, addr2line at %s does not exist\n"
+ % (self.__class__.__name__, self.addr2line_path)
+ )
+ return False
+
+ return True
+ except PlatformioException as e:
+ sys.stderr.write(
+ "%s: disabling, exception while looking for addr2line: %s\n"
+ % (self.__class__.__name__, e)
+ )
+ return False
+
+ def rx(self, text):
+ if not self.enabled:
+ return text
+
+ last = 0
+ while True:
+ idx = text.find("\n", last)
+ if idx == -1:
+ if len(self.buffer) < 4096:
+ self.buffer += text[last:]
+ break
+
+ line = text[last:idx]
+ if self.buffer:
+ line = self.buffer + line
+ self.buffer = ""
+ last = idx + 1
+
+ # Output each trace on a separate line below ours
+ # Logic identical to https://github.com/espressif/esp-idf/blob/master/tools/idf_monitor_base/logger.py#L131
+ for m in re.finditer(self.pcaddr_re, line):
+ if m is None:
+ continue
+
+ trace = self.get_backtrace(m)
+ if len(trace) != "":
+ text = text[: last] + trace + text[last :]
+ last += len(trace)
+
+ return text
+
+ def get_backtrace(self, match):
+ trace = "\n"
+ enc = "mbcs" if IS_WINDOWS else "utf-8"
+ args = [self.addr2line_path, u"-fipC", u"-e", self.firmware_path]
+ try:
+ addr = match.group()
+ output = (
+ subprocess.check_output(args + [addr])
+ .decode(enc)
+ .strip()
+ )
+ output = output.replace(
+ "\n", "\n "
+ ) # newlines happen with inlined methods
+ output = self.strip_project_dir(output)
+ # Output the trace in yellow color so that it is easier to spot
+ trace += "\033[33m=> %s: %s\033[0m\n" % (addr, output)
+ except subprocess.CalledProcessError as e:
+ sys.stderr.write(
+ "%s: failed to call %s: %s\n"
+ % (self.__class__.__name__, self.addr2line_path, e)
+ )
+ return trace
+
+ def strip_project_dir(self, trace):
+ while True:
+ idx = trace.find(self.project_dir)
+ if idx == -1:
+ break
+ trace = trace[:idx] + trace[idx + len(self.project_dir) + 1 :]
+ return trace
diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp
index 8683f9cb1..0a93298b9 100644
--- a/src/mesh/http/ContentHandler.cpp
+++ b/src/mesh/http/ContentHandler.cpp
@@ -833,7 +833,7 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
if (WiFi.encryptionType(i) != WIFI_AUTH_OPEN) {
JSONObject thisNetwork;
thisNetwork["ssid"] = new JSONValue(ssidArray);
- thisNetwork["rssi"] = new JSONValue(WiFi.RSSI(i));
+ thisNetwork["rssi"] = new JSONValue(int(WiFi.RSSI(i)));
networkObjs.push_back(new JSONValue(thisNetwork));
}
// Yield some cpu cycles to IP stack.
diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp
index 3247d02c1..5d49a213e 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -72,7 +72,8 @@ void setupModules()
new AirQualityTelemetryModule();
}
#endif
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
+ !defined(CONFIG_IDF_TARGET_ESP32C3)
new SerialModule();
#endif
#ifdef ARCH_ESP32
diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp
index 5637f7eb1..35b83e6c6 100644
--- a/src/modules/SerialModule.cpp
+++ b/src/modules/SerialModule.cpp
@@ -44,7 +44,8 @@
*/
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
+ !defined(CONFIG_IDF_TARGET_ESP32C3)
#define RX_BUFFER 128
#define TIMEOUT 250
diff --git a/src/modules/SerialModule.h b/src/modules/SerialModule.h
index 8c53d2064..cc696316d 100644
--- a/src/modules/SerialModule.h
+++ b/src/modules/SerialModule.h
@@ -8,7 +8,8 @@
#include
#include
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
+ !defined(CONFIG_IDF_TARGET_ESP32C3)
class SerialModule : public StreamAPI, private concurrency::OSThread
{
diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp
index f6bd4f50e..95e899a71 100644
--- a/src/platform/esp32/main-esp32.cpp
+++ b/src/platform/esp32/main-esp32.cpp
@@ -178,6 +178,7 @@ void cpuDeepSleep(uint64_t msecToWake)
Note: we don't isolate pins that are used for the LORA, LED, i2c, spi or the wake button
*/
+#if SOC_RTCIO_HOLD_SUPPORTED
static const uint8_t rtcGpios[] = {/* 0, */ 2,
/* 4, */
#ifndef USE_JTAG
@@ -191,6 +192,7 @@ void cpuDeepSleep(uint64_t msecToWake)
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
+#endif
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
// to detect wake and in normal operation the external part drives them hard.
@@ -200,7 +202,9 @@ void cpuDeepSleep(uint64_t msecToWake)
#ifdef BUTTON_PIN
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
+#if SOC_RTCIO_HOLD_SUPPORTED
uint64_t gpioMask = (1ULL << BUTTON_PIN);
+#endif
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
@@ -210,7 +214,9 @@ void cpuDeepSleep(uint64_t msecToWake)
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of
// just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
+#if SOC_PM_SUPPORT_EXT_WAKEUP
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
+#endif
#endif
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
diff --git a/src/sleep.cpp b/src/sleep.cpp
index 77c8d7119..9bdd52cb3 100644
--- a/src/sleep.cpp
+++ b/src/sleep.cpp
@@ -61,13 +61,15 @@ void setCPUFast(bool on)
* all WiFi use cases.
* (Added: Dec 23, 2021 by Jm Casler)
*/
+#ifndef CONFIG_IDF_TARGET_ESP32C3
LOG_DEBUG("Setting CPU to 240mhz because WiFi is in use.\n");
setCpuFrequencyMhz(240);
+#endif
return;
}
// The Heltec LORA32 V1 runs at 26 MHz base frequency and doesn't react well to switching to 80 MHz...
-#ifndef ARDUINO_HELTEC_WIFI_LORA_32
+#if !defined(ARDUINO_HELTEC_WIFI_LORA_32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
setCpuFrequencyMhz(on ? 240 : 80);
#endif
@@ -337,6 +339,8 @@ void enableModemSleep()
esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32S2
esp32_config.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
+#elif CONFIG_IDF_TARGET_ESP32C3
+ esp32_config.max_freq_mhz = CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ;
#else
esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
#endif
diff --git a/variants/ai-c3/platformio.ini b/variants/ai-c3/platformio.ini
new file mode 100644
index 000000000..76ecd9f0f
--- /dev/null
+++ b/variants/ai-c3/platformio.ini
@@ -0,0 +1,8 @@
+[env:ai-c3]
+extends = esp32c3_base
+board = esp32-c3-devkitm-1
+board_level = extra
+build_flags =
+ ${esp32_base.build_flags}
+ -D PRIVATE_HW
+ -I variants/ai-c3
diff --git a/variants/ai-c3/variant.h b/variants/ai-c3/variant.h
new file mode 100644
index 000000000..05f0abb51
--- /dev/null
+++ b/variants/ai-c3/variant.h
@@ -0,0 +1,26 @@
+// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
+
+#define I2C_SDA 8
+#define I2C_SCL 9
+
+#define BUTTON_PIN 0
+
+#define USE_RF95
+#undef RF95_SCK
+#define RF95_SCK 4
+#undef RF95_MISO
+#define RF95_MISO 5
+#undef RF95_MOSI
+#define RF95_MOSI 6
+#undef RF95_NSS
+#define RF95_NSS 7
+
+#define LORA_DIO0 10 // a No connect on the SX1262 module
+#define LORA_DIO1 3 // a No connect on the SX1262 module
+#define LORA_RESET 2
+
+#undef GPS_RX_PIN
+#undef GPS_TX_PIN
+
+#define HAS_SCREEN 0
+#define HAS_GPS 0