simpleSA/SweepLo.ino
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

709 lines
23 KiB
C++

/*
* Initialise variables and SI4432 for the low frequency sweep
*/
void initSweepLow()
{
// set up checkerboard sizes
gridHeight = GRID_HEIGHT;
gridWidth = DISPLAY_POINTS;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = X_ORIGIN;
yOrigin = Y_ORIGIN;
displayPoints = DISPLAY_POINTS;
xDelta = displayPoints / xGrid;
init_sweep();
tinySA_mode = SA_LOW_RANGE;
setting.Mode = tinySA_mode;
ResetSAMenuStack(); // Put menu stack back to root level
}
/*
* This function section handles the low freq range sweep
*/
void doSweepLow()
{
static uint32_t autoSweepStep = 0;
static uint32_t autoSweepFreq = 0;
static uint32_t autoSweepFreqStep = 0;
static uint32_t nextPointFreq = 0; // Frequency for the next display point. Used for substeps
static unsigned long setFreqMicros;
static unsigned long nowMicros;
static uint32_t sweepStep; // Step count
static uint32_t sweepFreqStep;
static int16_t pointMinGain; // to record minimum gain for the current display point
static int16_t pointMaxRSSI; // to record max RSSI of the samples in the current display point
static uint32_t pointMaxFreq; // record frequency where maximum occurred
static int16_t lastMode; // Record last operating mode (sig gen, normal)
static uint32_t lastIF;
static bool spurToggle;
static uint32_t actualFreq; // actual frequency
static uint32_t actualIF; // actual IF (Rx frequency)
static uint16_t currentPointRSSI;
static uint16_t peakRSSI;
static uint16_t prevPointRSSI;
static uint32_t peakFreq;
static uint16_t peakIndex;
static uint16_t maxRSSI;
static uint32_t maxFreq;
static uint16_t maxIndex;
static uint16_t pointsPastPeak;
static uint16_t pointsPastDip;
static uint16_t minRSSI; // Minimum level for the sweep
static uint16_t lastMinRSSI; // Minimum level for the previous sweep
static bool resetAverage; // Flag to indicate a setting has changed and average valuesneeds to be reset
static bool jsonDocInitialised = false;
static uint16_t chunkIndex;
static uint32_t offsetIF; // IF frequency offset by half the bandwidth to position in the centre of the filter
static uint32_t tgIF; // Track gen IF - SA IF plus any offset if both SI4432 defined
/*
* If paused and at the start of a sweep then do nothing
*/
if (!sweepStartDone && paused)
return;
/*
* If the "sweepStartDone" flag is false or if the "initSweep" flag is true, we need
* to set things up for the sweep.
*/
if (( !sweepStartDone || initSweep || changedSetting ) )
{
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things
{
//Serial.println("InitSweep or changedSetting");
autoSweepFreqStep = ( setting.ScanStop - setting.ScanStart ) / displayPoints;
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution
requiredRBW10 = setting.Bandwidth10; // and the resolution bandwidth
if ( requiredRBW10 == 0 ) // If the bandwidth is on "Auto" work out the required RBW
requiredRBW10 = (( setting.ScanStop - setting.ScanStart )) / 29000; // 290 points on display, kHz
if ( requiredRBW10 < 26 ) // If it's less than 2.6KHz
requiredRBW10 = 26; // set it to 2.6KHz
if ( requiredRBW10 > 6207 )
requiredRBW10 = 6207;
if ( requiredRBW10 != old_requiredRBW10 )
{
bandwidth = rcvr.SetRBW ( requiredRBW10, &delaytime ); // Set it in the receiver Si4432
old_requiredRBW10 = requiredRBW10;
}
/*
* The FIR filters in the SI4432 are centred above the nominal IF frequency as that is where the signals are meant to be.
* To make sure we are not on the edge of the filters offset the IF frequency down by half the bandwidth
* This needs to be optimised *********
*/
// offsetIF = setting.IF_Freq + setting.Bandwidth10 * 50; // bW10 is in kHz * 10, so * 100-> kHz, halved
offsetIF = setting.IF_Freq + RX_PASSBAND_OFFSET; // half of narrowest RBW
/*
* Need multiple readings for each pixel in the display to avoid missing signals.
* Work out how many points needed for the whole sweep:
*/
sweepPoints = (uint32_t)(( setting.ScanStop - setting.ScanStart ) / bandwidth / 1000.0 * OVERLAP + 0.5); // allow for some overlap (filters will have 3dB roll off at edge) and round up
if ( sweepPoints < displayPoints )
sweepPoints = displayPoints; // At least the right number of points for the display
sweepFreqStep = ( setting.ScanStop - setting.ScanStart ) / sweepPoints; // Step for each reading
if ( setting.Attenuate != old_settingAttenuate )
{
if ( !att.SetAtten ( setting.Attenuate )) // Set the internal attenuator
setting.Attenuate = att.GetAtten (); // Read back if limited (setting.Attenuate was outside range)
old_settingAttenuate = setting.Attenuate;
}
// pre-calculate adjustment for RSSI values
dBadjust = (double)setting.Attenuate - 120.0 + setting.LevelOffset - setting.ExternalGain;
//Serial.printf("SweepLo dBadjust = %f; leveloffset = %f; attenuate = %i, ext gain = %f\n",
// dBadjust, setting.LevelOffset, setting.Attenuate, setting.ExternalGain);
resetAverage = changedSetting;
xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output if wanted
maxGrid = setting.MaxGrid;
minGrid = setting.MinGrid;
#ifdef USE_WIFI
// Vary number of points to send in each chunk depending on delaytime
// A chunk is sent at the end of each sweep regardless
wiFiPoints = wiFiTargetTime / delaytime;
if (wiFiPoints > MAX_WIFI_POINTS)
wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > displayPoints*OVERLAP)
wiFiPoints = displayPoints*OVERLAP;
//Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
if ( numberOfWebsocketClients > 0 )
pushSettings ();
#endif // #ifdef USE_WIFI
#ifdef TG_IF_INSTALLED
if (tgIF_OK && (trackGenSetting.Mode == 1) )
tg_if.TxMode ( trackGenSetting.IF_Drive ); // Set tracking generator IF on
else
tg_if.RxMode();
#endif
#ifdef TG_LO_INSTALLED
if (tgLO_OK && (trackGenSetting.Mode == 1) )
tg_lo.TxMode ( trackGenSetting.LO_Drive ); // Set tracking generator LO on
else
tg_lo.RxMode();
#endif
#if defined(TG_IF_INSTALLED) && defined(TG_LO_INSTALLED)
if (tgLO_OK && tgIF_OK && (trackGenSetting.Mode == 2) ) // tracking gen as signal generator
{
tg_if.SetFrequency ( setting.IF_Freq + trackGenSetting.Offset );
tg_lo.SetFrequency ( setting.IF_Freq + trackGenSetting.Offset + trackGenSetting.Frequency );
tg_lo.TxMode ( trackGenSetting.LO_Drive ); // Set tracking generator LO on
tg_if.TxMode ( trackGenSetting.IF_Drive ); // Set tracking generator IF on
}
#endif
} // initSweep || changedSetting
autoSweepStep = 0; // Set the step counter to zero
sweepStep = 0;
autoSweepFreq = setting.ScanStart; // Set the start frequency.
nextPointFreq = autoSweepFreq + autoSweepFreqStep;
/* Spur reduction offsets the IF from its normal value. LO also has to be offset same amount
* Offset should be more than half the RX bandwidth to ensure spur is still not in the RX filter passband
* but not so big that the frequencies fall outside the SAW filter passband.
* Use the average trace set to minimum to see the result. Spurs if any will be visible
* at different frequencies.
* Real signals will be present at the same frequency, so a min trace will show only real signals
* How well this works depends on how flat the SAW filter (and SI4432 filter) response is
*/
if (setting.Spur && spurToggle) {
uint32_t IF_Shift = requiredRBW10 * 100; // bandwidth in Hz
if (IF_Shift > MAX_IF_SHIFT)
IF_Shift = MAX_IF_SHIFT;
tempIF = offsetIF - IF_Shift;
} else {
tempIF = offsetIF;
}
// track gen IF follows LO if only one SI4432, otherwise its offset by an amount to reduce blow by
if (tgLO_OK)
tgIF = tempIF + trackGenSetting.Offset;
else
tgIF = tempIF;
spurToggle = !spurToggle;
//Serial.printf("tempIF %u, spurOffset=%i, spur:%i, Toggle:%i\n", tempIF, tempIF - setting.IF_Freq, setting.Spur, spurToggle);
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write
{
}
if ( ( lastIF != tempIF ) || initSweep || changedSetting )
{
//Serial.printf("set rcvr Freq get:%u, tempIF:%u\n", rcvr.GetFrequency(), tempIF);
rcvr.SetFrequency ( tempIF ); // Set the RX Si4432 to the IF frequency
lastIF = tempIF;
actualIF = rcvr.GetFrequency();
#ifdef TG_IF_INSTALLED
if (tgIF_OK && (trackGenSetting.Mode == 1) )
tg_if.SetFrequency ( tgIF ); // Set tracking generator IF for the sweep
#endif
}
xmit.SetFrequency ( tempIF + autoSweepFreq ); // set the LO frequency, tempIF is offset if spur reduction on
#ifdef TG_LO_INSTALLED
if (tgLO_OK && (trackGenSetting.Mode == 1) )
tg_lo.SetFrequency ( tgIF + autoSweepFreq ); // Set tracking generator LO
#endif
setFreqMicros = micros(); // Store the time the frequency was changed
/*
* Actual frequency in the SI4432 is rounded and is limited by the possible resolution
*/
actualFreq = xmit.GetFrequency() - actualIF + RX_PASSBAND_OFFSET; // Used for next RSSI command and JSON entry
#ifdef USE_WIFI
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
{
jsonDocument.clear ();
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
else
jsonDocInitialised = false;
#endif // #ifdef USE_WIFI
startFreq = setting.ScanStart + tempIF; // Start freq for the LO
stopFreq = setting.ScanStop + tempIF; // Stop freq for the LO
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
/*
* Copy the values for the peaks (marker positions) to the old versions. No need to
* reset the indicies or frequencies; just the "Level".
*/
for ( int i = 0; i < MARKER_COUNT; i++ )
{
oldPeaks[i].Level = peaks[i].Level;
oldPeaks[i].Index = peaks[i].Index;
oldPeaks[i].Freq = peaks[i].Freq;
peaks[i].Level = 0;
}
DisplayInfo (); // Display axis, top and side bar text
peakLevel = 0; // Reset the peak values for the sweep
peakFreq = 0.0;
peakGain = 100; // Set to higher than gain can ever be
lastMinRSSI = minRSSI;
minRSSI = 300; // Higher than it can be
pointsPastPeak = 0; // Avoid possible peak detection at start of sweep
peakRSSI = 0;
maxRSSI = 0;
sweepStartDone = true; // Make sure this initialize is only done once per sweep
initSweep = false;
changedSetting = false;
if ( setActualPowerRequested )
{
SetPowerLevel ( actualPower );
setActualPowerRequested = false;
// Serial.printf ( "Setting actual Power %f \n", actualPower );
}
lastSweepStartMicros = sweepStartMicros; // Set last time we got here
sweepStartMicros = micros(); // Current time
sweepMicros = sweepStartMicros - lastSweepStartMicros; // Calculate sweep time (no rollover handling)
} // End of "if ( !sweepStartDone ) || initSweep || changedSetting )"
/*
* Here we do the actual sweep. Save the current step and frequencies for the next time
* through, then wait the required amount of time based on the RBW before taking the
* signal strength reading and changing the transmitter (LO) frequency.
*/
uint16_t oldSweepStep = autoSweepStep;
uint32_t oldSweepFreq = actualFreq;
/*
* Wait until time to take the next reading. If a long enough wait left
* then check the touchscreen and Websockets while we are waiting
* to improve response
*/
nowMicros = micros();
while (( nowMicros - setFreqMicros ) < delaytime )
{
if ( ( nowMicros - setFreqMicros + delaytime > MIN_DELAY_WEBSOCKETS ) &&
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
{
// Serial.print("W");
webSocket.loop (); // Check websockets - includes Yield() to allow other events to run
// Serial.println("w");
lastWebsocketMicros = nowMicros;
}
if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish!
UiProcessTouch (); // Check the touch screen
// Serial.println("w");
nowMicros = micros();
}
int rxRSSI = rcvr.GetRSSI (); // Read the RSSI from the RX SI4432
/*
* Note that there are two different versions of the print statement to send the
* RSSI readings to the serial output. You can change which one is commented out.
*
* The first one produces a tab separated list of just the frequency and RSSI
* reading. That format can be easily read inte something like Excel.
*
* The second one produces a listing more fit for human consumption!
*/
if ( showRSSI ) // Displaying RSSI?
{
// Serial.printf ( "%s\t%03d\n",
// FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
Serial.printf ( "Actual IF: %s", FormatFrequency ( rcvr.GetFrequency() ) );
Serial.printf ( " LO Freq: %s", FormatFrequency ( xmit.GetFrequency() ) );
Serial.printf ( " Sweep Freq: %s", FormatFrequency ( autoSweepFreq) );
Serial.printf ( " Actual Freq %s - RSSI: %03d\n",
FormatFrequency ( actualFreq ), rxRSSI ); // Send it to the serial output
}
if ( (numberOfWebsocketClients > 0) || (setting.ShowGain) )
gainReading = GetPreampGain ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains
autoSweepFreq += sweepFreqStep; // Increment the frequency
sweepStep++; // and increment the step count
/*
* Change the transmitter frequency for the next reading and record the time for
* the RBW required settling delay.
*/
uint32_t f = tempIF + autoSweepFreq;
xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read
// Serial.printf("LO Required: %i Actual %i\n", tempIF+autoSweepFreq, xmit.GetFrequency());
#ifdef TG_LO_INSTALLED
if (tgLO_OK && (trackGenSetting.Mode == 1) )
{
tg_lo.SetFrequency ( f + trackGenSetting.Offset ); // Set tracking generator LO
}
#endif
setFreqMicros = micros(); // Store the time the LO frequency was changed
#ifdef TG_LO_INSTALLED
// if (trackGenSetting.Mode == 1)
// Serial.printf("tglo %i @ %i\n", tg_lo.GetFrequency(), setFreqMicros);
#endif
#ifdef USE_WIFI
if ( numberOfWebsocketClients > 0 )
{
if ( jsonDocInitialised )
{
JsonObject dataPoint = Points.createNestedObject (); // Add an object to the Json array to be pushed to the client
dataPoint["x"] = oldSweepFreq/1000000.0; // Set the x(frequency) value
dataPoint["y"] = rxRSSI; // Set the y (RSSI) value
// Serial.printf ( "Add point chunkIndex %u, sweepStep %u of %u \n", chunkIndex, sweepStep, sweepPoints);
chunkIndex++; // increment no of data points in current WiFi chunk
if ( chunkIndex >= wiFiPoints ) // Send the chunk of data and start new jSon document
{
String wsBuffer;
if ( wsBuffer )
{
// Serial.print("D");
serializeJson ( jsonDocument, wsBuffer );
// Serial.printf("J%u", wsBuffer.length() );
unsigned long s = millis();
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
if (millis() - s > 1000)
{
Serial.println("webSocketTimeout");
Serial.println(wsBuffer);
websocketFailCount++;
if (websocketFailCount > 2)
numberOfWebsocketClients = 0;
} else {
websocketFailCount = 0; // reset if OK
}
// Serial.print("j");
}
else
Serial.println("No buffer :(");
}
}
if ( ( chunkIndex >= wiFiPoints ) || !jsonDocInitialised ) // Start new jSon document
{
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
}
/*
* Actual frequency in the SI4432 is rounded and is limited by the possible resolution
*/
actualFreq = xmit.GetFrequency() - actualIF + RX_PASSBAND_OFFSET; // Used for next RSSI command and JSON entry
#endif // #ifdef USE_WIFI
if ( rxRSSI > pointMaxRSSI ) // RSSI > maximum value for this point so far?
{
myActual[autoSweepStep] = rxRSSI; // Yes, save it
pointMaxRSSI = rxRSSI; // Added by G3ZQC - Remember new maximim
pointMaxFreq = oldSweepFreq;
}
if ( gainReading < pointMinGain ) // Gain < minimum gain for this point so far?
{
myGain[autoSweepStep] = gainReading; // Yes, save it
pointMinGain = gainReading; // Added by G3ZQC - Remember new minimum
}
if (rxRSSI < minRSSI) // Detect minimum for sweep
minRSSI = rxRSSI;
/*
* Have we enough readings for this display point? If yes, so do any averaging etc, reset
* the values so peak in the frequency step is recorded and update the display.
*/
if ( autoSweepFreq >= nextPointFreq )
{
nextPointFreq = nextPointFreq + autoSweepFreqStep; // Next display point frequency
autoSweepStep++; // Increment the index
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
DrawCheckerBoard ( oldSweepStep ); // Draw the grid for the point in the sweep we have just read
if ( resetAverage || setting.Average == AV_OFF ) // Store data, either as read or as rolling average
myData[oldSweepStep] = myActual[oldSweepStep];
else
{
switch ( setting.Average )
{
case AV_MIN:
if ( myData[oldSweepStep] > myActual[oldSweepStep] )
myData[oldSweepStep] = myActual[oldSweepStep];
break;
case AV_MAX:
if ( myData[oldSweepStep] < myActual[oldSweepStep] )
myData[oldSweepStep] = myActual[oldSweepStep];
break;
case AV_2:
myData[oldSweepStep] = ( myData[oldSweepStep] + myActual[oldSweepStep] ) / 2;
break;
case AV_4:
myData[oldSweepStep] = ( myData[oldSweepStep]*3 + myActual[oldSweepStep] ) / 4;
break;
case AV_8:
myData[oldSweepStep] = ( myData[oldSweepStep]*7 + myActual[oldSweepStep] ) / 8;
break;
}
DisplayPoint ( myData, oldSweepStep, AVG_COLOR );
}
if ( setting.ShowSweep )
DisplayPoint ( myActual, oldSweepStep, DB_COLOR );
if ( setting.ShowGain )
displayGainPoint ( myGain, oldSweepStep, GAIN_COLOR );
if ( setting.ShowStorage )
DisplayPoint ( myStorage, oldSweepStep, STORAGE_COLOR );
if ( setting.SubtractStorage )
rxRSSI = 128 + rxRSSI - myStorage[oldSweepStep];
/*
* Record the peak values but not if freq low enough to detect the LO
*/
if (( autoSweepFreq > MARKER_MIN_FREQUENCY ) && (oldSweepStep > 0))
{
if ( maxRSSI <= myActual[oldSweepStep] )
{
maxIndex = oldSweepStep;
maxRSSI = myActual[oldSweepStep];
maxFreq = oldSweepFreq;
// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep);
// displayPeakData ();
}
/*
* Save values used by peak detection. Need to save the previous value as we only
* know we have a peak once past it!
*/
prevPointRSSI = currentPointRSSI;
currentPointRSSI = myData[oldSweepStep];
/*
* Peak point detection. Four peaks, used to position the markers
*/
if ( currentPointRSSI >= prevPointRSSI ) // Level or ascending
{
pointsPastDip ++;
if ( pointsPastDip == PAST_PEAK_LIMIT )
{
pointsPastPeak = 0;
}
if ( currentPointRSSI > peakRSSI )
{
peakRSSI = currentPointRSSI; // Store values
peakFreq = oldSweepFreq;
peakIndex = oldSweepStep;
}
}
else
{
pointsPastPeak ++; // only a true peak if value decreased for a number of consecutive points
if ( pointsPastPeak == PAST_PEAK_LIMIT ) // We have a peak
{
pointsPastDip = 0;
/*
* Is this peak bigger than previous ones? Only check if bigger than smallest peak so far
*/
if ( peakRSSI > peaks[MARKER_COUNT-1].Level )
{
for ( uint16_t p = 0; p < MARKER_COUNT; p++ )
{
if ( peakRSSI > peaks[p].Level )
{
for ( uint16_t n = 3; n > p; n-- ) // Shuffle lower level peaks down
memcpy ( &peaks[n], &peaks[n-1], sizeof ( peak_t ));
peaks[p].Level = peakRSSI; // Save the peak values
peaks[p].Freq = peakFreq;
peaks[p].Index = peakIndex;
break;
}
}
}
peakRSSI = 0; // Reset peak values ready for next peak
} // We have a peak
} // Descending
} // if (( autoSweepFreq > 1000000 ) && (oldSweepStep > 0))
/*
* Draw the markers if main sweep is displayed. The markers know if they are enabled or not
* Only paint if sweep step is in range where there will be a marker
*/
if ( setting.ShowSweep || setting.Average != AV_OFF )
{
for ( int p = 0; p < MARKER_COUNT; p++ )
if (( abs ( oldSweepStep - oldPeaks[p].Index )
<= MARKER_SPRITE_HEIGHT / 2 ) && ( oldPeaks[p].Level > (lastMinRSSI + MARKER_NOISE_LIMIT) ))
marker[p].Paint ( &img, oldPeaks[p].Index - oldSweepStep,
rssiToImgY ( oldPeaks[p].Level ) );
}
// If in the last few points and gain trace is displayed show the gain scale
if ( setting.ShowGain && (oldSweepStep > displayPoints - 2 * CHAR_WIDTH) )
{
int16_t scaleX = displayPoints - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite
img.setPivot( scaleX, 0);
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour
}
if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( xOrigin+oldSweepStep-1, yOrigin );
myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
} // End of "if ( autoSweepFreq >= nextPointFreq )"
if ( sweepStep >= sweepPoints ) // If we have got to the end of the sweep
{
// autoSweepStep = 0;
sweepStartDone = false;
resetAverage = false;
if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start
// Serial.printf("MaxRSSI = %i, freq = %i\n", maxRSSI, maxFreq);
oldPeakLevel = maxRSSI; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[displayPoints-1] == 0 ) // Ensure a value in last data point
{
myActual[displayPoints-1] = rxRSSI; // Yes, save it
myGain[displayPoints-1] = gainReading;
myFreq[displayPoints-1] = oldSweepFreq;
}
if ( showRSSI == 1 ) // Only show it once?
showRSSI = 0; // Then turn it off
#ifdef USE_WIFI
if (( numberOfWebsocketClients > 0) && jsonDocInitialised && (chunkIndex > 0) )
{
String wsBuffer;
if (wsBuffer)
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
}
#endif // #ifdef USE_WIFI
} // End of "if ( sweepStep >= sweepPoints )"
} // End of "doSweepLow"