Merge branch 'main' into power_management

This commit is contained in:
Phillip Kühne 2025-02-09 20:20:27 +01:00
commit 862310bb3c
Signed by: phillip
GPG Key ID: E4C1C4D2F90902AA
10 changed files with 456 additions and 62 deletions

10
.gitignore vendored
View File

@ -34,13 +34,15 @@ docs/*
*.out
*.app
#VSCode
# VSCode
.vscode/*
.history/
*.vsix
*.workspace
# Build artifacts and cache
build/
# Tool-generated files
.cache/
compile_commands.json
compile_commands.json
# Build directories
build/

View File

@ -3,7 +3,7 @@
Dezibot dezibot = Dezibot();
// How many times to run a command on the display consecutively;
const uint16_t iterations = 5000;
const uint16_t iterations = 1000;
void setup() {
dezibot.begin();
@ -23,4 +23,10 @@ void loop() {
dezibot.display.println(iter);
}
delay(10);
//Completely off
dezibot.display.clear();
sleep(10);
//Completely on
dezibot.display.invertColor();
sleep(10);
}

View File

@ -1,10 +1,10 @@
#include "Dezibot.h"
#include "esp_pm.h"
#include "esp_pm.h"
#include "esp_task_wdt.h"
Dezibot dezibot;
const uint16_t cycleTime = 2e4; //20000 ms = 20 s
const uint16_t cycleTime = 20e3;
esp_pm_lock_handle_t cpuFreqLock;
esp_pm_lock_handle_t apbFreqLock;
@ -12,39 +12,39 @@ esp_pm_lock_handle_t lightSleepLock;
static bool pmLocksCreated = false;
void stress_task0(void *pvParameters) {
// Register ourselves with the task watchdog
if (esp_task_wdt_add(NULL) != ESP_OK) {
Serial.println("Failed to add task 0 to TWDT");
}
while (1) {
void stress_task(void *pvParameters) {
// Register with task watchdog
ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
// Variables for load generation
volatile uint32_t x = 0;
for (uint32_t i = 0; i < 10000; i++) {
x += i;
TickType_t last_wake_time = xTaskGetTickCount();
while (1) {
// Compute-intensive period
for (uint32_t i = 0; i < 10000; i++) {
x += i;
// Mix in some memory operations
if (i % 100 == 0) {
// Force cache misses occasionally
void* temp = malloc(32);
if (temp) {
free(temp);
}
}
}
// Reset watchdog
ESP_ERROR_CHECK(esp_task_wdt_reset());
}
esp_task_wdt_reset();
}
}
void stress_task1(void *pvParameters) {
// Register ourselves with the task watchdog
if (esp_task_wdt_add(NULL) != ESP_OK) {
Serial.println("Failed to add task 0 to TWDT");
}
while (1) {
volatile uint32_t x = 0;
for (uint32_t i = 0; i < 10000; i++) {
x += i;
}
esp_task_wdt_reset();
}
}
void setup() {
Serial.begin(115200);
while (!Serial) {
;
}
// while (!Serial) {
// ;
// }
uint32_t Freq = getCpuFrequencyMhz();
Serial.print("CPU Freq = ");
Serial.print(Freq);
@ -87,6 +87,17 @@ void setup() {
Serial.end();
}
void blip_io(int times) {
constexpr int ioPin = 17;
pinMode(ioPin, OUTPUT);
for(int i = 0; i<times; i++) {
digitalWrite(ioPin,1);
delay(1);
digitalWrite(ioPin,0);
delay(1);
}
}
/*
* A function that prints to serial, setting up the serial peripheral beforehand, and shutting it down afterwards.
* Do not use this on a regular basis, as it is probably very slow. It is useful for not having the serial peripheral
@ -96,10 +107,6 @@ void setup() {
*/
void setupAndCleanupSerialPrint(char *str) {
Serial.begin(115200);
while (!Serial) {
;
;
}
delay(10);
Serial.print(str);
delay(10);
@ -111,10 +118,11 @@ void setupAndCleanupSerialPrint(char *str) {
void loop() {
// Alternate between 20 Seconds at 100% CPU load, no artificial load but not sleeping, and normal behaviour (light sleep when there is nothing to do).
setupAndCleanupSerialPrint("Beginning stress phase\n");
blip_io(1);
TaskHandle_t core0StressTask = NULL;
TaskHandle_t core1StressTask = NULL;
xTaskCreatePinnedToCore(stress_task0, "CPU0Stress", 4096, NULL, 1, &core0StressTask, 0);
xTaskCreatePinnedToCore(stress_task1, "CPU1Stress", 4096, NULL, 1, &core1StressTask, 1);
xTaskCreatePinnedToCore(stress_task, "CPU0Stress", 4096, NULL, 1, &core0StressTask, 0);
xTaskCreatePinnedToCore(stress_task, "CPU1Stress", 4096, NULL, 1, &core1StressTask, 1);
vTaskDelay(pdMS_TO_TICKS(cycleTime));
setupAndCleanupSerialPrint("Still alive after waiting cycleTime after setting up stress tasks...\n");
esp_task_wdt_delete(core0StressTask);
@ -123,12 +131,14 @@ void loop() {
vTaskDelete(core1StressTask);
// Now disable light sleep
setupAndCleanupSerialPrint("Beginning idle with power management disabled\n");
blip_io(2);
esp_pm_lock_acquire(cpuFreqLock);
esp_pm_lock_acquire(apbFreqLock);
esp_pm_lock_acquire(lightSleepLock);
vTaskDelay(pdMS_TO_TICKS(cycleTime));
// Restore auto light sleep and dynamic frequency scaling
setupAndCleanupSerialPrint("Beginning idle with power management reenabled\n");
blip_io(3);
esp_pm_lock_release(cpuFreqLock);
esp_pm_lock_release(apbFreqLock);
esp_pm_lock_release(lightSleepLock);

View File

@ -0,0 +1,230 @@
/*
* Whetstone benchmark in C. This program is a translation of the
* original Algol version in "A Synthetic Benchmark" by H.J. Curnow
* and B.A. Wichman in Computer Journal, Vol 19 #1, February 1976.
*
* Used to test compiler optimization and floating point performance.
*
* Compile by: cc -O -s -o whet whet.c
* or: cc -O -DPOUT -s -o whet whet.c
* if output is desired.
*/
#define ITERATIONS 10 /* 1 Million Whetstone instructions */
#include "math.h"
double x1, x2, x3, x4, x, y, z, t, t1, t2;
double e1[4];
int i, j, k, l, n1, n2, n3, n4, n6, n7, n8, n9, n10, n11;
run_bench()
{
/* initialize constants */
t = 0.499975;
t1 = 0.50025;
t2 = 2.0;
/* set values of module weights */
n1 = 0 * ITERATIONS;
n2 = 12 * ITERATIONS;
n3 = 14 * ITERATIONS;
n4 = 345 * ITERATIONS;
n6 = 210 * ITERATIONS;
n7 = 32 * ITERATIONS;
n8 = 899 * ITERATIONS;
n9 = 616 * ITERATIONS;
n10 = 0 * ITERATIONS;
n11 = 93 * ITERATIONS;
/* MODULE 1: simple identifiers */
x1 = 1.0;
x2 = x3 = x4 = -1.0;
for(i = 1; i <= n1; i += 1) {
x1 = ( x1 + x2 + x3 - x4 ) * t;
x2 = ( x1 + x2 - x3 - x4 ) * t;
x3 = ( x1 - x2 + x3 + x4 ) * t;
x4 = (-x1 + x2 + x3 + x4 ) * t;
}
#ifdef POUT
pout(n1, n1, n1, x1, x2, x3, x4);
#endif
/* MODULE 2: array elements */
e1[0] = 1.0;
e1[1] = e1[2] = e1[3] = -1.0;
for (i = 1; i <= n2; i +=1) {
e1[0] = ( e1[0] + e1[1] + e1[2] - e1[3] ) * t;
e1[1] = ( e1[0] + e1[1] - e1[2] + e1[3] ) * t;
e1[2] = ( e1[0] - e1[1] + e1[2] + e1[3] ) * t;
e1[3] = (-e1[0] + e1[1] + e1[2] + e1[3] ) * t;
}
#ifdef POUT
pout(n2, n3, n2, e1[0], e1[1], e1[2], e1[3]);
#endif
/* MODULE 3: array as parameter */
for (i = 1; i <= n3; i += 1)
pa(e1);
#ifdef POUT
pout(n3, n2, n2, e1[0], e1[1], e1[2], e1[3]);
#endif
/* MODULE 4: conditional jumps */
j = 1;
for (i = 1; i <= n4; i += 1) {
if (j == 1)
j = 2;
else
j = 3;
if (j > 2)
j = 0;
else
j = 1;
if (j < 1 )
j = 1;
else
j = 0;
}
#ifdef POUT
pout(n4, j, j, x1, x2, x3, x4);
#endif
/* MODULE 5: omitted */
/* MODULE 6: integer arithmetic */
j = 1;
k = 2;
l = 3;
for (i = 1; i <= n6; i += 1) {
j = j * (k - j) * (l -k);
k = l * k - (l - j) * k;
l = (l - k) * (k + j);
e1[l - 2] = j + k + l; /* C arrays are zero based */
e1[k - 2] = j * k * l;
}
#ifdef POUT
pout(n6, j, k, e1[0], e1[1], e1[2], e1[3]);
#endif
/* MODULE 7: trig. functions */
x = y = 0.5;
for(i = 1; i <= n7; i +=1) {
x = t * atan(t2*sin(x)*cos(x)/(cos(x+y)+cos(x-y)-1.0));
y = t * atan(t2*sin(y)*cos(y)/(cos(x+y)+cos(x-y)-1.0));
}
#ifdef POUT
pout(n7, j, k, x, x, y, y);
#endif
/* MODULE 8: procedure calls */
x = y = z = 1.0;
for (i = 1; i <= n8; i +=1)
p3(x, y, &z);
#ifdef POUT
pout(n8, j, k, x, y, z, z);
#endif
/* MODULE9: array references */
j = 1;
k = 2;
l = 3;
e1[0] = 1.0;
e1[1] = 2.0;
e1[2] = 3.0;
for(i = 1; i <= n9; i += 1)
p0();
#ifdef POUT
pout(n9, j, k, e1[0], e1[1], e1[2], e1[3]);
#endif
/* MODULE10: integer arithmetic */
j = 2;
k = 3;
for(i = 1; i <= n10; i +=1) {
j = j + k;
k = j + k;
j = k - j;
k = k - j - j;
}
#ifdef POUT
pout(n10, j, k, x1, x2, x3, x4);
#endif
/* MODULE11: standard functions */
x = 0.75;
for(i = 1; i <= n11; i +=1)
x = sqrt( exp( log(x) / t1));
#ifdef POUT
pout(n11, j, k, x, x, x, x);
#endif
}
pa(e)
double e[4];
{
register int j;
j = 0;
lab:
e[0] = ( e[0] + e[1] + e[2] - e[3] ) * t;
e[1] = ( e[0] + e[1] - e[2] + e[3] ) * t;
e[2] = ( e[0] - e[1] + e[2] + e[3] ) * t;
e[3] = ( -e[0] + e[1] + e[2] + e[3] ) / t2;
j += 1;
if (j < 6)
goto lab;
}
p3(x, y, z)
double x, y, *z;
{
x = t * (x + y);
y = t * (x + y);
*z = (x + y) /t2;
}
p0()
{
e1[j] = e1[k];
e1[k] = e1[l];
e1[l] = e1[j];
}
#ifdef POUT
pout(n, j, k, x1, x2, x3, x4)
int n, j, k;
double x1, x2, x3, x4;
{
printf("%6d%6d%6d %5e %5e %5e %5e\n",
n, j, k, x1, x2, x3, x4);
}
#endif

View File

@ -9,15 +9,26 @@ void setup() {
dezibot.infraredLight.begin();
}
void blink_times(int times) {
constexpr int ioPin = 17;
pinMode(ioPin, OUTPUT);
for(int i = 0; i<times; i++) {
digitalWrite(ioPin,1);
delay(300);
digitalWrite(ioPin,0);
delay(300);
}
}
void loop() {
// put your main code here, to run repeatedly:
// Just turn the lights on alternating each five seconds, to get the consumption
dezibot.infraredLight.front.turnOn();
blink_times(1);
delay(cycleMillis);
dezibot.infraredLight.front.turnOff();
dezibot.infraredLight.bottom.turnOn();
delay(cycleMillis);
//Turn off and wait a little to measure baseline consumption
blink_times(2);
dezibot.infraredLight.bottom.turnOff();
delay(cycleMillis);
}

View File

@ -14,8 +14,7 @@ void loop() {
dezibot.motion.rotateAntiClockwise();
delay(20000);
// Turn on both motors at the same time
dezibot.motion.left.setSpeed(DEFAULT_BASE_VALUE);
dezibot.motion.right.setSpeed(DEFAULT_BASE_VALUE);
dezibot.motion.move();
delay(20000);
dezibot.motion.stop();
delay(20000);

View File

@ -0,0 +1,80 @@
#include <Adafruit_NeoPixel.h>
/*
WARNING: This example controls the RGB-LEDs directly, bypassing the
MultiColorLight component. This is not recommended for normal use, as it
bypasses the safety checks and color normalization. This is only intended
for diagnostic purposes.
*/
Adafruit_NeoPixel rgbLeds(3, 48);
void setup() { rgbLeds.begin(); }
void loop() {
// Ramp up the brightness of each color channel
// Allows for the PWM behaviour to quickly be observed on an oscilloscope
// Red
for (int bri = 0; bri < 0xFF; bri += 0x1) {
rgbLeds.setPixelColor(0, rgbLeds.Color(bri, 0, 0));
rgbLeds.show();
delay(10);
}
// Red
for (int bri = 0; bri < 0xFF; bri += 0x1) {
rgbLeds.setPixelColor(0, rgbLeds.Color(0, bri, 0));
rgbLeds.show();
delay(10);
}
// Blue
for (int bri = 0; bri < 0xFF; bri += 0x1) {
rgbLeds.setPixelColor(0, rgbLeds.Color(0, 0, bri));
rgbLeds.show();
delay(10);
}
// Combinations of the color channels
// Yellow (Red + Green)
for (int bri = 0; bri < 0xFF; bri += 0x1) {
rgbLeds.setPixelColor(0, rgbLeds.Color(bri, bri, 0));
rgbLeds.show();
delay(10);
}
// Purple (Red + Blue)
for (int bri = 0; bri < 0xFF; bri += 0x1) {
rgbLeds.setPixelColor(0, rgbLeds.Color(bri, 0, bri));
rgbLeds.show();
delay(10);
}
// Cyan (Green + Blue)
for (int bri = 0; bri < 0xFF; bri += 0x1) {
rgbLeds.setPixelColor(0, rgbLeds.Color(0, bri, bri));
rgbLeds.show();
delay(10);
}
// White (Red + Green + Blue)
for (int bri = 0; bri < 0xFF; bri += 0x1) {
rgbLeds.setPixelColor(0, rgbLeds.Color(bri, bri, bri));
rgbLeds.show();
delay(10);
}
// Now some constant states for comparable measurements
// Full brightness R+G+B
rgbLeds.setPixelColor(0, rgbLeds.Color(255, 255, 255));
rgbLeds.show();
sleep(5);
// Half brightness R+G+B
rgbLeds.setPixelColor(0, rgbLeds.Color(127, 127, 127));
rgbLeds.show();
sleep(5);
// Minimum brightness R+G+B
rgbLeds.setPixelColor(0, rgbLeds.Color(1, 1, 1));
rgbLeds.show();
sleep(5);
// Off (baseline)
rgbLeds.setPixelColor(0, rgbLeds.Color(0, 0, 0));
rgbLeds.show();
sleep(5);
}

View File

@ -3,7 +3,7 @@
Dezibot dezibot;
void setup() {
dezibot.lightDetection.begin();
dezibot.colorDetection.begin();
//dezibot.motion.detection.end();
// put your setup code here, to run once:
Serial.begin(115200);
@ -13,7 +13,7 @@ void setup() {
}
delay(1000);
// Test if VEML6040 is working correctly
char light_value = dezibot.lightDetection.getValue(DL_FRONT);
char light_value = dezibot.colorDetection.getColorValue(VEML_WHITE);
if (light_value != UINT16_MAX) {
Serial.printf("Light detection seems to be working (detected value: %d). Starting measurements...\r\n", light_value);
} else {
@ -29,6 +29,6 @@ void setup() {
void loop() {
// put your main code here, to run repeatedly:
dezibot.lightDetection.getValue(DL_FRONT);
dezibot.colorDetection.getColorValue(VEML_WHITE);
delay(10);
}

View File

@ -3,40 +3,46 @@
Dezibot dezibot = Dezibot();
const uint16_t cycleTime = 5e3; //5000 ms = 5 s
void blip_io(int times) {
constexpr int ioPin = 17;
pinMode(ioPin, OUTPUT);
for(int i = 0; i<times; i++) {
digitalWrite(ioPin,1);
delay(500);
digitalWrite(ioPin,0);
delay(500);
}
}
void setup() {
#ifdef DEBUG
Serial.begin(112500);
while (!Serial) {
; /* Wait for USB-CDC Serial init to complete. */
}
dezibot.display.begin();
dezibot.display.println("Debug enabled.");
Serial.println("Debug enabled.");
#endif
dezibot.communication.begin();
dezibot.communication.setGroupNumber(1);
dezibot.communication.sendMessage("Repeated send power consumption test commencing");
#ifdef DEBUG
dezibot.display.println("Mesh set up");
Serial.println("Mesh set up");
/* Set up receive handler */
dezibot.communication.onReceive(handle_receive);
dezibot.display.println("Set up receive. Printing incoming messages:");
Serial.println("Set up receive. Printing incoming messages:");
Serial.println("Sending broadcast messages to generate TX power consumption:");
#endif
blip_io(1);
delay(5000);
#ifdef DEBUG
Serial.println("Starting Transmission...");
#endif
blip_io(2);
}
void handle_receive(String &message) {
dezibot.display.println(message);
Serial.println(message);
}
void loop() {
/* Continuously send to consume power on TX */
for(int i=0; i<cycleTime; i++) {
dezibot.communication.sendMessage("Power Test Message");
delay(1);
}
delay(cycleTime);
dezibot.communication.sendMessage("Power Test Message");
sleep(2);
}

50
tools/generate_clangd_config.sh Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
PROJECT_PATH="$(cd "$SCRIPT_DIR/.." && pwd)"
# Default values that can be overridden by environment variables
# The ':=' operator means "use the environment variable if set, otherwise use this default"
: "${BOARD_FQBN:=esp32:esp32:esp32s3usbotg:USBMode=hwcdc}"
: "${LIBRARY_PATH:=$PROJECT_PATH}"
: "${BUILD_PATH:=$PROJECT_PATH/build}"
: "${BASE_INO_SKETCH_PATH:=$PROJECT_PATH/example/start}"
: "${COMPILE_COMMANDS:=compile_commands.json}"
mkdir -p "$BUILD_PATH"
if ! command -v arduino-cli >/dev/null 2>&1; then
echo "Error: arduino-cli is not installed or not in PATH"
echo "Please install it from https://arduino.github.io/arduino-cli/latest/installation/"
exit 1
fi
echo "Generating clangd configuration for $BOARD_FQBN in $BUILD_PATH"
echo "Based on compilation of $BASE_INO_SKETCH_PATH"
echo "This may take a while and will not produce any output until it's done."
echo "Please be patient..."
if ! arduino-cli compile \
--fqbn "$BOARD_FQBN" \
--library "$LIBRARY_PATH" \
--only-compilation-database \
--build-path "$BUILD_PATH" \
"$BASE_INO_SKETCH_PATH"; then
echo "Error: Failed to generate compilation database"
exit 1
fi
# Create symbolic link with error handling
if [ -f "$BUILD_PATH/$COMPILE_COMMANDS" ]; then
ln -sf "$BUILD_PATH/$COMPILE_COMMANDS" "$PROJECT_PATH/$COMPILE_COMMANDS"
echo "Successfully placed clangd configuration in project root"
echo "You no longer have to use the Arduino IDE for code completion \o/"
echo "Feel free to use any editor that supports clangd (e.g via LSP)"
echo "For example, Visual Studio Code with the 'clangd' extension, or VIM (with YouCompleteMe), or Emacs (with Eglot), or ..."
echo "Refer to https://clangd.llvm.org/installation#editor-plugins for more information"
else
echo "Error: Could not link \"complile_commands.json\" to the root directory. Please manually copy where needed."
exit 1
fi