diff --git a/Parcial 1/Homework3/Archivo_Lookup_Table.py b/Parcial 1/Homework3/Archivo_Lookup_Table.py new file mode 100644 index 0000000..0d9e238 --- /dev/null +++ b/Parcial 1/Homework3/Archivo_Lookup_Table.py @@ -0,0 +1,77 @@ + +"""Untitled3.ipynb + +Automatically generated by Colab. + +Original file is located at + https://colab.research.google.com/drive/1-ykZUAdzB6m7y0UvnUpPthAxPl4htzqB +""" + +!pip -q install numpy +import numpy as np +from google.colab import files + +def voltage_from_percent(p): + x = p + # f(x) = 4.60897e-09*x^5 - 1.13065e-06*x^4 + 9.13377e-05*x^3 - 0.00277445*x^2 + 0.0639597*x + 0.154238 + return (((((4.60897e-09*x - 1.13065e-06)*x + 9.13377e-05)*x + - 0.00277445)*x + 0.0639597)*x + 0.154238) + +# --- Parámetros LUT --- +MV_FS = 3300 # índice 0..3300 mV +USE_UINT8 = True # True: % entero (0..100); False: uint16 con x10 (0..1000) + + mV/1000 --- +def percent_from_mV(mv): + target = mv / 1000.0 + # Rango físico de tu poli (aprox): V(0)~0.154V, V(100)~3.168V + v0 = voltage_from_percent(0.0) + v1 = voltage_from_percent(100.0) + if target <= v0: return 0.0 + if target >= v1: return 100.0 + lo, hi = 0.0, 100.0 + for _ in range(40): # precisión alta + mid = 0.5*(lo+hi) + vm = voltage_from_percent(mid) + if vm < target: lo = mid + else: hi = mid + return 0.5*(lo+hi) + +percent = np.array([percent_from_mV(mv) for mv in range(MV_FS+1)]) + +if USE_UINT8: + lut_vals = np.rint(np.clip(percent, 0, 100)).astype(np.uint8) # 0..100 + c_type = "uint8_t"; LUT_SCALE = 1 +else: + lut_vals = np.rint(np.clip(percent*10.0, 0, 1000)).astype(np.uint16) # 0..1000 + c_type = "uint16_t"; LUT_SCALE = 10 + +lines = [] +lines.append("#ifndef LOOKUPTABLE_H") +lines.append("#define LOOKUPTABLE_H") +lines.append("") +lines.append("#include ") +lines.append(f"#define LUT_SIZE {MV_FS+1}") +lines.append(f"#define LUT_SCALE {LUT_SCALE} // 1:% entero; 10: décimas de %") +lines.append("#define LUT_INDEX_IS_MV 1 // índice = mV (0..3300)") +lines.append("") +lines.append(f"static const {c_type} lookup_table[LUT_SIZE] = {{") + +row=[] +for i,v in enumerate(lut_vals): + row.append(str(int(v))) + if (i+1)%16==0: + lines.append(" " + ", ".join(row) + ",") + row=[] +if row: + lines.append(" " + ", ".join(row) + ",") + +lines.append("};") +lines.append("") +lines.append("#endif // LOOKUPTABLE_H") + +with open("lookuptable.h","w") as f: + f.write("\n".join(lines)) + +files.download("lookuptable.h") +print("Generado lookuptable.h (mV -> %) ✅") \ No newline at end of file diff --git a/Parcial 1/Homework3/CMakeLists.txt b/Parcial 1/Homework3/CMakeLists.txt new file mode 100644 index 0000000..381fdbe --- /dev/null +++ b/Parcial 1/Homework3/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register( + SRCS "oneshot_read_main.c" + INCLUDE_DIRS "." + PRIV_REQUIRES esp_timer esp_adc +) + diff --git a/Parcial 1/Homework3/Generador de Polinomio.py b/Parcial 1/Homework3/Generador de Polinomio.py new file mode 100644 index 0000000..836aa32 --- /dev/null +++ b/Parcial 1/Homework3/Generador de Polinomio.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +"""Untitled2.ipynb + +Automatically generated by Colab. + +Original file is located at + https://colab.research.google.com/drive/19jSibHAWRwWB1Jo3CqqgVLrz-AeG2Fxe +""" + +!pip -q install openpyxl ipywidgets + +import io, numpy as np, pandas as pd, matplotlib.pyplot as plt +import ipywidgets as widgets +from IPython.display import display, clear_output +from google.colab import files +from google.colab import output as colab_output +colab_output.enable_custom_widget_manager() + +# ---------- Subir archivo ---------- +print("Sube tu archivo Excel (.xlsx) 👇") +uploaded = files.upload() +fname = next(iter(uploaded)) + +# ---------- Utilidades ---------- +def guess_col(cols, df, keywords): + for c in cols: + lc = str(c).lower() + if any(k in lc for k in keywords): + return c + for c in cols: # fallback: primera numérica + if pd.api.types.is_numeric_dtype(df[c]): return c + return cols[0] + +def format_poly_equation(coef): + # coef en orden descendente: a_n, a_{n-1}, ..., a0 + n = len(coef) - 1 + terms = [] + for i, a in enumerate(coef): + p = n - i + if abs(a) < 1e-12: + continue + a_str = f"{a:.6g}" + if p == 0: + terms.append(f"{a_str}") + elif p == 1: + terms.append(f"{a_str}*x") + else: + terms.append(f"{a_str}*x^{p}") + eq = " + ".join(terms) + eq = eq.replace("+ -", "- ") + return f"f(x) = {eq}" if eq else "f(x) = 0" + +def r2_score(y_true, y_pred): + ss_res = np.sum((y_true - y_pred)**2) + ss_tot = np.sum((y_true - np.mean(y_true))**2) + return 1 - ss_res/ss_tot if ss_tot > 0 else np.nan + +# ---------- UI: elegir hoja ---------- +xls = pd.ExcelFile(io.BytesIO(uploaded[fname])) +sheet_dd = widgets.Dropdown(options=xls.sheet_names, value=xls.sheet_names[0], description='Hoja:') +load_btn = widgets.Button(description='Cargar hoja', button_style='primary') +box_out = widgets.Output() + +def load_sheet(_): + with box_out: + clear_output() + df = pd.read_excel(io.BytesIO(uploaded[fname]), sheet_name=sheet_dd.value) + cols = list(df.columns) + + x_dd = widgets.Dropdown(options=cols, value=guess_col(cols, df, ['agua','water','%']), description='X (% agua):') + y_dd = widgets.Dropdown(options=cols, value=guess_col(cols, df, ['volt','voltage','tensión','tension']), description='Y (Voltaje):') + + model_dd = widgets.Dropdown(options=['Lineal','Polinomial','Exponencial'], value='Polinomial', description='Modelo:') + deg = widgets.IntSlider(value=2, min=1, max=5, step=1, description='Grado (poly)') + fit_btn = widgets.Button(description='Graficar y ajustar', button_style='success') + out_area = widgets.Output() + + def do_fit(_): + with out_area: + clear_output() + + # --- preparar datos --- + x = pd.to_numeric(df[x_dd.value], errors='coerce') + y = pd.to_numeric(df[y_dd.value], errors='coerce') + data = pd.DataFrame({'x': x, 'y': y}).dropna().sort_values('x') + x = data['x'].values.astype(float) + y = data['y'].values.astype(float) + + if len(x) < 2: + print("Necesitas al menos 2 puntos.") + return + + # --- ajuste según modelo --- + model = model_dd.value + eq_text, coef, yhat = "", None, None + + if model == 'Lineal': + coef = np.polyfit(x, y, 1) # [m, b] + yhat = np.polyval(coef, x) + eq_text = format_poly_equation(coef) + + elif model == 'Polinomial': + d = int(deg.value) + d = min(d, len(x)-1) # evitar sobreajuste sin puntos suficientes + coef = np.polyfit(x, y, d) # [a_d, ..., a0] + yhat = np.polyval(coef, x) + eq_text = format_poly_equation(coef) + + else: # Exponencial: y = a * exp(b x) => ln y = ln a + b x (requiere y>0) + pos = y > 0 + if pos.sum() < 2: + print("Para exponencial se requieren valores Y positivos.") + return + cx, cy = x[pos], np.log(y[pos]) + b, ln_a = np.polyfit(cx, cy, 1) # cy ≈ ln_a + b*cx + a = np.exp(ln_a) + yhat = a * np.exp(b*x) + coef = (a, b) # guardar como (a, b) + eq_text = f"f(x) = {a:.6g} * exp({b:.6g}*x)" + + # --- métricas --- + r2 = r2_score(y, yhat) + + # --- gráfica --- + plt.figure(figsize=(7,4.5)) + plt.scatter(x, y, label='Medido') + xf = np.linspace(x.min(), x.max(), 200) + if model == 'Exponencial': + yf = coef[0] * np.exp(coef[1] * xf) + else: + yf = np.polyval(coef, xf) + plt.plot(xf, yf, label='Ajuste') + plt.xlabel('Porcentaje de agua (%)') + plt.ylabel('Voltaje (V)') + plt.title('Calibración y función f(x)') + plt.grid(True) + plt.legend() + plt.show() + + # --- resultados --- + print("Ecuación:") + print(eq_text) + print(f"R² = {r2:.6f}") + + # código de la función en Python listo para copiar + if model == 'Exponencial': + a, b = coef + print("\nFunción f(x) en Python:") + print(f"def f(x):\n return {a:.12g} * np.exp({b:.12g} * x)") + else: + # coef es [a_n, ..., a0] + terms = [] + n = len(coef)-1 + for i, a in enumerate(coef): + p = n - i + if p == 0: + terms.append(f"{a:.12g}") + elif p == 1: + terms.append(f"{a:.12g}*x") + else: + terms.append(f"{a:.12g}*x**{p}") + body = " + ".join(terms).replace("+ -", "- ") + print("\nFunción f(x) en Python:") + print("def f(x):") + print(f" return {body}") + + fit_btn.on_click(do_fit) + display(widgets.VBox([ + widgets.HBox([x_dd, y_dd]), + widgets.HBox([model_dd, deg, fit_btn]), + out_area + ])) + +display(widgets.VBox([widgets.HBox([sheet_dd, load_btn]), box_out])) +load_btn.on_click(load_sheet) \ No newline at end of file diff --git a/Parcial 1/Homework3/HW3 Report.pdf b/Parcial 1/Homework3/HW3 Report.pdf new file mode 100644 index 0000000..22cc230 Binary files /dev/null and b/Parcial 1/Homework3/HW3 Report.pdf differ diff --git a/Parcial 1/Homework3/Readme.md b/Parcial 1/Homework3/Readme.md new file mode 100644 index 0000000..698a534 --- /dev/null +++ b/Parcial 1/Homework3/Readme.md @@ -0,0 +1 @@ +Homework 3 Juan Pablo Larios Franco 0244215 diff --git a/Parcial 1/Homework3/TablaVoltajes.xlsx b/Parcial 1/Homework3/TablaVoltajes.xlsx new file mode 100644 index 0000000..1f89106 Binary files /dev/null and b/Parcial 1/Homework3/TablaVoltajes.xlsx differ diff --git a/Parcial 1/Homework3/lookuptable.h b/Parcial 1/Homework3/lookuptable.h new file mode 100644 index 0000000..590883e --- /dev/null +++ b/Parcial 1/Homework3/lookuptable.h @@ -0,0 +1,219 @@ +#ifndef LOOKUPTABLE_H +#define LOOKUPTABLE_H + +#include +#define LUT_SIZE 3301 +#define LUT_SCALE 1 // 1:% entero; 10: décimas de % +#define LUT_INDEX_IS_MV 1 // índice = mV (0..3300) + +static const uint8_t lookup_table[LUT_SIZE] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 76, 77, + 77, 77, 77, 77, 77, 78, 93, 94, 94, 94, 94, 94, 94, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, +}; + +#endif // LOOKUPTABLE_H \ No newline at end of file diff --git a/Parcial 1/Homework3/oneshot_read_main.c b/Parcial 1/Homework3/oneshot_read_main.c new file mode 100644 index 0000000..3e2f211 --- /dev/null +++ b/Parcial 1/Homework3/oneshot_read_main.c @@ -0,0 +1,138 @@ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" + +#include "lookuptable.h" // lookup_table[], LUT_SIZE, LUT_SCALE, LUT_INDEX_IS_MV + +#define MY_ADC_UNIT ADC_UNIT_1 +#define MY_ADC_CHANNEL ADC_CHANNEL_5 // ADC1_CH5 = GPIO33 +#define MY_ADC_ATTEN ADC_ATTEN_DB_12 // 11 dB está deprecado; usar DB_12 (0–~3.3 V) +#define MY_ADC_BITWIDTH ADC_BITWIDTH_DEFAULT + +// ---------- Calibración ---------- +static bool adc_cali_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out) +{ + *out = NULL; +#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + adc_cali_curve_fitting_config_t cfg = { .unit_id=unit, .atten=atten, .bitwidth=MY_ADC_BITWIDTH }; + if (adc_cali_create_scheme_curve_fitting(&cfg, out) == ESP_OK) return true; +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + adc_cali_line_fitting_config_t cfg = { .unit_id=unit, .atten=atten, .bitwidth=MY_ADC_BITWIDTH }; + if (adc_cali_create_scheme_line_fitting(&cfg, out) == ESP_OK) return true; +#endif + return false; +} + +static void adc_cali_deinit(adc_cali_handle_t h) +{ +#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + if (h) adc_cali_delete_scheme_curve_fitting(h); +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + if (h) adc_cali_delete_scheme_line_fitting(h); +#endif +} + +// ---------- Tu polinomio: V = f(%), para el benchmark “poly” ---------- +static inline double voltage_from_percent(double p) +{ + // f(x) = 4.60897e-09*x^5 - 1.13065e-06*x^4 + 9.13377e-05*x^3 + // - 0.00277445*x^2 + 0.0639597*x + 0.154238 + double x = p; + return (((((4.60897e-09*x - 1.13065e-06)*x + 9.13377e-05)*x + - 2.77445e-03)*x + 6.39597e-02)*x + 1.54238e-01); +} + +// Invertir polinomio por bisección: dado mV -> % +static inline double percent_from_mV_poly(int mv) +{ + if (mv < 0) return 0.0; + double targetV = mv / 1000.0; // mV -> V + double lo = 0.0, hi = 100.0; + for (int i = 0; i < 40; ++i) { + double mid = 0.5*(lo + hi); + double Vm = voltage_from_percent(mid); + if (Vm < targetV) { + lo = mid; + } else { + hi = mid; + } + } + double p = 0.5*(lo + hi); + if (p < 0.0) { + p = 0.0; + } else if (p > 100.0) { + p = 100.0; + } + return p; +} + +// ---------- % por Lookup Table (mV -> %) ---------- +static inline double percent_from_lut(int raw, int mv) +{ +#if defined(LUT_INDEX_IS_MV) && (LUT_INDEX_IS_MV == 1) + int idx = mv; // índice = mV (0..3300) +#else + int idx = raw; // índice = RAW (0..4095) +#endif + if (idx < 0) idx = 0; + if (idx >= LUT_SIZE) idx = LUT_SIZE - 1; + +#if (LUT_SCALE == 1) + return (double)lookup_table[idx]; // % entero +#else + return lookup_table[idx] / 10.0; // décimas de % +#endif +} + +void app_main(void) +{ + // Unidad ADC + adc_oneshot_unit_handle_t adc; + adc_oneshot_unit_init_cfg_t ucfg = { .unit_id = MY_ADC_UNIT }; + ESP_ERROR_CHECK(adc_oneshot_new_unit(&ucfg, &adc)); + + // Canal + adc_oneshot_chan_cfg_t ccfg = { .bitwidth = MY_ADC_BITWIDTH, .atten = MY_ADC_ATTEN }; + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc, MY_ADC_CHANNEL, &ccfg)); + + // Calibración -> mV + adc_cali_handle_t cali = NULL; + bool do_cali = adc_cali_init(MY_ADC_UNIT, MY_ADC_ATTEN, &cali); + + while (1) { + int raw = 0, mv = -1; + ESP_ERROR_CHECK(adc_oneshot_read(adc, MY_ADC_CHANNEL, &raw)); +#if defined(LUT_INDEX_IS_MV) && (LUT_INDEX_IS_MV == 1) + if (do_cali) { + if (adc_cali_raw_to_voltage(cali, raw, &mv) != ESP_OK) mv = -1; + } else { + mv = (int)((raw / 4095.0) * 3300.0 + 0.5); + } +#endif + + // ---- Benchmark: polinomio ---- + int64_t t0 = esp_timer_get_time(); + double pct_poly = percent_from_mV_poly(mv); + int64_t t1 = esp_timer_get_time(); + + // ---- Benchmark: LUT ---- + int64_t t2 = esp_timer_get_time(); + double pct_lut = percent_from_lut(raw, mv); + int64_t t3 = esp_timer_get_time(); + + // Resultado + tiempos (µs) + printf("raw=%4d mv=%4d | poly=%.1f%% (%lld us) | LUT=%.1f%% (%lld us)\n", + raw, mv, pct_poly, (long long)(t1 - t0), pct_lut, (long long)(t3 - t2)); + + vTaskDelay(pdMS_TO_TICKS(200)); + } + + adc_cali_deinit(cali); + ESP_ERROR_CHECK(adc_oneshot_del_unit(adc)); +} + diff --git a/Parcial_2/HW6/20251028_153752.mp4 b/Parcial_2/HW6/20251028_153752.mp4 new file mode 100644 index 0000000..3bce001 Binary files /dev/null and b/Parcial_2/HW6/20251028_153752.mp4 differ diff --git a/Parcial_2/HW6/Readme.md b/Parcial_2/HW6/Readme.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Parcial_2/HW6/Readme.md @@ -0,0 +1 @@ + diff --git a/Parcial_2/HW6/TAP1T6.png b/Parcial_2/HW6/TAP1T6.png new file mode 100644 index 0000000..f5eba58 Binary files /dev/null and b/Parcial_2/HW6/TAP1T6.png differ diff --git a/Parcial_2/HW6/TAP2T6.png b/Parcial_2/HW6/TAP2T6.png new file mode 100644 index 0000000..f7fad76 Binary files /dev/null and b/Parcial_2/HW6/TAP2T6.png differ diff --git a/Parcial_2/HW6/TAP3T6.png b/Parcial_2/HW6/TAP3T6.png new file mode 100644 index 0000000..1929857 Binary files /dev/null and b/Parcial_2/HW6/TAP3T6.png differ diff --git a/Parcial_2/HW6/TBP1T6.png b/Parcial_2/HW6/TBP1T6.png new file mode 100644 index 0000000..e2639f5 Binary files /dev/null and b/Parcial_2/HW6/TBP1T6.png differ diff --git a/Parcial_2/HW6/TBP2T6.png b/Parcial_2/HW6/TBP2T6.png new file mode 100644 index 0000000..c95f140 Binary files /dev/null and b/Parcial_2/HW6/TBP2T6.png differ diff --git a/Parcial_2/HW6/TBP3T6.png b/Parcial_2/HW6/TBP3T6.png new file mode 100644 index 0000000..89ec4a2 Binary files /dev/null and b/Parcial_2/HW6/TBP3T6.png differ diff --git a/Parcial_2/HW6/TCP2T6.png b/Parcial_2/HW6/TCP2T6.png new file mode 100644 index 0000000..467948e Binary files /dev/null and b/Parcial_2/HW6/TCP2T6.png differ diff --git a/Parcial_2/HW6/TCP3T6.png b/Parcial_2/HW6/TCP3T6.png new file mode 100644 index 0000000..82b2b90 Binary files /dev/null and b/Parcial_2/HW6/TCP3T6.png differ diff --git a/Parcial_2/HW6/TPP4T6.png b/Parcial_2/HW6/TPP4T6.png new file mode 100644 index 0000000..57e1c1b Binary files /dev/null and b/Parcial_2/HW6/TPP4T6.png differ diff --git a/Parcial_2/HW6/adafruit.c b/Parcial_2/HW6/adafruit.c new file mode 100644 index 0000000..bbecec2 --- /dev/null +++ b/Parcial_2/HW6/adafruit.c @@ -0,0 +1,132 @@ +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_netif.h" +#include "mqtt_client.h" +#include "esp_system.h" + +#define WIFI_SSID "casa" // <-- tu SSID +#define WIFI_PASS "8857640388" // <-- tu password + +// -------- Adafruit IO ---------- +#define AIO_USERNAME "Jplarios12" +#define AIO_KEY "aio_tNgy15olNnTcs36nUzX1UfT8FIAZ" +#define AIO_FEED "temp" + +// SIN TLS (puerto 1883): +#define AIO_URI "mqtt://io.adafruit.com:1883" +// Con TLS sería: "mqtts://io.adafruit.com:8883" + NTP + certificado raíz + +static const char *TAG = "MQTT_APP"; +static esp_mqtt_client_handle_t g_client = NULL; +static bool mqtt_started = false; + +// ---------- Publisher ---------- +static void publisher_task(void *arg) { + char topic[128]; + char payload[64]; + snprintf(topic, sizeof(topic), "%s/feeds/%s", AIO_USERNAME, AIO_FEED); + + float temp = 24.0f; + while (1) { + temp += 0.1f; + if (temp > 30.0f) temp = 24.0f; + snprintf(payload, sizeof(payload), "%.2f", temp); + int msg_id = esp_mqtt_client_publish(g_client, topic, payload, 0, 1, 0); + ESP_LOGI(TAG, "PUB -> topic='%s' payload='%s' msg_id=%d", topic, payload, msg_id); + vTaskDelay(pdMS_TO_TICKS(5000)); + } +} + +// ---------- MQTT events ---------- +static void mqtt_event_handler(void *handler_args, + esp_event_base_t base, + int32_t event_id, + void *event_data) { + esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data; + switch ((esp_mqtt_event_id_t)event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT connected"); + xTaskCreatePinnedToCore(publisher_task, "publisher_task", 4096, NULL, 5, NULL, tskNO_AFFINITY); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGW(TAG, "MQTT disconnected"); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT published msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_ERROR: + ESP_LOGE(TAG, "MQTT event error"); + break; + default: + break; + } +} + +static void mqtt_start(void) { + if (mqtt_started) return; + esp_mqtt_client_config_t cfg = { + .broker.address.uri = AIO_URI, + .credentials.username = AIO_USERNAME, + .credentials.authentication.password = AIO_KEY, + .session.protocol_ver = MQTT_PROTOCOL_V_3_1_1, + .session.keepalive = 30, + .network.timeout_ms = 30000, + }; + g_client = esp_mqtt_client_init(&cfg); + ESP_ERROR_CHECK(g_client ? ESP_OK : ESP_FAIL); + ESP_ERROR_CHECK(esp_mqtt_client_register_event(g_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL)); + ESP_ERROR_CHECK(esp_mqtt_client_start(g_client)); + mqtt_started = true; + ESP_LOGI(TAG, "MQTT client started (URI=%s)", AIO_URI); +} + +// ---------- Wi-Fi ---------- +static void on_got_ip(void *arg, esp_event_base_t base, int32_t id, void *data) { + ip_event_got_ip_t *e = (ip_event_got_ip_t *)data; + ESP_LOGI(TAG, "STA got IP: " IPSTR " / gw " IPSTR, + IP2STR(&e->ip_info.ip), IP2STR(&e->ip_info.gw)); + // arrancar MQTT sólo cuando ya tenemos IP/DNS + mqtt_start(); +} + +static void wifi_init_sta(void) { + ESP_LOGI(TAG, "Init NVS/WiFi STA"); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_t *netif = esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + wifi_config_t wifi_cfg = {0}; + strncpy((char*)wifi_cfg.sta.ssid, WIFI_SSID, sizeof(wifi_cfg.sta.ssid) - 1); + strncpy((char*)wifi_cfg.sta.password, WIFI_PASS, sizeof(wifi_cfg.sta.password) - 1); + wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + + // handler: cuando obtengamos IP, arrancamos MQTT + ESP_ERROR_CHECK(esp_event_handler_instance_register( + IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL, NULL)); + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); + (void)netif; // evitar warning si no se usa netif directamente +} + +// ---------- app_main ---------- +void app_main(void) { + ESP_ERROR_CHECK(nvs_flash_init()); + wifi_init_sta(); + // Nota: NO arrancamos MQTT aquí; se hace en on_got_ip() +} + diff --git a/Parcial_2/HW6/test1.py b/Parcial_2/HW6/test1.py new file mode 100644 index 0000000..dcefff8 --- /dev/null +++ b/Parcial_2/HW6/test1.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +import argparse, csv, os, sys +from datetime import datetime +import paho.mqtt.client as mqtt +from paho.mqtt.properties import Properties +from paho.mqtt.packettypes import PacketTypes + +def now_iso(): + from datetime import datetime + return datetime.now().isoformat(timespec="seconds") + +def ensure_csv(path): + new = not os.path.exists(path) + f = open(path, "a", newline="", encoding="utf-8") + w = csv.writer(f) + if new: + w.writerow(["timestamp","topic","payload","qos","retain","mid","user_properties"]) + return f, w + +def on_connect(client, userdata, flags, reason_code, properties): + print(f"[{now_iso()}] CONNECTED rc={reason_code}") + topic = f"{userdata['team']}/#" + client.subscribe(topic, qos=1) + print(f"[{now_iso()}] SUBSCRIBED to {topic}") + +def on_message(client, userdata, msg): + props = getattr(msg, "properties", None) + user_props = [] + if props and getattr(props, "UserProperty", None): + user_props = [f"{k}={v}" for (k,v) in props.UserProperty] + row = [ + now_iso(), + msg.topic, + msg.payload.decode("utf-8", errors="replace"), + msg.qos, + int(msg.retain), + msg.mid, + ";".join(user_props) + ] + userdata["writer"].writerow(row) + userdata["file"].flush() + print(f"[{now_iso()}] RX {msg.topic}: {row[2]} (qos={msg.qos}, retain={int(msg.retain)})") + +def main(): + ap = argparse.ArgumentParser(description="MQTT v5 CSV logger") + ap.add_argument("--host", default="127.0.0.1") + ap.add_argument("--port", type=int, default=1883) + ap.add_argument("--team", default="u01", help="Prefijo de tópicos, ej. u01") + ap.add_argument("--csv", default="mqtt_log.csv") + args = ap.parse_args() + + f, w = ensure_csv(args.csv) + userdata = {"team": args.team, "file": f, "writer": w} + + client = mqtt.Client(client_id=f"logger-{args.team}", userdata=userdata, protocol=mqtt.MQTTv5) + client.on_connect = on_connect + client.on_message = on_message + + props = Properties(PacketTypes.CONNECT) # opcional: props v5 + client.connect(args.host, args.port, keepalive=60, properties=props) + + print(f"[{now_iso()}] CONNECTING to {args.host}:{args.port} … (team={args.team})") + try: + client.loop_forever() + except KeyboardInterrupt: + print("\nbye.") + finally: + f.close() + +if __name__ == "__main__": + main() + diff --git a/Parcial_2/HW6/test2.py b/Parcial_2/HW6/test2.py new file mode 100644 index 0000000..e991b84 --- /dev/null +++ b/Parcial_2/HW6/test2.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +import argparse, csv, os, sys, getpass +from datetime import datetime +import paho.mqtt.client as mqtt +from paho.mqtt.properties import Properties +from paho.mqtt.packettypes import PacketTypes +import paramiko + +def now_iso(): + return datetime.now().isoformat(timespec="seconds") + +def ensure_csv(path): + new = not os.path.exists(path) + f = open(path, "a", newline="", encoding="utf-8") + w = csv.writer(f) + if new: + w.writerow(["timestamp","topic","payload","qos","retain","mid","user_properties"]) + return f, w + +def ssh_check(host, user, password, cmd="systemctl is-active mosquitto"): + print(f"[{now_iso()}] [SSH] Checking Mosquitto on {host} …") + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(host, username=user, password=password, timeout=10) + _, stdout, _ = ssh.exec_command(cmd, timeout=10) + out = stdout.read().decode().strip() + ssh.close() + print(f"[{now_iso()}] [SSH] Service state: {out}") + return out + +def on_connect(client, userdata, flags, reason_code, properties): + print(f"[{now_iso()}] CONNECTED rc={reason_code}") + topic = f"{userdata['team']}/#" + client.subscribe(topic, qos=1) + print(f"[{now_iso()}] SUBSCRIBED to {topic}") + +def on_message(client, userdata, msg): + props = getattr(msg, "properties", None) + user_props = [] + if props and getattr(props, "UserProperty", None): + user_props = [f"{k}={v}" for (k,v) in props.UserProperty] + row = [ + now_iso(), + msg.topic, + msg.payload.decode("utf-8", errors="replace"), + msg.qos, + int(msg.retain), + msg.mid, + ";".join(user_props) + ] + userdata["writer"].writerow(row) + userdata["file"].flush() + print(f"[{now_iso()}] RX {msg.topic}: {row[2]} (qos={msg.qos}, retain={int(msg.retain)})") + +def main(): + ap = argparse.ArgumentParser(description="MQTT v5 CSV logger + SSH check") + ap.add_argument("--host", default="127.0.0.1") + ap.add_argument("--port", type=int, default=1883) + ap.add_argument("--team", default="u01") + ap.add_argument("--csv", default="mqtt_log.csv") + ap.add_argument("--ssh-host", default="127.0.0.1") + ap.add_argument("--ssh-user", default=getpass.getuser()) + ap.add_argument("--ssh-pass", default=None) + args = ap.parse_args() + + if args.ssh_pass is None: + args.ssh_pass = getpass.getpass(prompt=f"Password for {args.ssh_user}@{args.ssh_host}: ") + + # SSH check before connecting MQTT (as PDF requests) + ssh_check(args.ssh_host, args.ssh_user, args.ssh_pass, "systemctl is-active mosquitto") + + f, w = ensure_csv(args.csv) + userdata = {"team": args.team, "file": f, "writer": w} + + client = mqtt.Client(client_id=f"logger-{args.team}", userdata=userdata, protocol=mqtt.MQTTv5) + client.on_connect = on_connect + client.on_message = on_message + + props = Properties(PacketTypes.CONNECT) # keep v5 props ready if needed + client.connect(args.host, args.port, keepalive=60, properties=props) + + print(f"[{now_iso()}] CONNECTING to {args.host}:{args.port} … (team={args.team})") + try: + client.loop_forever() + except KeyboardInterrupt: + print("\nbye.") + finally: + f.close() + +if __name__ == "__main__": + main() + diff --git a/Parcial_2/HWK5/Server.c b/Parcial_2/HWK5/Server.c new file mode 100644 index 0000000..7122e43 --- /dev/null +++ b/Parcial_2/HWK5/Server.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +static void caesar(char *s, int k, int decrypt){ + if(!s) return; + k = ((k%26)+26)%26; + if(decrypt) k = 26-k; + for(char *p=s; *p; ++p){ + char c=*p; + if(c>='a'&&c<='z') *p = 'a'+((c-'a'+k)%26); + else if(c>='A'&&c<='Z') *p = 'A'+((c-'A'+k)%26); + } +} + +int main(int argc, char **argv){ + if(argc<3){ fprintf(stderr,"Usage: %s \n", argv[0]); return 1; } + int port = atoi(argv[1]); + int shift = atoi(argv[2]); + + int s = socket(AF_INET, SOCK_STREAM, 0); + if(s<0){ perror("socket"); return 1; } + + int opt=1; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); + struct sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + + + if(bind(s,(struct sockaddr*)&addr,sizeof(addr))<0){ perror("bind"); return 1; } + if(listen(s,8)<0){ perror("listen"); return 1; } + printf("Server listening on port %d (shift=%d)\n", port, shift); + + for(;;){ + int c = accept(s,NULL,NULL); + if(c<0){ perror("accept"); continue; } + char buf[2048]; ssize_t n; + while((n=recv(c,buf,sizeof(buf)-1,0))>0){ + buf[n]='\0'; + caesar(buf, shift, 0); // ENCRYPT on server + send(c, buf, strlen(buf), 0); + } + close(c); + } +} + + diff --git a/Parcial_2/HWK5/main.c b/Parcial_2/HWK5/main.c new file mode 100644 index 0000000..df781ac --- /dev/null +++ b/Parcial_2/HWK5/main.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#define WIFI_SSID "juan pablo's Galaxy A52" +#define WIFI_PASS "tuxl5077" +#define SERVER_IP "192.168.51.1" +#define SERVER_PORT 5000 +#define CAESAR_SHIFT 3 // debe coincidir con el del servidor + +static const char *TAG = "ESP_CLIENT"; + +static void caesar(char *s, int k, int decrypt) { + if (!s) return; + k = ((k % 26) + 26) % 26; + if (decrypt) k = 26 - k; + for (char *p = s; *p; ++p) { + char c = *p; + if (c >= 'a' && c <= 'z') *p = 'a' + ((c - 'a' + k) % 26); + else if (c >= 'A' && c <= 'Z') *p = 'A' + ((c - 'A' + k) % 26); + } +} + +static void wifi_init(void) { + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wc = {0}; + strcpy((char*)wc.sta.ssid, WIFI_SSID); + strcpy((char*)wc.sta.password, WIFI_PASS); + wc.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wc)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); + ESP_LOGI(TAG, "Connecting WiFi to SSID=%s ...", WIFI_SSID); +} + +void app_main(void) { + ESP_ERROR_CHECK(nvs_flash_init()); + wifi_init(); + + // Espera IP por DHCP + vTaskDelay(pdMS_TO_TICKS(3000)); + + while (1) { + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (s < 0) { ESP_LOGE(TAG, "socket fail"); vTaskDelay(pdMS_TO_TICKS(2000)); continue; } + + struct sockaddr_in dest = {0}; + dest.sin_family = AF_INET; + dest.sin_port = htons(SERVER_PORT); + inet_pton(AF_INET, SERVER_IP, &dest.sin_addr); + ESP_LOGI(TAG, "Connecting to %s:%d ...", SERVER_IP, SERVER_PORT); + if (connect(s, (struct sockaddr*)&dest, sizeof(dest)) < 0) { + ESP_LOGE(TAG, "connect fail"); + close(s); + vTaskDelay(pdMS_TO_TICKS(2000)); + continue; + } + + const char *msg = "Hello from ESP32!\n"; + send(s, msg, strlen(msg), 0); + + char rx[1024]; + int n = recv(s, rx, sizeof(rx)-1, 0); + if (n > 0) { + rx[n] = '\0'; + ESP_LOGI(TAG, "Encrypted from server: %s", rx); + caesar(rx, CAESAR_SHIFT, /*decrypt=*/1); + ESP_LOGI(TAG, "Decrypted on ESP32: %s", rx); + } else { + ESP_LOGW(TAG, "No data"); + } + close(s); + vTaskDelay(pdMS_TO_TICKS(2000)); + } +} diff --git a/Parcial_2/HWK5/tcp_tester.py b/Parcial_2/HWK5/tcp_tester.py new file mode 100644 index 0000000..a9b4ac4 --- /dev/null +++ b/Parcial_2/HWK5/tcp_tester.py @@ -0,0 +1,11 @@ +import socket, sys + +host = "192.168.51.1" # <-- tu IP del servidor +port = 5000 +msg = "Hola soy Zhivago" + +with socket.create_connection((host, port)) as s: + s.sendall(msg.encode()) + print("Message sent:", msg) + data = s.recv(1024) + print("Received:", data.decode())