From c64e5954bae681bc14af4dd92b8f80d081ab971a Mon Sep 17 00:00:00 2001 From: Michael Clemens // DK1MI Date: Mon, 8 May 2023 22:49:04 +0000 Subject: [PATCH] =?UTF-8?q?=E2=80=9Ehl2-interface.ino=E2=80=9C=20=C3=A4nde?= =?UTF-8?q?rn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hl2-interface.ino | 734 ++++++++++++++++++++++------------------------ 1 file changed, 355 insertions(+), 379 deletions(-) diff --git a/hl2-interface.ino b/hl2-interface.ino index 905020e..acb5c44 100644 --- a/hl2-interface.ino +++ b/hl2-interface.ino @@ -1,412 +1,388 @@ -/**************************************************************************************************************************** - Remote PA Monitor - solution to remotely monitor RF power, SWR and more of QO-100 power amplifiers +//*************************************************************************************** +//* PA70 Controller - Band, and Fan Control by Temperature for Arduino Nano * +//* To be used together with Hermes Lite v2 (http://www.hermeslite.com/) * +//* by Cesc Gudayol (EA3IGT) * +//* Version 3.0.1 01/04/2021 * +//* * +//* More info at: https://github.com/ea3igt/HL2-PA70 * +//* * +//*************************************************************************************** +//* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND * +//*************************************************************************************** +//* v2.0 * 24/10/2020 * Initial version * +//* v2.1 * 27/10/2020 * Convert sendOled to a function to refresh the OLED display * +//* V2.2 * 25/11/2020 * Store last band selected in EPROM * +//* * 12/12/2020 * Get 6 least significative I2C bus bits to select the band * +//* V2.3 * 14/12/2020 * Use EXTTR to control PTT status * +//* V3.0 * 20/02/2021 * First version to test new Control Board v2.1 * +//* V3.0.1 * 01/04/2021 * Correct OLED refresh problems * +//*************************************************************************************** +// +// Pin Configuration: +// - PIN A00 OUTPUT: SCL for I2C Master (OLED display) +// - PIN A01 OUTPUT: SDA for I2C Master (OLED display) +// - PIN A04 INPUT: SDA for I2C Slave (Hermes Lite 2 Control bus) +// - PIN A05 INPUT: SCL for I2C Slave (Hermes Lite 2 Control bus) +// - PIN A03 INPUT: NTC divider voltage +// - PIN D02 INPUT: EXTTR (PTT) control from Hermes Lite 2 +// - PIN D03 OUTPUT: LPF Band 1 Control (80m) +// - PIN D04 OUTPUT: LPF Band 2 Control (40/60m) +// - PIN D05 OUTPUT: LPF Band 3 Control (30/20m) +// - PIN D06 OUTPUT: LPF Band 4 Control (17/15/12/10m) +// - PIN D07 LPF Spare +// - PIN D08 LPF Spare +// - PIN D09 LPF Spare +// - PIN D10 LPF Spare -> Antenna Switch +// - PIN D11 OUTPUT: PWM for Fan control f(Temp) +// - PIN D12 OUTPUT: PTT Control to the Power Amp - For Ethernet shields using WT32_ETH01 (ESP32 + LAN8720) - Uses WebServer_WT32_ETH01, a library for the Ethernet LAN8720 in WT32_ETH01 to run WebServer +#include //For I2C control +#include //For SSD1306 Chip Control (OLED) +#include //To store values from PA70-Controller +#include //Library to delay inside one Interrupt - Michael Clemens, DK1MI - Licensed under MIT license +//#define TEST //Uncomment if Testing (Serial Monitor traces) - *****************************************************************************************************************************/ +// Declaration for an SH1106 display connected to I2C +// U8G2_RX: Rotation (R0 = 0º | R1 = 90º | R2 = 180º | R3 = 270º | MIRROR) +// SW SCL pin A0 +// SW SDA pin A1 +// Use U8X8_PIN_NONE if the reset pin is not connected (All Boards without Reset of the Display) +U8G2_SH1106_128X64_NONAME_2_SW_I2C u8g2(U8G2_R0, A0, A1, U8X8_PIN_NONE); -#define DEBUG_ETHERNET_WEBSERVER_PORT Serial +// Declaration for the Hermes Lites v2 I2C (Listen to Adress 0x20) +// HW SDA pin A4 +// HW SCL pin A5 -// Debug Level from 0 to 4 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 3 +// Thermometer definitions +int ThermistorPin = 3; //Pin A3 for NTC voltage divider reading +int Vo; //To store thermistor bridge readings +float R1 = 10000; //Adjust to the R value for the divider (About 10K Ohm) +float logR2, R2, T; +float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07; -#include -#include "index.h" // Main Web page header file -#include "config.h" // Config Web page header file -#include +// PWM definitions +const byte PWM_Pin = 11; //PWM Pin -Preferences translation_fwd; -Preferences translation_ref; -Preferences config; +// LPF Output Pins (D3..D10 pins can be used) +#define LPF_band1 3 //Pin D3 +#define LPF_band2 4 //Pin D4 +#define LPF_band3 5 //Pin D5 +#define LPF_band4 6 //Pin D6 -String config_items [ ] = {"show_fwd", "show_ref", "show_swr", "show_mV", "show_dBm", "show_watt"}; -String config_defaults [ ] = {"true", "true", "true", "true", "false", "true"}; +#define Antenna 19 //Pin D10 -int voltage_fwd,voltage_ref; -float fwd_dbm=0, ref_dbm=0; -double fwd_watt=0, ref_watt=0; -byte iii=0; +// PTT Control +#define EXTTR 2 //Pin D2 +#define PTTpin 12 //Pin D12 -String conf_content; -String conf_translate_fwd_table = ""; -String conf_translate_ref_table = ""; -String conf_config_table = ""; -String del_action = ""; +// Other Configurations +float initialTempFanActivation = 27; //Minimum temperature for Fan Activation +float tempFanActivation; //to implement the histeresis cycle +float previousTemp_fanActivation; //to implement the histeresis cycle +bool fanConnected = false; //to store if Fan is activated to show on OLED +String myBand="---"; //to store myBand String +float myTemp; //to store Temperature read from NTC +byte selectedBand; //to store Selected Band +byte previousSelectedBand; //to store previously Selected Band +byte previousI2CBand; //to store previously I2C Band decoded +byte PTTStatus = 0; //to store global PTT Status +bool PTTChanged = false; //to store global PTT Status Changed +int selected_antenna = 0; -String band = "70cm"; -String band_fwd = band + "_fwd"; -String band_ref = band + "_ref"; -String band_list []= {"3cm", "13cm", "70cm", "2m"}; +// SetUp function initializations +void setup() { + #ifdef TEST + Serial.begin(9600); + #endif -int IO2_FWD = 2; -int IO4_REF = 4; + // U8G2 Display initialization + u8g2.begin(); + u8g2.setFont(u8g2_font_helvB12_tr); + u8g2.firstPage(); + do { + u8g2.drawStr(5,25,"PA70 Ctrl v3.0.1"); //Software version + u8g2.drawStr(5,53," EA3IGT"); //Change to whatever you want + } while ( u8g2.nextPage() ); -WebServer server(80); + // I2C Slave Setup + Wire.begin(0x20); // join i2c bus with address to listen + Wire.onReceive(receiveEvent); // register event -// Select the IP address according to your local network -//IPAddress myIP(192, 168, 88, 247); -//IPAddress myGW(192, 168, 88, 1); -//IPAddress mySN(255, 255, 255, 0); + // Fan Setup + tempFanActivation = initialTempFanActivation; + previousTemp_fanActivation = tempFanActivation; -// Google DNS Server IP -//IPAddress myDNS(8, 8, 8, 8); + // LPF Setup + pinMode(LPF_band1, OUTPUT); + pinMode(LPF_band2, OUTPUT); + pinMode(LPF_band3, OUTPUT); + pinMode(LPF_band4, OUTPUT); + // Antenna + pinMode(Antenna, OUTPUT); + + // OLED Setup + pinMode(A0,OUTPUT); + pinMode(A1,OUTPUT); -// converts dBm to Watt -double dbm_to_watt(float dbm) { - return pow( 10.0, (dbm - 30.0) / 10.0); + // PTT Setup + pinMode(PTTpin,OUTPUT); + pinMode(EXTTR,INPUT); + + // PWM Setup + pinMode(PWM_Pin,OUTPUT); + + delay(200); + + //Listen to EXTTR status change + attachInterrupt(digitalPinToInterrupt(EXTTR),changePTT,CHANGE); + + //Get stored Band from EEPROM for initial filter selection + previousSelectedBand=EEPROM.read(0); //Read LPF filter Band from EEPROM byte 0 + previousI2CBand=EEPROM.read(1); //Read I2C byte Band from EEPROM byte 1 + + #ifdef TEST + Serial.print(F("*** Previous I2C Band: ")); + Serial.println(previousI2CBand); + Serial.print(F("*** Previous Selected Band: ")); + Serial.println(previousSelectedBand); + Serial.println(F("Initializing...")); + Serial.println(); + #endif + + decodeBand(previousI2CBand); + setLpfBand(previousSelectedBand); //Select LPF filter + + delay(2000); //Delay to show the Logo & Version + sendOled(); //Refresh OLED } -// takes a voltage value and translates it -// to dBm based on the corresponding lookup table -float millivolt_to_dbm(int mv, bool fwd) +// Main loop program +void loop() { - float lastval = 0; - float nextval = 0; - int lastkey = 0; - int nextkey = 0; - float stored_val =0; - for (int i=0; i<3400; i++) { - if (fwd) { - stored_val = translation_fwd.getFloat(String(i).c_str()); - } else { - stored_val = translation_ref.getFloat(String(i).c_str()); - } - if (!isnan(stored_val)) { - if (i < mv) { - lastval = stored_val; - lastkey = i; - } else { - nextval = stored_val; - nextkey = i; - break; - } + int PWMDuty; + int Times = 100; //Average N times to be more stable + float Temperature = 0; //All thermometer code from: https://bit.ly/3pOUjVo + for(int i = 0; i < Times; i++) { + Vo = analogRead(ThermistorPin); + R2 = R1 * (1024.0 / (float)Vo - 1.0); + logR2 = log(R2); + T = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2)); + T = T - 273.15 + 1; //+1 is my personal adjustment to be more precise + Temperature = Temperature + T; + if (PTTChanged) + { + digitalWrite(PTTpin,PTTStatus); //Toggle PTT + PTTChanged=false; //Reset PTTChanged } } - - float lowerkey = min(lastkey, nextkey); - float lowerval = min(lastval, nextval); - /* - float higherkey = max(lastkey, nextkey); - - float higherval = max(lastval, nextval); - float diffkey = higherkey - lowerkey; - float diffval = higherval - lowerval; - float x = diffval / diffkey; - float y = mv - lowerkey; - float z = x * y; - - float result = lowerval + z; - */ - float diffkey = max(lastkey, nextkey) - min(lastkey, nextkey); - float diffval = max(lastval, nextval) - min(lastval, nextval); - float result = lowerval + ((diffval / diffkey) * (mv - lowerkey)); + myTemp = int(Temperature/Times*10)/(float)10.0; //Rounded to get 1 decimal - //Serial.print("measured voltage: " + String(mv) + " LastVal: " + String(lastval) + " LastKey: " + String(lastkey) + " Nextval: " + String(nextval) + " NextKey:" + String(nextkey) + "\n"); - return result; -} - - -// read voltages from both input pins -// calculates avaerage value of 20 measurements -void read_directional_couplers() -{ - int voltage_sum_fwd = 0; - int voltage_sum_ref = 0; - for(iii=0; iii<20; iii++) // Take 20 samples and save the highest value - { voltage_sum_fwd += analogReadMilliVolts(IO2_FWD); - voltage_sum_ref += analogReadMilliVolts(IO4_REF); - //Serial.println(String(voltage_fwd)); - //if(voltage_fwd > voltage_fwd_peak) voltage_fwd_peak = voltage_fwd; // safe the peak of 10 measurements - //if(voltage_ref > voltage_ref_peak) voltage_ref_peak = voltage_ref; - } - //voltage_fwd = voltage_fwd_peak; // use peak voltage for processing - //voltage_ref = voltage_ref_peak; - - voltage_fwd = voltage_sum_fwd/20; // use peak voltage for processing - voltage_ref = voltage_sum_ref/20; - - fwd_dbm = millivolt_to_dbm(voltage_fwd, true); - ref_dbm = millivolt_to_dbm(voltage_ref, false); - - fwd_watt = dbm_to_watt(fwd_dbm); - ref_watt = dbm_to_watt(ref_dbm); - - Serial.print(String(fwd_watt) + "\n"); - - //voltage_fwd_peak = 0; // set peak voltages back to 0 - //voltage_ref_peak = 0; - -} - -// delivers the dashboard page in "index.h" -void handleRoot() -{ - String s = MAIN_page; - server.send(200, "text/html", s); -} - - -// delivers a 404 page if a non-existant resouurce is requested -void handleNotFound() -{ - String message = F("File Not Found\n\n"); - - message += F("URI: "); - message += server.uri(); - message += F("\nMethod: "); - message += (server.method() == HTTP_GET) ? F("GET") : F("POST"); - message += F("\nArguments: "); - message += server.args(); - message += F("\n"); - - for (uint8_t i = 0; i < server.args(); i++) + PWMDuty = int((myTemp - tempFanActivation)*20); //Calculate PWMDuty (increase to get more aggresive fan) + if (PWMDuty < 0) { - message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; + PWMDuty = 0; + } + if (PWMDuty > 254) + { + PWMDuty = 254; } - server.send(404, F("text/plain"), message); + #ifdef TEST + Serial.print(F("Temperature: ")); + Serial.print(myTemp,1); + Serial.print(F(" - PWMDuty: ")); + Serial.println(PWMDuty); + #endif + + analogWrite(PWM_Pin,PWMDuty); //Select the appropriate PWM f(temperature) + + if (PWMDuty > 0 and not(fanConnected)) //Implement histeresis cycle + { + tempFanActivation = initialTempFanActivation - 1; + } + if (PWMDuty == 0 and fanConnected) + { + tempFanActivation = initialTempFanActivation; + } + + if (PWMDuty > 0) + { + fanConnected=true; + } + else + { + fanConnected=false; + } + + #ifdef TEST + if (fanConnected) + { + Serial.println(F("Fan Connected")); + Serial.print(F("Minimum Temperature Fan Deactivation: ")); + Serial.println(tempFanActivation,1); + } + else + { + Serial.println(F("Fan Disconnected")); + Serial.print(F("Minimum Temperature Fan Activation: ")); + Serial.println(tempFanActivation,1); + } + Serial.print(F("BAND:")); + Serial.println(myBand); + Serial.println(); + #endif + + sendOled(); //Refresh OLED } - -// executes the function to gather sensor data -// delivers gathered data to dashboard page -// invoked periodically by the dashboard page -void handleDATA() { - read_directional_couplers(); - - // calculate SWR - float swr = (1 + sqrt(ref_watt/fwd_watt)) / (1 - sqrt(ref_watt/fwd_watt)); - - String output = String(fwd_watt,3) + ";" + String(fwd_dbm,3) + ";" + String(voltage_fwd) + ";" + String(ref_watt,3) + ";" + String(ref_dbm,3) + ";" + String(voltage_ref) + ";" + String(swr) + ";" + band; - server.send(200, "text/plane", output); -} - -// main function for displaying the configuration page -// invoked by the "configuration" button on the dashboard page -void handleCONFIG() { - if (conf_translate_fwd_table == "") { - build_translate_table(true); - } - if (conf_translate_ref_table == "") { - build_translate_table(false); - } - if (conf_config_table == "") { - build_config_table(); - } - - conf_content = "\r\n"; - conf_content += ""; - conf_content += "

