867 lines
24 KiB
C++
867 lines
24 KiB
C++
![]() |
/*
|
||
|
* "TinySA_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 "TinySA_wifi.h" // WiFi definitions
|
||
|
#include "Si4432.h" // Si4432 definitions
|
||
|
#include "Cmd.h" // Command processing functions
|
||
|
|
||
|
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 );
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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;
|
||
|
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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.localIP ();
|
||
|
|
||
|
Serial.printf ( "Access Point started, result = %b \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;
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Format of message to process
|
||
|
*
|
||
|
* #a 123.123 Start frequency
|
||
|
* #b 123.123 Stop frequency
|
||
|
*
|
||
|
* 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':
|
||
|
SetSweepStart ( value * 1000000.0 ); // Set sweep start frequency
|
||
|
break;
|
||
|
|
||
|
case 'b':
|
||
|
SetSweepStop ( value * 1000000.0 ); // Set sweep stop frequency
|
||
|
break;
|
||
|
|
||
|
case 'c':
|
||
|
SetSweepCenter ( value * 1000000.0, WIDE ); // Set sweep center frequency
|
||
|
break;
|
||
|
|
||
|
case 'd':
|
||
|
SetLoDrive ( (uint8_t) value );
|
||
|
break;
|
||
|
|
||
|
case 'g':
|
||
|
SetPreampGain( (int) value ); // Set PreAmp gain register
|
||
|
break;
|
||
|
|
||
|
case 'i': // IF Frequency
|
||
|
SetIFFrequency ( (int32_t) ( value * 1000000.0 ));
|
||
|
break;
|
||
|
|
||
|
case 'o': // Ref Output (LO GPIO2)
|
||
|
SetRefOutput ( (int) value );
|
||
|
break;
|
||
|
|
||
|
case 'p': // Adjust actual power level
|
||
|
RequestSetPowerLevel( value );
|
||
|
break;
|
||
|
|
||
|
case 's': // Adjust sweep span
|
||
|
SetSweepSpan ( (int32_t) ( value * 1000000.0 ));
|
||
|
break;
|
||
|
|
||
|
case 'A': // Internal Attenuation (PE4302)
|
||
|
SetAttenuation ( value );
|
||
|
break;
|
||
|
|
||
|
case 'R': // Requested RBW. 0=Auto
|
||
|
SetRBW ( value );
|
||
|
break;
|
||
|
|
||
|
case 'S': // Spur Reduction on/off
|
||
|
SetSpur ( value );
|
||
|
break;
|
||
|
|
||
|
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,""" );
|
||
|
break;
|
||
|
|
||
|
case '&':
|
||
|
strcat (b, "&" );
|
||
|
break;
|
||
|
|
||
|
case '<':
|
||
|
strcat ( b,"<" );
|
||
|
break;
|
||
|
|
||
|
case '>':
|
||
|
strcat ( b,">" );
|
||
|
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 < DISPLAY_POINTS-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 ( DISPLAY_POINTS )
|
||
|
+ JSON_OBJECT_SIZE ( 1 ) + DISPLAY_POINTS * 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 < DISPLAY_POINTS; 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 ( DISPLAY_POINTS )
|
||
|
+ JSON_OBJECT_SIZE ( 14 ) + DISPLAY_POINTS * JSON_OBJECT_SIZE ( 2 );
|
||
|
|
||
|
AsyncJsonResponse * response = new AsyncJsonResponse ( false, bufferSize );
|
||
|
|
||
|
JsonObject root = response->getRoot();
|
||
|
|
||
|
root["dispPoints"] = DISPLAY_POINTS;
|
||
|
root["start"] = setting.ScanStart / 1000.0;
|
||
|
root["stop"] = setting.ScanStop / 1000.0;
|
||
|
root["IF"] = setting.IF_Freq / 1000000.0;
|
||
|
root["attenuation"] = setting.Attenuate;
|
||
|
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 < DISPLAY_POINTS; 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["dispPoints"] = DISPLAY_POINTS;
|
||
|
root["start"] = setting.ScanStart / 1000.0;
|
||
|
root["stop"] = setting.ScanStop / 1000.0;
|
||
|
root["IF"] = setting.IF_Freq / 1000000.0;
|
||
|
root["attenuation"] = setting.Attenuate;
|
||
|
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;
|
||
|
|
||
|
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 ()
|
||
|
{
|
||
|
size_t capacity = JSON_ARRAY_SIZE ( DISPLAY_POINTS )
|
||
|
+ DISPLAY_POINTS*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 );
|
||
|
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
|
||
|
|
||
|
jsonDocument["mType"] = "Settings";
|
||
|
jsonDocument["dispPoints"] = DISPLAY_POINTS;
|
||
|
jsonDocument["start"] = setting.ScanStart / 1000.0;
|
||
|
jsonDocument["stop"] = setting.ScanStop / 1000.0;
|
||
|
jsonDocument["IF"] = setting.IF_Freq / 1000000.0;
|
||
|
jsonDocument["attenuation"] = setting.Attenuate;
|
||
|
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;
|
||
|
|
||
|
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 ( DISPLAY_POINTS )
|
||
|
+ DISPLAY_POINTS*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 );
|
||
|
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
|
||
|
|
||
|
jsonDocument["mType"] = "Settings";
|
||
|
jsonDocument["dispPoints"] = DISPLAY_POINTS;
|
||
|
jsonDocument["start"] = startFreq_IF / 1000.0;
|
||
|
jsonDocument["stop"] = stopFreq_IF / 1000.0;
|
||
|
jsonDocument["IF"] = sigFreq_IF / 1000000.0;
|
||
|
jsonDocument["attenuation"] = setting.Attenuate;
|
||
|
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;
|
||
|
|
||
|
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 ( DISPLAY_POINTS )
|
||
|
+ DISPLAY_POINTS*JSON_OBJECT_SIZE ( 2 ) + JSON_OBJECT_SIZE ( 13 );
|
||
|
static DynamicJsonDocument jsonDocument ( capacity ); // buffer for json data to be pushed to the web clients
|
||
|
|
||
|
jsonDocument["mType"] = "Settings";
|
||
|
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["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;
|
||
|
|
||
|
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 ();
|
||
|
}
|