simpleSA/simpleSA_wifi.cpp
M0WID 1746058f31 Fixed TG CS pin define compile error
Corrected problem with CS pins floating if module installed but not initialised.
Also corrected compiler error if neither TG SI4432 defined
2020-10-09 21:14:58 +01:00

1186 lines
36 KiB
C++

/*
* "simpleSA_wifi.cpp"
*
* Trial wifi charting functionality to see if WiFi affects the scan results
* Based on example here https://circuits4you.com/2019/01/11/esp8266-data-logging-with-real-time-graphs/
*
* Requires some files to be placed into the spiffs area of flash
*
* Modified in Version 2.1 by WA2FZW:
*
* Eliminated all calls to "WriteSettings". All of the functions in "Cmd.cpp"
* that actually update the "setting" structure elements now handle that task
* and do so based on whether or not the value of the parameter actually
* changed or not which wasn't the case before.
*/
#include "simpleSA_wifi.h" // WiFi definitions
#include "si4432.h" // Si4432 definitions
#include "cmd.h" // Command processing functions
/*
* Variables to determine size of grid and waterfall
* In Bandscope mode the grid is reduced. In future it may be possible to add
* a waterfall to the main sweep, but not yet
*/
extern uint16_t gridHeight;
extern uint16_t gridWidth;
extern uint16_t yGrid; // no of grid divisions
extern uint16_t yDelta; // no of points/division
extern uint16_t xGrid;
extern uint16_t displayPoints;
extern uint16_t xDelta;
extern uint16_t waterfallHeight;
extern unsigned long sweepMicros; // To report the scan time
extern int bpfCount; // Number of elements in the bandpassFilters array
extern int updateSidebar; // Flag to indicate no of clients has changed
extern void ClearDisplay ();
extern void DisplayError ( uint8_t severity, const char *l1, const char *l2, const char *l3, const char *l4 );
extern void setMode (uint16_t newMode);
/*
* In Version 1.8, the transmitter and receiver Si4432 modules are implemented as
* objects.
*/
extern Si4432 rcvr; // Object for the receiver
extern Si4432 xmit; // And the transmitter
extern bool AGC_On; // Flag indicates if Preamp AGC is enabled
extern uint8_t AGC_Reg; // Fixed value for preampGain if not auto
extern uint32_t startFreq_IF;
extern uint32_t stopFreq_IF;
extern uint32_t sigFreq_IF;
extern uint16_t sigGenOutputOn;
extern uint32_t startFreq_RX;
extern uint32_t stopFreq_RX;
AsyncWebServer server ( 80 ); // Create the webserver object
AsyncResponseStream *response;
/*
* Install WebSockets library by Markus Sattler
* https://github.com/Links2004/arduinoWebSockets
*/
WebSocketsServer webSocket = WebSocketsServer ( 81 );
uint8_t socketNumber;
unsigned long messageNumber;
extern uint8_t numberOfWebsocketClients;
/*
* Tracking of number of Wi-Fi reconnects and total connection time
*/
unsigned long numberOfReconnects;
unsigned long millisConnected;
IPAddress ipAddress; // Store the IP address for use elsewhere, eg info
/*
* Function to format IP address nicely
*/
char *FormatIPAddress ( IPAddress ipAddress )
{
static char formatBuffer[20] = {0};
sprintf( formatBuffer, "%d.%d.%d.%d", ipAddress[0], ipAddress[1],
ipAddress[2], ipAddress[3] );
return formatBuffer;
}
boolean startAP () // Start the WiFi Access Point, keep the user informed.
{
ClearDisplay (); // Fade to black
Serial.println ( "Starting Access Point" ); // Put in the instructions
delay(2000);
// Scan WiFi SSIDs
WiFi.scanNetworks(true);
/*
* Start by kicking off the soft-AP. Call depends on whether or not password
* is required to connect
*/
#ifdef AP_PASSWORD
boolean result = WiFi.softAP ( PROGRAM_NAME, AP_PASSWORD );
#else
boolean result = WiFi.softAP ( PROGRAM_NAME );
#endif
if ( !result ) // This has failed, tell the user
DisplayError ( ERR_WARN, "Failed to open AP:", "WiFi Disabled", NULL, NULL );
else
ipAddress = WiFi.softAPIP();
Serial.printf ( "Access Point started, result = %i \n", result );
return result;
}
boolean connectWiFi() // Connect to Wifi using SSID (ie via Router)
{
Serial.printf ( "Connecting to: %s \n", WIFI_SSID );
// Serial.println( (char *) &configuration.WiFi_SSID[0] );
WiFi.softAPdisconnect (); // Disconnect anything that we may have
/*
* Start the connection:
*/
// WiFi.begin ( (char *) &configuration.WiFi_SSID[0], (char *) &configuration.WiFi_Password[0] );
WiFi.begin ( WIFI_SSID, WIFI_PASSWORD );
WiFi.setSleep (false ); // Disable sleep
/*
* Apply any hostname that we may have
*/
// if ( strlen ( (char *) &configuration.Hostname[0]) > 0 )
// WiFi.setHostname ( (char *) &configuration.Hostname[0] );
// else
WiFi.setHostname ( PROGRAM_NAME );
int maxTry = 10; // Wait for the connection to be made.
tft.setCursor ( 0, 190 );
tft.printf ( "Connecting to WiFi %s", WIFI_SSID );
while (( WiFi.status() != WL_CONNECTED ) && ( maxTry > 0 ))
{
tft.print("."); // Nice touch!!!
delay ( 1000 ); // Wait and update the try count.
maxTry--;
if ( maxTry <= 0 )
{
DisplayError ( ERR_WARN, "Connecting to", WIFI_SSID, "failed!", "WiFi Disabled" );
return false;
}
}
ipAddress = WiFi.localIP (); // We are connected, display the IP address
Serial.printf ( "Connected - IP %s \n", FormatIPAddress ( ipAddress ));
tft.printf ( "\n\nConnected - IP %s \n", FormatIPAddress ( ipAddress ));
}
/*
* Handle websocket events
* This is how dta is sent from the web page to the ESP32
*/
void webSocketEvent ( uint8_t num, WStype_t type, uint8_t* payload, size_t payloadLength )
{
switch ( type )
{
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected!\n", num);
if ( numberOfWebsocketClients > 0 )
numberOfWebsocketClients--;
updateSidebar = true;
break;
case WStype_CONNECTED:
Serial.printf ( "[%u] Connected from ", num );
Serial.println ( webSocket.remoteIP ( num ));
numberOfWebsocketClients++;
updateSidebar = true;
webSocket.sendTXT ( num, "Connected" ); // send message back to client
break;
/*
*
* Message format
* #(code) value where code is a single char. Case is important
*
* a start freq (MHz) - sig gen freq if in sig gen mode
* b stop frequency
* c centre frequency
* s Span
* d Local Oscillator Drive
* g PreAmpGain/Mode
* i IF frequency
* m mode
* p set actual power to peak value
* o RefOut
* r request Settings are pushed
* A internal attenuation (PE4302)
* E external gain ( eg attenuator(-ve) or preamp(+ve) )
* R requested RBW
* S Spur reduction Off/On
* t trackGen Off/On/Generate
* T trackGen output level
* f trackgen signal generator frequency
* F Sig Gen frequency
* L Sig Gen level (dBm)
* G Sig Gen Off/On
* P Sig Gen set max output level (dBm)
*
* Other formats are ignored
*/
case WStype_TEXT:
if ( payload[0] == '#' )
{
if ( payloadLength > 3 )
{
char* field = strtok ( (char*) &payload[3], " " ); // Extract the field value as string
float value = atof ( field ); // Convert to float
if ( isnan ( value )) // If not a number
return; // Bail out!
Serial.printf ( "payload command %c value %f\n", payload[1], value );
switch ( payload[1] )
{
case 'a':
switch (setting.Mode) {
case SA_LOW_RANGE:
SetSweepStart ( value * 1000000.0 ); // Set Low range sweep start frequency
break;
case SIG_GEN_LOW_RANGE:
SetSweepStart ( value * 1000000.0 ); // Set Low range sweep start frequency
break;
case IF_SWEEP:
SetIFsweepStart ( value * 1000000.0 ); // Set IF sweep start frequency
break;
case RX_SWEEP:
SetRXsweepStart ( value * 1000000.0 ); // Set RX sweep start frequency
break;
case BANDSCOPE:
SetBandscopeStart ( value * 1000000.0 ); // Set sweep start frequency
break;
}
break;
case 'b':
switch (setting.Mode) {
case SA_LOW_RANGE:
SetSweepStop ( value * 1000000.0 ); // Set Low range sweep start frequency
break;
case IF_SWEEP:
SetIFsweepStop ( value * 1000000.0 ); // Set IF sweep start frequency
break;
case RX_SWEEP:
SetRXsweepStop ( value * 1000000.0 ); // Set RX sweep start frequency
break;
}
break;
case 'c':
SetSweepCenter ( value * 1000000.0, WIDE ); // Set sweep center frequency
break;
case 'd':
SetLoDrive ( (uint8_t) value );
break;
case 'f':
SetTGFreq ( value );
break;
case 'g':
SetPreampGain( (int) value ); // Set PreAmp gain register
break;
case 'i': // IF Frequency
SetIFFrequency ( (int32_t) ( value * 1000000.0 ));
break;
case 'm': // Set mode
setMode ( (int16_t) ( value ));
break;
case 'o': // Ref Output (LO GPIO2)
SetRefOutput ( (int) value );
break;
case 'p': // Adjust actual power level
RequestSetPowerLevel( value );
break;
case 'r': // request of settings by client
switch (setting.Mode)
{
case (SA_LOW_RANGE):
pushSettings();
break;
case (IF_SWEEP):
pushIFSweepSettings();
break;
case (RX_SWEEP):
pushRXSweepSettings();
break;
case (BANDSCOPE):
pushBandscopeSettings();
break;
case (SIG_GEN_LOW_RANGE):
pushSigGenSettings();
break;
default:
Serial.println("Invalid mode in Request setting handler - simpleSA_wifi.cpp");
}
break;
case 's': // Adjust sweep span
SetSweepSpan ( (int32_t) ( value * 1000000.0 ));
break;
#ifdef TG_IF_INSTALLED
case 't': // Tracking Generator off/on/generate
SetTracking ( (int8_t)value );
break;
#endif
case 'A': // Internal Attenuation (PE4302)
SetAttenuation ( value );
break;
case 'E': // External Gain (+ve) or Attenuation (-ve)
SetExtGain ( value );
break;
case 'F':
SetSGFreq ( value ); // Signal Generator Frequency
break;
case 'G':
SetSGState ( (uint16_t)value ); // Signal Generator output on/off
break;
case 'L': // Signal Generator output power (dBm)
SetSGPower ( value );
break;
case 'R': // Requested RBW. 0=Auto
SetRBW ( value );
Serial.printf("Wifi RBW %f\n", value);
break;
case 'S': // Spur Reduction on/off
SetSpur ( (int8_t)value );
break;
#ifdef TG_IF_INSTALLED
case 'T': // Tracking Generator output power (dBm)
SetTGPower ( value );
break;
#endif
default:
Serial.printf ( "payload[1] was %c\n", payload[1] );
break;
}
}
}
else // payload[0] is not '#'
{
webSocket.sendTXT ( num, "pong" ); // send message back to client to keep connection alive
Serial.printf ( "[%u] get Text: %s\n", num, payload );
}
break;
default:
Serial.println ( "Case?" );
break;
}
}
/*
* Monitor Wi-Fi connection if it is alive. If not alive then wait until it reconnects.
*/
void isWiFiAlive ( void )
{
if ( WiFi.status() != WL_CONNECTED )
{
Serial.print ( "not connected" );
while ( WiFi.status() != WL_CONNECTED )
{
Serial.print ( "." );
delay(500);
}
numberOfReconnects++;
millisConnected = millis();
}
}
/*
* Some routines for XML
*/
char *escapeXML ( char *s ) // Use special codes for some characters
{
static char b[1024];
b[0] = '\0'; // Null terminator
for ( int i = 0; i < strlen(s); i++ )
{
switch ( s[i] )
{
case '\"':
strcat ( b,"&quot;" );
break;
case '&':
strcat (b, "&amp;" );
break;
case '<':
strcat ( b,"&lt;" );
break;
case '>':
strcat ( b,"&gt;" );
break;
default:
int l = strlen ( b );
b[l] = s[i];
b[l + 1] = '\0';
break;
}
}
}
/*
* Add an XML tag with its value to the buffer b
*/
void addTagNameValue ( char *b, char *_name, char *value )
{
strcat ( b,_name );
strcat ( b, "=\"" );
strcat ( b,value );
strcat ( b,"\" " );
}
/*
* On request from web page convert the data from a scan into XML and send
*
* Ideally we would push the data to the web page at the end of a scan,
* or perhaps just create the xml at the end of each scan - investigate later
*/
void onGetScan ( AsyncWebServerRequest *request )
{
response = request->beginResponseStream ( "text/xml" );
// Serial.println ( "onGetScan" );
response->print ( "<?xml version=\"1.0\" encoding=\"utf-16\"?>" );
response->println ( "<Points>" );
for( int i = 0; i < displayPoints-1; i++ ) // For each data point
{
// Serial.printf ( "<Point F=\"%i\" RSSI=\"%i\"/> %i\n",myFreq[i], myData[i], i );
response->printf ( "<P F=\"%i\" R=\"%i\"/>\n", myFreq[i], myData[i] );
}
response->print ( "</Points>" );
request->send ( response );
}
/*
* On request from web page convert the gain data from a sweep into JSON and send
* Ideally we would push the data to the web page at the end of a scan,
*/
void onGetGainSweep ( AsyncWebServerRequest *request )
{
size_t bufferSize = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ JSON_OBJECT_SIZE ( 1 ) + SCREEN_WIDTH * JSON_OBJECT_SIZE ( 2 );
AsyncJsonResponse * response = new AsyncJsonResponse ( false, bufferSize );
// response->addHeader ( "Server","ESP Async Web Server" );
JsonObject root = response->getRoot();
JsonArray gainPoints = root.createNestedArray ( "gainPoints" ); // Add gainPoints array
/*
* Add the objects to the array
*/
for ( int i = 0; i < displayPoints; i++ ) // For each data point
{
JsonObject dataPoint = gainPoints.createNestedObject(); // Add an object to the array
dataPoint["x"] = myFreq[i]/1000000.0; // set the x(frequency) value
dataPoint["y"] = myGain[i]; // set the y (gain) value
}
response->setLength();
request->send ( response );
}
/*
* On request from web page convert the data from a sweep into JSON and send.
* Ideally we would push the data to the web page at the end of a scan.
*/
void onGetSweep ( AsyncWebServerRequest *request )
{
size_t bufferSize = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ JSON_OBJECT_SIZE ( 14 ) + SCREEN_WIDTH * JSON_OBJECT_SIZE ( 2 );
AsyncJsonResponse * response = new AsyncJsonResponse ( false, bufferSize );
JsonObject root = response->getRoot();
root["dispPoints"] = displayPoints;
root["start"] = setting.ScanStart / 1000.0;
root["stop"] = setting.ScanStop / 1000.0;
root["IF"] = setting.IF_Freq / 1000000.0;
root["attenuation"] = setting.Attenuate;
root["extGain"] = setting.ExternalGain;
root["levelOffset"] = setting.LevelOffset;
root["setRBW"] = setting.Bandwidth10;
root["bandwidth"] = bandwidth;
root["RefOut"] = setting.ReferenceOut;
root["Drive"] = setting.Drive;
root["sweepPoints"] = sweepPoints;
if ( AGC_On )
root["PreAmp"] = 0x60; // Auto
else
root["PreAmp"] = setting.PreampGain; // Fixed gain
JsonArray Points = root.createNestedArray ( "Points" ); // Add Points array
for ( int i = 0; i < displayPoints; i++ ) //For each data point
{
JsonObject dataPoint = Points.createNestedObject(); // add an object to the array
dataPoint["x"] = myFreq[i]/1000000.0; // set the x(frequency) value
dataPoint["y"] = myData[i]; // set the y (RSSI) value
}
response->setLength();
request->send ( response );
}
/*
* On request from web page send the settings as JSON
*/
void onGetSettings (AsyncWebServerRequest *request)
{
AsyncJsonResponse * response = new AsyncJsonResponse(false) ;
JsonObject root = response->getRoot();
root["mType"] = "Settings";
root["mode"] = setting.Mode;
root["dispPoints"] = displayPoints;
root["start"] = setting.ScanStart / 1000.0;
root["stop"] = setting.ScanStop / 1000.0;
root["IF"] = setting.IF_Freq / 1000000.0;
root["attenuation"] = setting.Attenuate;
root["extGain"] = setting.ExternalGain;
root["levelOffset"] = setting.LevelOffset;
root["setRBW"] = setting.Bandwidth10;
root["bandwidth"] = bandwidth;
root["RefOut"] = setting.ReferenceOut;
root["Drive"] = setting.Drive;
root["sweepPoints"] = sweepPoints;
root["spur"] = setting.Spur;
root["tg"] = trackGenSetting.Mode;
root["tgPower"] = trackGenSetting.Power;
root["tgFreq"] = trackGenSetting.Frequency;
root["tgMod"] = trackGenSetting.ModulationType;
root["tgModFreq"] = trackGenSetting.ModFrequency;
root["sg"] = sigGenOutputOn; //
root["sgPower"] = sigGenSetting.Power;
root["sgMod"] = sigGenSetting.ModulationType;
root["sgModFreq"] = sigGenSetting.ModFrequency;
root["sgFreq"] = sigGenSetting.Frequency;
root["sgCal"] = sigGenSetting.Calibration;
root["sgRange"] = ATTENUATOR_RANGE + 11; // SI4432 output can be adjusted over 21dBm but max to SAW filter is 10dBm
root["tgCal"] = trackGenSetting.Calibration;
root["tgRange"] = ATTENUATOR_RANGE + 21; // no SAW filter so 20dBm output is possible.
if ( AGC_On )
root["PreAmp"] = 0x60; // Auto
else
root["PreAmp"] = setting.PreampGain; // Fixed gain
response->setLength();
request->send ( response );
// Serial.printf ( "Get Settings sweepPoints %u\n", sweepPoints );
}
/*
* Push the settings data to the websocket clients
*/
void pushSettings ()
{
if ( numberOfWebsocketClients == 0 )
return;
size_t capacity = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ SCREEN_WIDTH*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 17 );
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
jsonDocument["mType"] = "Settings";
jsonDocument["mode"] = setting.Mode;
jsonDocument["dispPoints"] = displayPoints;
jsonDocument["start"] = setting.ScanStart / 1000.0;
jsonDocument["stop"] = setting.ScanStop / 1000.0;
jsonDocument["IF"] = setting.IF_Freq / 1000000.0;
jsonDocument["attenuation"] = setting.Attenuate;
jsonDocument["extGain"] = setting.ExternalGain;
jsonDocument["levelOffset"] = setting.LevelOffset;
jsonDocument["setRBW"] = setting.Bandwidth10;
jsonDocument["bandwidth"] = bandwidth;
jsonDocument["RefOut"] = setting.ReferenceOut;
jsonDocument["Drive"] = setting.Drive;
jsonDocument["sweepPoints"] = sweepPoints;
jsonDocument["spur"] = setting.Spur;
jsonDocument["tg"] = trackGenSetting.Mode;
jsonDocument["tgPower"] = trackGenSetting.Power;
jsonDocument["tgFreq"] = trackGenSetting.Frequency;
jsonDocument["tgMod"] = trackGenSetting.ModulationType;
jsonDocument["tgModFreq"] = trackGenSetting.ModFrequency;
jsonDocument["sg"] = sigGenOutputOn; //
jsonDocument["sgPower"] = sigGenSetting.Power;
jsonDocument["sgMod"] = sigGenSetting.ModulationType;
jsonDocument["sgModFreq"] = sigGenSetting.ModFrequency;
jsonDocument["sgFreq"] = sigGenSetting.Frequency;
jsonDocument["sgCal"] = sigGenSetting.Calibration;
jsonDocument["sgRange"] = ATTENUATOR_RANGE + 11; // SI4432 output can be adjusted over 21dBm but max to SAW filter is 10dBm
jsonDocument["tgCal"] = trackGenSetting.Calibration;
jsonDocument["tgRange"] = ATTENUATOR_RANGE + 21; // no SAW filter so 20dBm output is possible.
if ( AGC_On )
jsonDocument["PreAmp"] = 0x60; // Auto
else
jsonDocument["PreAmp"] = setting.PreampGain; // Fixed gain
String wsBuffer;
if ( wsBuffer )
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
// Serial.printf ( "Push Settings sweepPoints %u\n", sweepPoints );
}
/*
* Push the settings data to the websocket clients
*/
void pushIFSweepSettings ()
{
size_t capacity = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ SCREEN_WIDTH*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 17 );
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
jsonDocument["mType"] = "Settings";
jsonDocument["mode"] = setting.Mode;
jsonDocument["dispPoints"] = displayPoints;
jsonDocument["start"] = startFreq_IF / 1000.0;
jsonDocument["stop"] = stopFreq_IF / 1000.0;
jsonDocument["IF"] = setting.IF_Freq / 1000000.0;
jsonDocument["attenuation"] = setting.Attenuate;
jsonDocument["extGain"] = setting.ExternalGain;
jsonDocument["levelOffset"] = setting.LevelOffset;
jsonDocument["setRBW"] = setting.Bandwidth10;
jsonDocument["bandwidth"] = bandwidth;
jsonDocument["RefOut"] = setting.ReferenceOut;
jsonDocument["Drive"] = setting.Drive;
jsonDocument["sweepPoints"] = sweepPoints;
jsonDocument["spur"] = setting.Spur;
jsonDocument["tg"] = trackGenSetting.Mode;
jsonDocument["tgPower"] = trackGenSetting.Power;
jsonDocument["tgFreq"] = trackGenSetting.Frequency;
jsonDocument["sg"] = sigGenOutputOn; //
jsonDocument["sgPower"] = sigGenSetting.Power;
jsonDocument["sgMod"] = sigGenSetting.ModulationType;
jsonDocument["sgModFreq"] = sigGenSetting.ModFrequency;
jsonDocument["sgFreq"] = sigGenSetting.Frequency;
jsonDocument["sgCal"] = sigGenSetting.Calibration;
jsonDocument["sgRange"] = ATTENUATOR_RANGE + 11; // SI4432 output can be adjusted over 21dBm but max to SAW filter is 10dBm
jsonDocument["tgCal"] = trackGenSetting.Calibration;
jsonDocument["tgRange"] = ATTENUATOR_RANGE + 21; // no SAW filter so 20dBm output is possible.
if ( AGC_On )
jsonDocument["PreAmp"] = 0x60; // Auto
else
jsonDocument["PreAmp"] = setting.PreampGain; // Fixed gain
String wsBuffer;
if ( wsBuffer )
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
// Serial.printf ( "Push Settings sweepPoints %u\n", sweepPoints );
}
/*
* Push the settings data to the websocket clients
*/
void pushRXSweepSettings ()
{
size_t capacity = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ SCREEN_WIDTH*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 17 );
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
jsonDocument["mType"] = "Settings";
jsonDocument["mode"] = setting.Mode;
jsonDocument["dispPoints"] = displayPoints;
jsonDocument["start"] = startFreq_RX / 1000.0;
jsonDocument["stop"] = stopFreq_RX / 1000.0;
jsonDocument["IF"] = setting.IF_Freq / 1000000.0;
jsonDocument["attenuation"] = setting.Attenuate;
jsonDocument["extGain"] = setting.ExternalGain;
jsonDocument["levelOffset"] = 0;
jsonDocument["setRBW"] = setting.Bandwidth10;
jsonDocument["bandwidth"] = bandwidth;
jsonDocument["RefOut"] = setting.ReferenceOut;
jsonDocument["Drive"] = setting.Drive;
jsonDocument["sweepPoints"] = sweepPoints;
jsonDocument["spur"] = setting.Spur;
jsonDocument["tg"] = trackGenSetting.Mode;
jsonDocument["tgPower"] = trackGenSetting.Power;
jsonDocument["tgFreq"] = trackGenSetting.Frequency;
jsonDocument["sg"] = sigGenOutputOn; //
jsonDocument["sgPower"] = sigGenSetting.Power;
jsonDocument["sgMod"] = sigGenSetting.ModulationType;
jsonDocument["sgModFreq"] = sigGenSetting.ModFrequency;
jsonDocument["sgFreq"] = sigGenSetting.Frequency;
jsonDocument["sgCal"] = sigGenSetting.Calibration;
jsonDocument["sgRange"] = ATTENUATOR_RANGE + 11; // SI4432 output can be adjusted over 21dBm but max to SAW filter is 10dBm
jsonDocument["tgCal"] = trackGenSetting.Calibration;
jsonDocument["tgRange"] = ATTENUATOR_RANGE + 21; // no SAW filter so 20dBm output is possible.
if ( AGC_On )
jsonDocument["PreAmp"] = 0x60; // Auto
else
jsonDocument["PreAmp"] = setting.PreampGain; // Fixed gain
String wsBuffer;
if ( wsBuffer )
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
// Serial.printf ( "Push Settings sweepPoints %u\n", sweepPoints );
}
/*
* Push the settings data to the websocket clients
*/
void pushBandscopeSettings ()
{
size_t capacity = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ SCREEN_WIDTH*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 17 );
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
jsonDocument["mType"] = "Settings";
jsonDocument["mode"] = setting.Mode;
jsonDocument["dispPoints"] = setting.BandscopePoints;
jsonDocument["start"] = setting.BandscopeStart / 1000.0;
jsonDocument["stop"] = ( setting.BandscopeStart + setting.BandscopeSpan ) / 1000.0;
jsonDocument["IF"] = setting.IF_Freq / 1000000.0;
jsonDocument["attenuation"] = setting.Attenuate;
jsonDocument["extGain"] = setting.ExternalGain;
jsonDocument["levelOffset"] = setting.LevelOffset;
jsonDocument["setRBW"] = setting.Bandwidth10;
jsonDocument["bandwidth"] = bandwidth;
jsonDocument["RefOut"] = setting.ReferenceOut;
jsonDocument["Drive"] = setting.Drive;
jsonDocument["sweepPoints"] = sweepPoints;
jsonDocument["spur"] = setting.Spur;
jsonDocument["tg"] = trackGenSetting.Mode;
jsonDocument["tgPower"] = trackGenSetting.Power;
jsonDocument["tgFreq"] = trackGenSetting.Frequency;
jsonDocument["sg"] = sigGenOutputOn; //
jsonDocument["sgPower"] = sigGenSetting.Power;
jsonDocument["sgMod"] = sigGenSetting.ModulationType;
jsonDocument["sgModFreq"] = sigGenSetting.ModFrequency;
jsonDocument["sgFreq"] = sigGenSetting.Frequency;
jsonDocument["sgCal"] = sigGenSetting.Calibration;
jsonDocument["sgRange"] = ATTENUATOR_RANGE + 11; // SI4432 output can be adjusted over 21dBm but max to SAW filter is 10dBm
jsonDocument["tgCal"] = trackGenSetting.Calibration;
jsonDocument["tgRange"] = ATTENUATOR_RANGE + 21; // no SAW filter so 20dBm output is possible.
if ( AGC_On )
jsonDocument["PreAmp"] = 0x60; // Auto
else
jsonDocument["PreAmp"] = setting.PreampGain; // Fixed gain
String wsBuffer;
if ( wsBuffer )
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
// Serial.printf ( "Push Settings sweepPoints %u\n", sweepPoints );
}
/*
* Push the settings data to the websocket clients
*/
void pushSigGenSettings ()
{
size_t capacity = JSON_ARRAY_SIZE ( SCREEN_WIDTH )
+ SCREEN_WIDTH*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 17 );
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
jsonDocument["mType"] = "Settings";
jsonDocument["mode"] = setting.Mode;
jsonDocument["dispPoints"] = displayPoints;
jsonDocument["start"] = startFreq_IF / 1000.0;
jsonDocument["stop"] = stopFreq_IF / 1000.0;
jsonDocument["IF"] = setting.IF_Freq / 1000000.0;
jsonDocument["attenuation"] = setting.Attenuate;
jsonDocument["extGain"] = setting.ExternalGain;
jsonDocument["levelOffset"] = setting.LevelOffset;
jsonDocument["setRBW"] = setting.Bandwidth10;
jsonDocument["bandwidth"] = bandwidth;
jsonDocument["RefOut"] = setting.ReferenceOut;
jsonDocument["Drive"] = setting.Drive;
jsonDocument["sweepPoints"] = sweepPoints;
jsonDocument["spur"] = setting.Spur;
jsonDocument["tg"] = trackGenSetting.Mode;
jsonDocument["tgPower"] = trackGenSetting.Power;
jsonDocument["tgMod"] = trackGenSetting.ModulationType;
jsonDocument["tgModFreq"] = trackGenSetting.ModFrequency;
jsonDocument["tgFreq"] = trackGenSetting.Frequency;
jsonDocument["sg"] = sigGenOutputOn; //
jsonDocument["sgPower"] = sigGenSetting.Power;
jsonDocument["sgMod"] = sigGenSetting.ModulationType;
jsonDocument["sgModFreq"] = sigGenSetting.ModFrequency;
jsonDocument["sgFreq"] = sigGenSetting.Frequency;
jsonDocument["sgCal"] = sigGenSetting.Calibration;
jsonDocument["sgRange"] = (ATTENUATOR_RANGE + RX_SI4432_MAX_DRIVE * 3); // SI4432 output can be adjusted over 21dBm but max to SAW filter is 10dBm
jsonDocument["tgCal"] = trackGenSetting.Calibration;
jsonDocument["tgRange"] = ATTENUATOR_RANGE + 21; // no SAW filter so full 20dBm output is possible.
if ( AGC_On )
jsonDocument["PreAmp"] = 0x60; // Auto
else
jsonDocument["PreAmp"] = setting.PreampGain; // Fixed gain
String wsBuffer;
if ( wsBuffer )
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
// Serial.printf ( "Push Settings sweepPoints %u\n", sweepPoints );
}
/*
* Prepare a response ready for push to web clients
*/
/*
* On request from web page return the list of valid RBW settings from the
* "bandpassFilters" array (found in the ".ino" file).
*/
void onGetRbwList ( AsyncWebServerRequest *request )
{
response = request->beginResponseStream ( "application/json" );
// Serial.println ( "onGetRbwList" );
response->print ( "[" ); // Start of object
int filterCount = rcvr.GetBandpassFilterCount ();
for ( int i = 0; i < filterCount-1 ; i++ ) // For each element in the bandpassfilters array
response->printf ( "{\"bw10\":%i,\"bw\":%5.1f},",
rcvr.GetBandpassFilter10(i),
(float) rcvr.GetBandpassFilter10(i) / 10.0 );
response->printf ( "{\"bw10\":%i,\"bw\":%5.1f}", rcvr.GetBandpassFilter10(filterCount-1),
(float) rcvr.GetBandpassFilter10(filterCount-1) / 10.0 );
response->println ( "]" ); // End of object
request->send ( response );
}
/*
* On request from web page return the list of valid attenuations
* In the case of the PE4302 this is 0-31.5 in 0.5db steps,
* but we will reduce this to 3dB steps
* Insertion loss is about 1.5dB
*
*/
void onGetAttenList ( AsyncWebServerRequest *request )
{
response = request->beginResponseStream ( "application/json" );
// Serial.println ( "onGetAttList" );
response->print ( "[" ); // Start of object
for ( int i = 0; i < 30 ; i = i + 3 ) // For each possible attenuation
response->printf ( "{\"dB\":%i},", i );
response->printf ( "{\"dB\":%i}", 30 );
response->println ( "]" ); // End of object
request->send ( response );
}
/*
* Functions to execute when the user presses buttons on the webpage
*/
void onDoReboot ( AsyncWebServerRequest *request )
{
request->redirect ( "index.html" ); // Redirect to the index page
ESP.restart ();
}
/*
* Function sets the sweep parameters based on the data in the form posted by the web page
* No longer used
*/
// doSetSweep ? setStart = 10 & setStop = 20 & setExtGain = 0 & setRBW = 26
void onSetSweep ( AsyncWebServerRequest *request )
{
Serial.print ( request->url ()); // Get the paramaters passed from the
Serial.print ( ":-" ); // web page, checking they exist
if ( request->hasParam ( "setStart" ))
{
Serial.print ( "setStart;" );
AsyncWebParameter* startInput = request->getParam ( "setStart" );
SetSweepStart ( atof ( startInput->value().c_str()) * 1000000.0 );
}
if ( request->hasParam ( "setStop" ))
{
Serial.print ( "setStop;" );
AsyncWebParameter* stopInput = request->getParam ( "setStop" );
SetSweepStop( atof ( stopInput->value().c_str()) * 1000000.0 );
}
if ( request->hasParam ( "setExtGain" ))
{
Serial.print ( "setExtGain;" );
AsyncWebParameter* extGainInput = request->getParam ( "setExtGain" );
// Need to add function later
}
if ( request->hasParam ( "refOut" ))
{
Serial.print ( "refOut;" );
AsyncWebParameter* setRefOut = request->getParam ( "setRefOut" );
setting.ReferenceOut = atoi ( setRefOut->value().c_str() );
xmit.SetPowerReference ( setting.ReferenceOut );
}
if ( request->hasParam ( "setAtten" ))
{
Serial.print ( "setAtten:" );
AsyncWebParameter* setAtten = request->getParam ( "setAtten");
SetAttenuation ( atoi ( setAtten->value().c_str() ));
Serial.print ( atoi ( setAtten->value().c_str() ));
Serial.print ( "; " );
}
if ( request->hasParam ( "setRBW" ))
{
Serial.print ( "setRBW ");
AsyncWebParameter* rbwInput = request->getParam ( "setRBW" );
SetRBW ( atoi ( rbwInput->value().c_str() ));
Serial.printf ( "setting.bandwidth = %i, input = %i;", setting.Bandwidth10, atoi ( rbwInput->value().c_str() ));
}
Serial.println ();
request->redirect ( "index.html" ); // redirect to the index page
}
/*
* Function sets the sweep parameters based on the data in the form posted by the web page
* No longer used
*/
// doSetSweep ? setStart = 10 &setStop = 20 & setExtGain = 0 & setRBW = 26
void onSettings ( AsyncWebServerRequest *request )
{
Serial.print ( request->url() ); // Get the paramaters passed from the web
Serial.print ( ":-" ); // page, checking they exist
if ( request->hasParam ( "setActPower" ))
{
Serial.print ( "setActPower;" );
AsyncWebParameter* setLevelInput = request->getParam ( "setActPower" );
SetPowerLevel ( atof ( setLevelInput->value().c_str()) );
}
Serial.println();
request->redirect ( "index.html"); // Redirect to the index page
}
void onGetNameVersion ( AsyncWebServerRequest *request )
{
AsyncResponseStream *response = request->beginResponseStream ( "text/xml" );
response->printf ( "<?xml version=\"1.0\" encoding=\"utf-16\"?>" );
response->printf ( "<IndexNameVersion " );
response->printf ( "Name=\"%s\" ",PROGRAM_NAME );
response->printf ( "Version=\"V%s\" ",PROGRAM_VERSION );
response->printf ( "Copyright=\"PD0EK\"" );
response->printf ( "/>" );
request->send(response);
}
void onGetSIDDs ( AsyncWebServerRequest *request )
{
char b[1024];
b[0] = '\0';
Serial.println ( "" );
Serial.println ( "Scanning for SSIDs" );
/*
* We need to return a blob of XML containing the visible SSIDs
*/
strcpy ( b, "<SSIDs>" ); // Start of XML
int n = WiFi.scanComplete ();
if ( n == -2 )
WiFi.scanNetworks ( true );
else if ( n )
{
for ( int i = 0; i < n; ++i )
{
strcat ( b,"<SSID Name = \"" ); // Add the SSID to the result
strcat ( b, WiFi.SSID (i).c_str() );
strcat ( b,"\" />" );
Serial.println ( "... " + WiFi.SSID (i) );
}
WiFi.scanDelete ();
if ( WiFi.scanComplete() == -2 )
WiFi.scanNetworks ( true );
}
strcat ( b, "</SSIDs>" ); // Complete the XML
request->send ( 200, "text/xml", b ); // Send it to the server
}
/*
* Build the web server
* the order here is important - put frequent ones at top of list to improve performance
*/
void buildServer () // We can now configure and start the server
{
Serial.println ( "Building Server.." );
server.reset (); // Clear any existing settings and events
server.on ( "/getSweep", HTTP_GET, onGetSweep ); // Set event to return sweep data as JSON array
server.on ( "/getGainSweep", HTTP_GET, onGetGainSweep ); // Set event to return sweep gain data as JSON array
server.on ( "/getSettings", HTTP_GET, onGetSettings ); // Set event to return settings data as JSON array
server.on ( "/doSetSweep", HTTP_POST, onSetSweep ); // Set event to set sweep values received from client
server.on ( "/doReboot", HTTP_GET, onDoReboot ); // Set event to reboot the ESP32
server.on ( "/getNameVersion", HTTP_GET, onGetNameVersion );// Set event to return name and version
server.on ( "/getScan", HTTP_GET, onGetScan ); // Set event to return sweep data as XML
server.on ( "/getRbwList", HTTP_GET, onGetRbwList ); // Set event to return RBW options as JSON array
server.on ( "/getAttenList", HTTP_GET, onGetAttenList ); // Set event to return attenuator options as JSON array
server.on ( "/getSSIDs", HTTP_GET, onGetSIDDs ); // Set event to return list of SSID as XML
server.on ( "/doSettings", HTTP_POST, onSettings ); // Set event to set setting values received from client
server.serveStatic ( "/", SPIFFS, "/" ).setDefaultFile ( "index.html" );
server.begin ();
}