ESP8266 request multiple HTTP GET simultaneously - ajax

I started my first project with the ESP8266.
It's a Temperature Monitor which shows the data on a webserver.
Since I don't want to refresh the page manually to get the new data, I'm using HTTP requests to display it.
I'm sending 3 different requests, one for the current temperature, one for the highest and one for the lowest.
The problem i'm facing is, that the data won't refresh simultaneously, though I am sending all of those at the same time.
That's the code that's sending the requests:
<script>
function getCurrent() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("current").innerHTML =
this.responseText;
}
};
xhttp.open("GET", "readCurrent", true);
xhttp.send();
}
function getHigh() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("high").innerHTML =
this.responseText;
}
};
xhttp.open("GET", "readHigh", true);
xhttp.send();
}
function getLow() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("low").innerHTML =
this.responseText;
}
};
xhttp.open("GET", "readLow", true);
xhttp.send();
}
setInterval(function() {
getHigh();
getLow();
getCurrent();
}, 2000);
</script>
And that's the code handling them:
float temp;
float hoechst;
float tiefst;
void handleRoot() {
String s = MAIN_page; //Read HTML contents
server.send(200, "text/html", s); //Send web page
}
void handleCurrent() {
float t = temp;
server.send(200, "text/plane", String(t));
}
void handleHigh() {
float high = hoechst;
server.send(200, "text/plane", String(high));
}
void handleLow() {
float low = tiefst;
server.send(200, "text/plane", String(low));
}
void setup() {
[...]
server.on("/", handleRoot);
server.on("/readCurrent", handleCurrent);
server.on("/readHigh", handleHigh);
server.on("/readLow", handleLow);
[...]
}
The Loop is just updating the Temperatures with this function:
void updateTemperatures() {
sensor.requestTemperatures();
yield();
float low = tiefst;
float high = hoechst;
float t = sensor.getTempCByIndex(0);
if(t < low) {
low = t;
} else if(t > high) {
high = t;
}
yield();
temp = t;
tiefst = low;
hoechst = high;
}
And handling the clients (server.handleClient())
So my Question: How can I update the data simultaneously, or is that even possible with the ESP8266?

You update the data simultaneously by returning all three values in one request.
That would be the way to do it with any web server, let alone one running on an extremely limited processor like the ESP8266.
You can return all three values at once with code that looks something like this:
void handleAll() {
String results_json = "{ \"temperature\": " + String(temp) + ",", +
"\"high\": " + String(hoechst) + "," +
"\"low\": " + String(tiefst) + " }";
server.send(200, "application/json", results_json);
}
This composes a JSON object with all three values in it. JSON is "JavaScript Object Notation" and is very easy for Javascript to put together and take apart.
You'd also need to update your ESP8266 web server code to add
server.on("/readAll", handleAll);
With this change you can eliminate the other three /read handlers.
And you'd need to update your Javascript. You'd just need to do one call in Javascript, convert the returned text to a Javascript object and read each of the three values from it to set the elements in the DOM. This is something jQuery can so trivially for you.
And, it's 'text/plain', not 'text/plane'.
You might also check out jQuery - it will greatly simplify your Javascript code.

Simply put: You can't update the data simultaneously because there's only one CPU core. Also, you should design with economy in mind, you wanted to have three transactions to get a few numbers... One of the simplest forms of database is CSV, or "Comma-Separated Values"; essentially: values separated by commas.
Knowing the order your temperatures are going to be in the list (low, current, high), you can simply create a new variable, or concatenate your variables in the statement that outputs the data, that's low "," current "," high and that would return you something like -1.23455,23.53556,37.23432 that you can easily split into three by looking for the commas, and using string operators.
Now you can get your three values from a single transaction from the low-spec device!
Good luck! : )

Related

EWS The server cannot service this request right now

