Merge pull request #2 from shinys000114/master

Improved AP reconnection logic in STA mode
This commit is contained in:
Hardkernel Co., Ltd.
2025-12-08 10:14:32 +09:00
committed by GitHub
8 changed files with 96 additions and 39 deletions

View File

@@ -77,4 +77,13 @@ sudo apt install nodejs npm nanopb
1. After flashing, the ESP32 will either connect to the pre-configured Wi-Fi network or start an Access Point (APSTA). 1. After flashing, the ESP32 will either connect to the pre-configured Wi-Fi network or start an Access Point (APSTA).
2. Check the serial monitor logs to find the IP address assigned to the device in STA mode, or the default AP address (usually `192.168.4.1`). 2. Check the serial monitor logs to find the IP address assigned to the device in STA mode, or the default AP address (usually `192.168.4.1`).
3. Open a web browser and navigate to the device's IP address. 3. Open a web browser and navigate to the device's IP address.
4. You should now see the ODROID Remote control panel. 4. You should now see the ODROID Remote control panel.
## Docs
- Hardkernel WiKi: [https://wiki.odroid.com/accessory/powermate](https://wiki.odroid.com/accessory/powermate)
## Repo
- Hardkernel Github: [https://github.com/hardkernel/odroid-powermate](https://github.com/hardkernel/odroid-powermate)
- Original Repo: [https://github.com/shinys000114/odroid-powermate](https://github.com/shinys000114/odroid-powermate)

5
example/logger/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
/.venv/
/venv/
status_pb2.py
test.csv
plot.png

View File

@@ -35,7 +35,7 @@ python3 logger.py -u admin -p password -o test.csv 192.168.30.5
#### Plot data #### Plot data
```shell ```shell
python3 csv_2_plot.py test.csv plot.png python3 csv_2_plot.py test.csv plot.png [--type power voltage current]
``` ```
![plot.png](plot.png) ![plot.png](plot.png)

View File

@@ -1,17 +1,19 @@
import argparse import argparse
import matplotlib.dates as mdates import matplotlib.dates as mdates
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import os
import pandas as pd import pandas as pd
def plot_power_data(csv_path, output_path): def plot_power_data(csv_path, output_path, plot_types):
""" """
Reads power data from a CSV file and generates a plot image. Reads power data from a CSV file and generates a plot image.
Args: Args:
csv_path (str): The path to the input CSV file. csv_path (str): The path to the input CSV file.
output_path (str): The path to save the output plot image. output_path (str): The path to save the output plot image.
plot_types (list): A list of strings indicating which plots to generate
(e.g., ['power', 'voltage', 'current']).
""" """
try: try:
# Read the CSV file into a pandas DataFrame # Read the CSV file into a pandas DataFrame
@@ -25,44 +27,47 @@ def plot_power_data(csv_path, output_path):
print(f"An error occurred while reading the CSV file: {e}") print(f"An error occurred while reading the CSV file: {e}")
return return
# Create a figure and a set of subplots (3 rows, 1 column) # --- Plotting Configuration ---
plot_configs = {
'power': {'title': 'Power Consumption', 'ylabel': 'Power (W)',
'cols': ['vin_power', 'main_power', 'usb_power']},
'voltage': {'title': 'Voltage', 'ylabel': 'Voltage (V)',
'cols': ['vin_voltage', 'main_voltage', 'usb_voltage']},
'current': {'title': 'Current', 'ylabel': 'Current (A)', 'cols': ['vin_current', 'main_current', 'usb_current']}
}
channel_labels = ['VIN', 'MAIN', 'USB']
channel_colors = ['red', 'green', 'blue']
num_plots = len(plot_types)
if num_plots == 0:
print("No plot types selected. Exiting.")
return
# Create a figure and a set of subplots based on the number of selected plot types.
# sharex=True makes all subplots share the same x-axis (time) # sharex=True makes all subplots share the same x-axis (time)
fig, axes = plt.subplots(3, 1, figsize=(15, 18), sharex=True) # squeeze=False ensures that 'axes' is always a 2D array, even if num_plots is 1.
fig, axes = plt.subplots(num_plots, 1, figsize=(15, 6 * num_plots), sharex=True, squeeze=False)
axes = axes.flatten() # Flatten the 2D array to 1D for easier iteration
# --- Plot 1: Power (W) --- # --- Loop through selected plot types and generate plots ---
ax1 = axes[0] for i, plot_type in enumerate(plot_types):
ax1.plot(df['timestamp'], df['vin_power'], label='VIN', color='red') ax = axes[i]
ax1.plot(df['timestamp'], df['main_power'], label='MAIN', color='green') config = plot_configs[plot_type]
ax1.plot(df['timestamp'], df['usb_power'], label='USB', color='blue')
ax1.set_title('Power Consumption')
ax1.set_ylabel('Power (W)')
ax1.legend()
ax1.grid(True, which='both', linestyle='--', linewidth=0.5)
# --- Plot 2: Voltage (V) --- for j, col_name in enumerate(config['cols']):
ax2 = axes[1] ax.plot(df['timestamp'], df[col_name], label=channel_labels[j], color=channel_colors[j])
ax2.plot(df['timestamp'], df['vin_voltage'], label='VIN', color='red')
ax2.plot(df['timestamp'], df['main_voltage'], label='MAIN', color='green')
ax2.plot(df['timestamp'], df['usb_voltage'], label='USB', color='blue')
ax2.set_title('Voltage')
ax2.set_ylabel('Voltage (V)')
ax2.legend()
ax2.grid(True, which='both', linestyle='--', linewidth=0.5)
# --- Plot 3: Current (A) --- ax.set_title(config['title'])
ax3 = axes[2] ax.set_ylabel(config['ylabel'])
ax3.plot(df['timestamp'], df['vin_current'], label='VIN', color='red') ax.legend()
ax3.plot(df['timestamp'], df['main_current'], label='MAIN', color='green') ax.grid(True, which='both', linestyle='--', linewidth=0.5)
ax3.plot(df['timestamp'], df['usb_current'], label='USB', color='blue')
ax3.set_title('Current')
ax3.set_ylabel('Current (A)')
ax3.legend()
ax3.grid(True, which='both', linestyle='--', linewidth=0.5)
# --- Formatting the x-axis (Time) --- # --- Formatting the x-axis (Time) ---
# Improve date formatting on the x-axis # Improve date formatting on the x-axis
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S')) # Apply formatting to the last subplot's x-axis
ax3.xaxis.set_major_locator(plt.MaxNLocator(15)) # Limit the number of ticks last_ax = axes[-1]
last_ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
last_ax.xaxis.set_major_locator(plt.MaxNLocator(15)) # Limit the number of ticks
plt.xlabel('Time') plt.xlabel('Time')
plt.xticks(rotation=45) plt.xticks(rotation=45)
@@ -86,9 +91,17 @@ def main():
parser = argparse.ArgumentParser(description="Generate a plot from an Odroid PowerMate CSV log file.") parser = argparse.ArgumentParser(description="Generate a plot from an Odroid PowerMate CSV log file.")
parser.add_argument("input_csv", help="Path to the input CSV log file.") parser.add_argument("input_csv", help="Path to the input CSV log file.")
parser.add_argument("output_image", help="Path to save the output plot image (e.g., plot.png).") parser.add_argument("output_image", help="Path to save the output plot image (e.g., plot.png).")
parser.add_argument(
"-t", "--type",
nargs='+',
choices=['power', 'voltage', 'current'],
default=['power', 'voltage', 'current'],
help="Types of plots to generate. Choose from 'power', 'voltage', 'current'. "
"Default is to generate all three."
)
args = parser.parse_args() args = parser.parse_args()
plot_power_data(args.input_csv, args.output_image) plot_power_data(args.input_csv, args.output_image, args.type)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -8,5 +8,6 @@
void wifi_init_sta(void); void wifi_init_sta(void);
void wifi_init_ap(void); void wifi_init_ap(void);
void initialize_sntp(void); void initialize_sntp(void);
void wifi_set_auto_reconnect(bool enable);
#endif // ODROID_POWER_MATE_PRIV_WIFI_H #endif // ODROID_POWER_MATE_PRIV_WIFI_H

View File

@@ -81,6 +81,14 @@ void wifi_scan_aps(wifi_ap_record_t** ap_records, uint16_t* count)
*count = 0; *count = 0;
*ap_records = NULL; *ap_records = NULL;
wifi_set_auto_reconnect(false);
wifi_ap_record_t ap_info;
if (esp_wifi_sta_get_ap_info(&ap_info) != ESP_OK)
{
esp_wifi_disconnect();
}
// Start scan, this is a blocking call // Start scan, this is a blocking call
if (esp_wifi_scan_start(NULL, true) == ESP_OK) if (esp_wifi_scan_start(NULL, true) == ESP_OK)
{ {
@@ -100,6 +108,16 @@ void wifi_scan_aps(wifi_ap_record_t** ap_records, uint16_t* count)
} }
} }
} }
wifi_set_auto_reconnect(true);
if (esp_wifi_sta_get_ap_info(&ap_info) != ESP_OK)
{
if (!nconfig_value_is_not_set(WIFI_SSID))
{
wifi_connect();
}
}
} }
esp_err_t wifi_get_current_ap_info(wifi_ap_record_t* ap_info) esp_err_t wifi_get_current_ap_info(wifi_ap_record_t* ap_info)

