/*
* "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 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 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
/*
* 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 '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;
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;
case 'A': // Internal Attenuation (PE4302)
SetAttenuation ( value );
break;
case 'E': // External Gain (+ve) or Attenuation (-ve)
SetExtGain ( 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;
case 't': // Tracking Generator on/off
SetTracking ( (int8_t)value );
break;
case 'T': // Tracking Generator output power (dBm)
SetTGPower ( 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 ( "" );
response->println ( "" );
for( int i = 0; i < displayPoints-1; i++ ) // For each data point
{
// Serial.printf ( " %i\n",myFreq[i], myData[i], i );
response->printf ( "