/* * "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 uint16_t bpfCount; // Number of elements in the bandpassFilters array extern double bpfCalibrations[MAX_SI4432_FILTERS]; // temporary storage for calibration values extern uint16_t bpfIndex; // Index for current rbw filter 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,""" ); 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 ( "

\n", myFreq[i], myData[i] ); } response->print ( "" ); 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["filterCal"] = bpfCalibrations[bpfIndex]; 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["filterCal"] = bpfCalibrations[bpfIndex]; 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["filterCal"] = bpfCalibrations[bpfIndex]; 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["filterCal"] = bpfCalibrations[bpfIndex]; 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["filterCal"] = bpfCalibrations[bpfIndex]; 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["filterCal"] = bpfCalibrations[bpfIndex]; 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["filterCal"] = bpfCalibrations[bpfIndex]; 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 ( "" ); response->printf ( "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, "" ); // 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,"" ); Serial.println ( "... " + WiFi.SSID (i) ); } WiFi.scanDelete (); if ( WiFi.scanComplete() == -2 ) WiFi.scanNetworks ( true ); } strcat ( b, "" ); // 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 (); }