I am seeing errors while exporting email in office 365 account using ews managed api, "The server cannot service this request right now. Try again later." Why is that error occurring and what can be done about it?
I am using the following code for that work:-
_GetEmail = (EmailMessage)item;
bool isread = _GetEmail.IsRead;
sub = _GetEmail.Subject;
fold = folder.DisplayName;
historicalDate = _GetEmail.DateTimeSent.Subtract(folder.Service.TimeZone.GetUtcOffset(_GetEmail.DateTimeSent));
props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(_source, item.Id, props);
bytes = new byte[email.MimeContent.Content.Length];
fs = new MemoryStream(bytes, 0, email.MimeContent.Content.Length, true);
fs.Write(email.MimeContent.Content, 0, email.MimeContent.Content.Length);
Demail = new EmailMessage(_destination);
Demail.MimeContent = new MimeContent("UTF-8", bytes);
// 'SetExtendedProperty' used to maintain historical date of items
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(57, MapiPropertyType.SystemTime), historicalDate);
// PR_MESSAGE_DELIVERY_TIME
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(3590, MapiPropertyType.SystemTime), historicalDate);
if (isread == false)
{
Demail.IsRead = isread;
}
if (_source.RequestedServerVersion == flagVersion && _destination.RequestedServerVersion == flagVersion)
{
Demail.Flag = _GetEmail.Flag;
}
_lstdestmail.Add(Demail);
_objtask = new TaskStatu();
_objtask.TaskId = _taskid;
_objtask.SubTaskId = subtaskid;
_objtask.FolderId = Convert.ToInt64(folderId);
_objtask.SourceItemId = Convert.ToString(_GetEmail.InternetMessageId.ToString());
_objtask.DestinationEmail = Convert.ToString(_fromEmail);
_objtask.CreatedOn = DateTime.UtcNow;
_objtask.IsSubFolder = false;
_objtask.FolderName = fold;
_objdbcontext.TaskStatus.Add(_objtask);
try
{
if (counter == countGroup)
{
Demails = new EmailMessage(_destination);
Demails.Service.CreateItems(_lstdestmail, _destinationFolder.Id, MessageDisposition.SaveOnly, SendInvitationsMode.SendToNone);
_objdbcontext.SaveChanges();
counter = 0;
_lstdestmail.Clear();
}
}
catch (Exception ex)
{
ClouldErrorLog.CreateError(_taskid, subtaskid, ex.Message + GetLineNumber(ex, _taskid, subtaskid), CreateInnerException(sub, fold, historicalDate));
counter = 0;
_lstdestmail.Clear();
continue;
}
This error occurs only if try to export in office 365 accounts and works fine in case of outlook 2010, 2013, 2016 etc..
Usually this is the case when exceed the EWS throttling in Exchange. It is explain in here.
Make sure you already knew throttling policies and your code comply with them.
You can find throttling policies using Get-ThrottlingPolicy if you have the server.
One way to solve the throttling issue you are experiencing is to implement paging instead of requesting all items in one go. You can refer to this link.
For instance:
using Microsoft.Exchange.WebServices.Data;
static void PageSearchItems(ExchangeService service, WellKnownFolderName folder)
{
int pageSize = 5;
int offset = 0;
// Request one more item than your actual pageSize.
// This will be used to detect a change to the result
// set while paging.
ItemView view = new ItemView(pageSize + 1, offset);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
view.Traversal = ItemTraversal.Shallow;
bool moreItems = true;
ItemId anchorId = null;
while (moreItems)
{
try
{
FindItemsResults<Item> results = service.FindItems(folder, view);
moreItems = results.MoreAvailable;
if (moreItems && anchorId != null)
{
// Check the first result to make sure it matches
// the last result (anchor) from the previous page.
// If it doesn't, that means that something was added
// or deleted since you started the search.
if (results.Items.First<Item>().Id != anchorId)
{
Console.WriteLine("The collection has changed while paging. Some results may be missed.");
}
}
if (moreItems)
view.Offset += pageSize;
anchorId = results.Items.Last<Item>().Id;
// Because you’re including an additional item on the end of your results
// as an anchor, you don't want to display it.
// Set the number to loop as the smaller value between
// the number of items in the collection and the page size.
int displayCount = results.Items.Count > pageSize ? pageSize : results.Items.Count;
for (int i = 0; i < displayCount; i++)
{
Item item = results.Items[i];
Console.WriteLine("Subject: {0}", item.Subject);
Console.WriteLine("Id: {0}\n", item.Id.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine("Exception while paging results: {0}", ex.Message);
}
}
}

Create map after get information of location through AJAX

I need to get the longitude and latitude of a location from a text file through AJAX then create a map using that information. This is what I have done:
function createMap(){
var request;
if (window.XMLHttpRequest)
{
request = new XMLHttpRequest();
}
if (request)
{
var number = Math.floor( (Math.random() * 3) + 1 );
var url = "text_" + number + ".txt";
request.open("GET", url,true);
request.onreadystatechange = function()
{
if (request.readyState == 4 && request.status == 200)
{
var syd=new google.maps.LatLng(-33.884183, 151.214944);
var woll=new google.maps.LatLng(request.responseText);
function initialize()
{
var mapProp = {
center:syd,
zoom:6,
mapTypeId:google.maps.MapTypeId.ROADMAP
};
var map=new google.maps.Map(document.getElementById("outputAJAX"),mapProp);
var myTrip=[syd,woll];
var flightPath=new google.maps.Polyline({
path:myTrip,
strokeColor:"#0000FF",
strokeOpacity:0.8,
strokeWeight:2
});
flightPath.setMap(map);
}
initialize();
}
}
request.send();
} else {
document.getElementById("outputAJAX").innerHTML = "<span style='font-weight:bold;'>AJAX</span> is not supported by this browser!";
}}
However, the map didn't show up. Do you guys have any suggestion?
A google.maps.LatLng object takes two Numbers as its arguments:
LatLng(lat:number, lng:number, noWrap?:boolean) Creates a LatLng object representing a geographic point. Latitude is specified in degrees within the range [-90, 90]. Longitude is specified in degrees within the range [-180, 180]. Set noWrap to true to enable values outside of this range. Note the ordering of latitude and longitude.
This won't work (you are only passing one argument, a string):
var woll=new google.maps.LatLng(request.responseText);
Assuming request.responseText is a comma separated string containing two numeric values, this should work:
var coordStr = request.responseText;
var coords = coordStr.split(",");
var woll = new google.maps.LatLng(parseFloat(coords[0]),
parseFloat(coords[1]));
proof of concept fiddle

How to modify http response in Firefox extension

Hey i have been able to write an nsIStreamListener listener to listen on responses and get the response text following tutorials at nsitraceablechannel-intercept-http-traffic .But i am unable to modify the response sent to browser.Actually if i return the reponse and sent back to chain it reflects in firebug but not in browser.
What i am guessing is we will have to replace default listener rather than listening in the chain.I cant get any docs anywhere which explains how to do this.
Could anyone give me some insight into this.This is mainly for education purposes.
Thanks in advance
Edit : As of now i have arrived at a little solutions i am able to do this
var old;
function TracingListener() {}
TracingListener.prototype = {
originalListener: null,
receivedData: null, //will be an array for incoming data.
//For the listener this is step 1.
onStartRequest: function (request, context) {
this.receivedData = []; //initialize the array
//Pass on the onStartRequest call to the next listener in the chain -- VERY IMPORTANT
//old.onStartRequest(request, context);
},
//This is step 2. This gets called every time additional data is available
onDataAvailable: function (request, context, inputStream, offset, count) {
var binaryInputStream = CCIN("#mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream");
binaryInputStream.setInputStream(inputStream);
var storageStream = CCIN("#mozilla.org/storagestream;1",
"nsIStorageStream");
//8192 is the segment size in bytes, count is the maximum size of the stream in bytes
storageStream.init(8192, count, null);
var binaryOutputStream = CCIN("#mozilla.org/binaryoutputstream;1",
"nsIBinaryOutputStream");
binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));
// Copy received data as they come.
var data = binaryInputStream.readBytes(count);
this.receivedData.push(data);
binaryOutputStream.writeBytes(data, count);
//Pass it on down the chain
//old.onDataAvailable(request, context,storageStream.newInputStream(0), offset, count);
},
onStopRequest: function (request, context, statusCode) {
try {
//QueryInterface into HttpChannel to access originalURI and requestMethod properties
request.QueryInterface(Ci.nsIHttpChannel);
//Combine the response into a single string
var responseSource = this.receivedData.join('');
//edit data as needed
responseSource = "test";
console.log(responseSource);
} catch (e) {
//standard function to dump a formatted version of the error to console
dumpError(e);
}
var stream = Cc["#mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
stream.setData(responseSource, -1);
//Pass it to the original listener
//old.originalListener=null;
old.onStartRequest(channel, context);
old.onDataAvailable(channel, context, stream, 0, stream.available());
old.onStopRequest(channel, context, statusCode);
},
QueryInterface: function (aIID) {
if (aIID.equals(Ci.nsIStreamListener) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw components.results.NS_NOINTERFACE;
},
readPostTextFromRequest: function (request, context) {
try {
var is = request.QueryInterface(Ci.nsIUploadChannel).uploadStream;
if (is) {
var ss = is.QueryInterface(Ci.nsISeekableStream);
var prevOffset;
if (ss) {
prevOffset = ss.tell();
ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
}
// Read data from the stream..
var charset = "UTF-8";
var text = this.readFromStream(is, charset, true);
if (ss && prevOffset == 0)
ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
return text;
} else {
dump("Failed to Query Interface for upload stream.\n");
}
} catch (exc) {
dumpError(exc);
}
return null;
},
readFromStream: function (stream, charset, noClose) {
var sis = CCSV("#mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream");
sis.setInputStream(stream);
var segments = [];
for (var count = stream.available(); count; count = stream.available())
segments.push(sis.readBytes(count));
if (!noClose)
sis.close();
var text = segments.join("");
return text;
}
}
httpRequestObserver = {
observe: function (request, aTopic, aData) {
if (typeof Cc == "undefined") {
var Cc = components.classes;
}
if (typeof Ci == "undefined") {
var Ci = components.interfaces;
}
if (aTopic == "http-on-examine-response") {
request.QueryInterface(Ci.nsIHttpChannel);
console.log(request.statusCode);
var newListener = new TracingListener();
request.QueryInterface(Ci.nsITraceableChannel);
channel = request;
//newListener.originalListener
//add new listener as default and save old one
old = request.setNewListener(newListener);
old.originalListener = null;
var threadManager = Cc["#mozilla.org/thread-manager;1"]
.getService(Ci.nsIThreadManager);
threadManager.currentThread.dispatch(newListener, Ci.nsIEventTarget.DISPATCH_NORMAL);
}
},
QueryInterface: function (aIID) {
if (typeof Cc == "undefined") {
var Cc = components.classes;
}
if (typeof Ci == "undefined") {
var Ci = components.interfaces;
}
if (aIID.equals(Ci.nsIObserver) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw components.results.NS_NOINTERFACE;
},
};
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.addObserver(httpRequestObserver,
"http-on-examine-response", false);
This example works for me on Firefox 34 (current nightly): https://github.com/Noitidart/demo-nsITraceableChannel
I downloaded the xpi, edited bootstrap.js to modify the stream:
132 // Copy received data as they come.
133 var data = binaryInputStream.readBytes(count);
134 data = data.replace(/GitHub/g, "TEST");
135 this.receivedData.push(data);
installed the XPI then reloaded the github page. It read "TEST" in the footer.
The version of code you posted doesn't actually pass the results back to the old listener, so that's the first thing that ought to be changed.
It also may have interacted with Firebug or another extension badly. It's a good idea to try reproducing the problem in a clean profile (with only your extension installed).

Asynchronous progress bar on a loop

Using MVC3, C#, jQuery, Ajax ++
My html
<div>
Start Long Running Process
</div>
<br />
<div id="statusBorder">
<div id="statusFill">
</div>
</div>
The javascript part part of the html
var uniqueId = '<%= Guid.NewGuid().ToString() %>';
$(document).ready(function (event) {
$('#startProcess').click(function () {
$.post("SendToDB/StartLongRunningProcess", { id: uniqueId,
//other parameters to be inserted like textbox
}, function () {
$('#statusBorder').show();
getStatus();
});
event.preventDefault;
});
});
function getStatus() {
var url = 'SendToDB/GetCurrentProgress';
$.get(url, function (data) {
if (data != "100") {
$('#status').html(data);
$('#statusFill').width(data);
window.setTimeout("getStatus()", 100);
}
else {
$('#status').html("Done");
$('#statusBorder').hide();
alert("The Long process has finished");
};
});
}
This is the controller.
//Some global variables. I know it is not "good practice" but it works.
private static int _GlobalSentProgress = 0;
private static int _GlobalUsersSelected = 0;
public void StartLongRunningProcess(string id,
//other parameters
)
{
int percentDone = 0;
int sent = 0;
IEnumerable<BatchListModel> users;
users = new UserService(_userRepository.Session).GetUsers(
//several parameters)
foreach (var c in users)
{
var usr = _userRepository.LoadByID(c.ID);
var message = new DbLog
{
//insert parameters
};
_DbLogRepository.Save(message);
sent++;
double _GlobalSentProgress = (double)sent / (double)_GlobalUsersSelected * 100;
if (percentDone < 100)
{
percentDone = Convert.ToInt32(_GlobalSentProgress);
}
//this is supposed to give the current progress to the "GetStatus" in the javascript
public int GetCurrentProgress()
{
return _GlobalSentProgress;
}
Right now the div with the progress bar never shows up. It is honestly kind of broken. But I hope you understand my logic.
In the loop doing the insertions, I do have this calculation:
double _GlobalSentProgress = (double)sent / (double)_GlobalUsersSelected * 100;
Then I convert the _GlobalSentProgress to a normal int in the
percentDone = Convert.ToInt32(_GlobalSentProgress);
so it no longer has any decimals any longer.
If only I could send this "percentDone" or "_GlobalSentProgress" variable (wich is showing perfectly how many percent I have come in the insertion) asynchronous into the "data" variable in javascript every single time it loops, it would work. Then "data" would do it's "statusFill" all the time and show the bar correctly. This is the logic I use.
I believe the word thrown around in order to accomplish this is "asynchronous". I have looked at 2 very promising guides but I was not able to make it work with my loop.
Anyone have a suggestion on how I can do this?
Edit 2: Outer div is named statusBorder not status.

AJAX POST Request Only Works Once in Safari 5

I use my own custom AJAX library (I'm not interested in using jQuery, etc.), which is working flawlessly in the following browsers:
Firefox 7
Chrome 14
IE 8
IE 8 (compatibility mode)
Using my custom AJAX library in the aforementioned browsers, I can make as many AJAX requests as I want, in any order, using GET and/or POST methods, and they all work flawlessly. Since a new AJAX object is created for every request (see code below), I can even have more than one AJAX request process simultaneously with success.
However, in Safari 5 an AJAX POST request only passes POST data to the server if it is the absolute first AJAX request to execute. Even if I execute the exact same AJAX POST request twice in a row, the POST data is only passed to the server during the first request. Here is the JavaScript in my custom AJAX library:
if (!Array.indexOf)
{
Array.prototype.indexOf = function(obj) { for (var i = 0; i < this.length; i++) { if (this[i] == obj) { return i; } } return -1; };
}
function ajaxObject()
{
if (window.ActiveXObject)
{
var activexmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
for (var i = 0; i < activexmodes.length; i++)
{
try
{
return new ActiveXObject(activexmodes[i]);
}
catch (e)
{
}
}
}
else if (window.XMLHttpRequest)
{
return new XMLHttpRequest();
}
else
{
return false;
}
}
function ajaxRequest(aURI, aContainerId, aPostData, aResponseType, aAvoidBrowserCache)
{
// Initialize
var xmlhttp = new ajaxObject();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
if (aResponseType != "eval" && aResponseType != "EVAL")
{
// Show HTML for response
document.getElementById(aContainerId).innerHTML = xmlhttp.responseText;
}
else
{
// Parse & execute JavaScript for response
var responseText = xmlhttp.responseText;
var startPos, endPos;
for (var i = 0; i < responseText.length; i++)
{
if (responseText.substring(i, i + 6) == "<eval>")
{
startPos = i + 6;
break;
}
}
for (var i = startPos; i < responseText.length; i++)
{
if (responseText.substring(i, i + 7) == "</eval>")
{
endPos = i;
break;
}
}
textToEval = responseText.substring(startPos, endPos);
eval(textToEval);
}
}
else
{
try
{
if (xmlhttp.status != 0 && xmlhttp.status != 200)
{
alert('Error ' + xmlhttp.status);
}
}
catch (e)
{
// Handle IE8 debug "unknown error"
}
}
}
if (aAvoidBrowserCache != false)
{
// Combat browser caching:
aURI = aURI + (aURI.indexOf("?") == -1 ? "?" : "&");
theTime = new Date().getTime();
aURI = aURI + theTime + "=" + theTime;
}
// Make request
if (typeof aPostData == "undefined" || aPostData == null || aPostData == "")
{
// GET request
xmlhttp.open("GET", aURI, true);
xmlhttp.send();
}
else
{
// POST request
var parameters = "";
if (aPostData.constructor.toString().indexOf("Array") != -1)
{
// Use parameters passed as array
for (var postCount = 0; postCount < aPostData.length; postCount++)
{
if (parameters != "")
{
parameters = parameters + "&";
}
parameters = parameters + aPostData[postCount][0] + "=" + encodeURIComponent(aPostData[postCount][1]);
}
}
else
{
// Use parameters passed as string
parameters = aPostData;
}
xmlhttp.open("POST", aURI, true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(parameters);
}
}
So for example, either of the following AJAX POST requests will pass POST data if they are the absolute first AJAX request (whether GET or POST); otherwise, the POST data is not passed:
ajaxRequest("test.aspx", "", [["name1","value1"],["name2","value2"]], "eval");
or
ajaxRequest("test.aspx", "", "name1=value1&name2=value2", "eval");
I have added debug statements all throughout my AJAX library, and the POST parameters are being created in the "parameters" variable as expected prior to each POST request. I have absolutely no idea why, only in Safari 5 (out of the mentioned browsers), I have this problem. Any ideas?
Thanks in advance!
Jesse
The reason the call is failing is because of a bug in Safari when working with Windows Authentication under IIS. Go to the Authentication settings of your website. Right click on Windows Authentication, choose providers and remove Negotiate, leaving NTLM which works fine. I haven't tested Kerberos.
This issue only appears in certain builds of safari.
Came here from the thread you mentioned might be a dupe. I never solved our problem, but have you tried a simple page making post requests? With our issue it's a post problem, not an AJAX problem, we're still stumped though.
What version of IIS are you running on the server?
I can confirm that the problem seems related to some sort of interaction between Safari & IIS. Luckily, I only develop and test this portion of the code on Windows. I moved it unchanged to a LAMP (Linux/Apache) staging server (prior to moving to our LAMP production server) and the problem went away. I was seeing the problem with Safari 5, IIS 5.1, & an ActiveState Perl 5.6 CGI.
Under RHEL 5, Apache 2.2, & Perl 5.8, it is gone.

Resources