View File

@@ -16,9 +16,13 @@
#include "wifi.h" #include "wifi.h"
#include "indicator.h" #include "indicator.h"
static bool s_auto_reconnect = true;
static const char* TAG = "WIFI"; static const char* TAG = "WIFI";
void wifi_set_auto_reconnect(bool enable) { s_auto_reconnect = enable; }
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{ {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED)
@@ -46,10 +50,18 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t e
} }
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
{ {
led_set(LED_RED, BLINK_TRIPLE); led_set(LED_BLU, BLINK_TRIPLE);
wifi_event_sta_disconnected_t* event = (wifi_event_sta_disconnected_t*)event_data; wifi_event_sta_disconnected_t* event = (wifi_event_sta_disconnected_t*)event_data;
ESP_LOGW(TAG, "Disconnected from AP, reason: %s", wifi_reason_str(event->reason)); ESP_LOGW(TAG, "Disconnected from AP, reason: %s", wifi_reason_str(event->reason));
// ESP-IDF will automatically try to reconnect by default.
if (event->reason != WIFI_REASON_ASSOC_LEAVE)
{
if (s_auto_reconnect && !nconfig_value_is_not_set(WIFI_SSID))
{
ESP_LOGI(TAG, "Connection lost, attempting to reconnect...");
esp_wifi_connect();
}
}
} }
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
{ {

View File

@@ -3,4 +3,3 @@
nvs,data,nvs,0x9000,24K, nvs,data,nvs,0x9000,24K,
phy_init,data,phy,0xf000,4K, phy_init,data,phy,0xf000,4K,
factory,app,factory,0x10000,2M, factory,app,factory,0x10000,2M,
littlefs, data, littlefs, ,1536K,
1 # ESP-IDF Partition Table
3 nvs,data,nvs,0x9000,24K,
4 phy_init,data,phy,0xf000,4K,
5 factory,app,factory,0x10000,2M,
littlefs, data, littlefs, ,1536K,