Configuration

"; - conf_content += "

Band Selection

"; - conf_content += "
"; - conf_content += "
"; - conf_content += "

"; - conf_content += "

Translation Detector voltage /mV to RF-Power level /dBm

"; - conf_content += "

FWD

"; - conf_content += conf_translate_fwd_table; - conf_content += "

"; - conf_content += "

REF

"; - conf_content += conf_translate_ref_table; - conf_content += "

"; - conf_content += "

General Configuration Items

"; - conf_content += "

"; - conf_content += conf_config_table; - conf_content += "

"; - conf_content += ""; - server.send(200, "text/html", conf_content); -} - -// generates the translation table for either the FWD or -// REF values -void build_translate_table(bool fwd) { - String tbl = ""; - if (fwd) { - tbl = "
"; - } else { - tbl = ""; - } - tbl += ""; - tbl += ""; - - for (int i=0; i<3400; i++) { - float stored_val = 0; - if (fwd) { - stored_val = translation_fwd.getFloat(String(i).c_str()); - } else { - stored_val = translation_ref.getFloat(String(i).c_str()); - } - if (!isnan(stored_val)) { - tbl += ""; - } - } - tbl += ""; - tbl += "
millivolt (mV)decibel-milliwatts (dBm)WattAction
"; - tbl += String(i); - tbl += ""; - tbl += String(stored_val,3); - tbl += ""; - tbl += String(dbm_to_watt(stored_val),3); - tbl += ""; - tbl += ""; - tbl += "
       
"; - if (fwd) { - conf_translate_fwd_table = tbl; - } else { - conf_translate_ref_table = tbl; - } -} - -// generates the table with generic configuration items -void build_config_table() { - conf_config_table = "
"; - conf_config_table += ""; - conf_config_table += ""; - for (int i=0; i
KeyValueAction