-
• #2
Fri 2020.01.17
'if power if removed long enough, it will start without errors on the next boot'
'Why should the device act differently immediately after a 'save()''Has reading over the following answered your concern?
http://www.espruino.com/Saving
Quick Start Best Practices - Proper way to call init() on start up
Short answer, the onInit() function does not execute when just uploading.re: 'it will start without errors on the next boot'
This would be expected, as after executing save(), the same onInit() function will execute on power up.
-
• #3
Sorry, I need to make it a little more clear. It actually doesn't start without errors on the next boot. In fact, most of the time, a power-cycle will not cause things to work. Every now and then however, IT DOES. This happens so unpredictably that I have not been able to nail down any repeatable behavior, such as a minimum amount of down time.
Now, originally, I did have the wifi.on('connected' event inside the onInit() function. Then in an attempt to simplify things, I moved it out. I just put that event handler back in the onInit() on the assumption that it needed to be registered again after a reboot.
No such luck... sometimes things work, sometime not after a boot. But the one thing I have not ever seen fail is proper operation after typing save() on the left, even if there have been absolutely no changes to the code.
If it will help, here is the entirety of the code in question:
var wifi = require("Wifi"); var bme; var mqtt; var temp; var pressure; var humidity; function onInit() { wifi.startAP('AgilatechESP', { password: 'xxxxxxxx', authMode: 'wpa2' }, (err) => { if (err) throw err; else wifi.connect("HeartJS", {password : "xxxxxxxxxx"}, (err) => { if (err) throw err; }); }); SPI1.setup({mosi:A7, miso:A6, sck:A5}); bme = require("BME280").connectSPI(SPI1, B1); wifi.on('connected', () => { require("http").createServer(pageReq).listen(8080); mqtt = require("MQTT").connect({ host: "xxx.xxx.xxx.xxx" }); loadData(); dataInterval(); }); } function pageReq(req, res) { const reqdat = url.parse(req.url, true); var ret = ""; if (reqdat.path === '/') { ret = `Temperature: ${tempF}, Pressure: ${pressure}, Humidity: ${humidity}`; //ret = "Hello World"; } res.writeHead(200); res.end(ret); } function seaLevelPressure(pressure_mb, elevation, temp) { return pressure_mb * Math.pow((1 - ((0.0065 * elevation) / (temp + 0.0065 * elevation + 273.15))), -5.257); } function dataInterval() { var timer = setInterval( () => { loadData(); }, 600000); } function loadData() { let data = bme.getData(); data.pressure = seaLevelPressure(data.pressure, 1750, data.temp); pressure = Math.round(data.pressure * 100) / 100; temp = Math.round(data.temp * 10) / 10; tempF = Math.round(((data.temp * 9/5) + 32) * 10) / 10; humidity = Math.round(data.humidity * 10) / 10; }
-
• #4
Hi,
nice work, thanks for sharing.
Check this conversation to get in touch with onInit() and save()
code observation:
- add tempf as global var
- place wifi.disconnect and wifi.stopAP() as first wifi calls to catch the event
- if needed use res.writeHead(200, {'Content-Type': 'text/html'})
- add tempf as global var
-
• #5
Sat 2020.01.18
@ScottyMT, I have no experience with MQTT, but does this article and code block provide some insight?
I realize your project is using a WiFi but this is as close I could find
MQTT and Espruino
Although this is a really old version of Espruino, and it's content not directly ralated to the MQTT task at hand, does the use of these snippets provide confirmation for at least the WiFi configuration, when MQTT is not referenced in your code?
Could there be some delay setting up SPI after the WiFi init, causing lost packets? After reading the referenced pages here, an untested suggestion might be to set up SPI, then call the WiFi section from asetTimeout()
with say a one and a half second delay?or/and . . .
Try the 'hello.txt' example code block at page end to separate the WiFi from MQTT task, proving out the WiFi code block and gain some hardware confidence.
Has the use of the 'debugger' been tried? looking for null and undefined objectsIt will be necessary to use the @MaBe conditional example (post #2 there) found following his link in the #4 post above, rather than save() each time.
-
• #6
I appreciate all the help and suggestions.
The main source of frustration was the unpredictability of it -- sometimes things would init and work, sometimes not. However, after reading through all the referenced forum threads, and what @Robin mentioned about a delay setting up SPI, I thought maybe I'm expecting too much from the little guy on startup.
I am starting WiFi, connecting to a router, starting a simultaneous AP, starting a SPI device, initiating an MQTT module and connecting it to a broker. There are quite likely several opportunities for conflict in there. Sometimes, however, things did happen to start and finish with such timing that it all works. Usually though, that was not the case.
What I tried is to wrap every startup/connection/instantiation in a delay setTimeout. Is this overkill? Maybe, but it does seem to work, and only delays the complete initiation by a few seconds. But best of all, it now starts up with success every time, all the time, whether on battery, or hooked to USB.
Thanks again for the advice.
-
• #7
Sun 2020.01.19
Glad it is all working @ScottyMT
Clarification here
'mentioned about a delay setting up SPI'
'to wrap every startup/connection/instantiation in a delay setTimeout. Is this overkill?'While it may not have been entirely clear, the reference to a delay in post #5 was directed at setting up SPI after the WiFi init taking time, not a delay in the setup of SPI. As you astutely pointed out "There are quite likely several opportunities for conflict," the WiFi module would have to be fetched, the connect task and setup of the AP would take a bit of time, and at the same time SPI setup, and MQTT broker init were being performed.
It may not have been clear that using multiple setTimeout()'s was the intent. The pseudo code: Within the init function, start SPI, inside one timeout task, init and start the AP, inside a separate timeout, init and start the connection to the MQTT broker. In that way, each timeout could be tweaked to get the optimum start delay minimum.
Remember that Espruino will execute the setTimeout() code lines sequentially immediately, that is there is no wait for the timeout to occur before the next code line will execute, so each subsequent timeout value will have to be slightly longer, so they actually stagger an actual half second or so later.
-
• #8
I'm not 100% sure but I think it's possible that you may be getting two WiFi 'connected' events from the two different WiFi connections. In that case what'd be happening is the EspruinoWiFi would be not only trying to create an HTTP server on the same port twice, but the first time it'd be doing it at the same time as connecting to a WiFi access point.
Do you think you could try this instead of your existing onInit and see if it helps? I added a few extra delays - they may not all be required but I guess it's possible they would help.
The main thing though was removing the 'connected' handler and just calling it manually after the second connection was made.
function connectMQTT() { require("http").createServer(pageReq).listen(8080); setTimeout(function() { mqtt = require("MQTT").connect({ host: "xxx.xxx.xxx.xxx" }); loadData(); dataInterval(); }, 2000); } function onInit() { SPI1.setup({mosi:A7, miso:A6, sck:A5}); bme = require("BME280").connectSPI(SPI1, B1); setTimeout(function() { // After 2 seconds, connect wifi.startAP('AgilatechESP', { password: 'xxxxxxxx', authMode: 'wpa2' }, (err) => { if (err) throw err; else wifi.connect("HeartJS", {password : "xxxxxxxxxx"}, (err) => { if (err) throw err; connectMQTT(); }); }); }, 2000); }
While it might make some sense to do stuff on the 'connected' handler (for instance if WiFi dropped out and then reconnected) you'd need to make sure you handle it properly. Right now you'd end up creating a new interval with dataInterval each time you reconnected without properly removing the old one first.
Hope that's some help!
-
• #9
Thanks for the help. It does seem that the wifi.on('connected' event is only triggered once, and only on wifi.connect and not wifi.startAP (based on login inside the event handler).
Just from a design perspective, it does make sense to do certain things if and only if the wifi actually made a successful connection, so that's why I've stuck with keeping things inside the handler. Also, now with the into delays, everything has been working 100% of the time.
Here's what I finally settled on (relevant functions only):
function onInit() { SPI1.setup({mosi:A7, miso:A6, sck:A5}); bme = require("BME280").connectSPI(SPI1, B1); setTimeout(startApAndStation, 2000); wifi.on('disconnected', () => { mqtt.disconnect(); wifi.stopAP(); clearInterval(timer); }); wifi.on('connected', () => { setTimeout(() => { require("http").createServer(pageReq).listen(8080); }, 2000); setTimeout(() => { mqtt = require("MQTT").connect({host: "xxx.xxx.xxx.xxx"}); }, 4000); setTimeout (() => { dataInterval(); }, 8000); }); } function startApAndStation() { wifi.startAP('AgilatechESP', { password: 'xxxxxxxx', authMode: 'wpa2' }, (err) => { if (err) throw err; else wifi.setAPIP({ip:"10.0.132.1", gw:"10.0.132.1", netmask:"255.255.255.0"}, (err) => { if (err) throw err; else wifi.connect("HeartJS", {password : "xxxxxxxxxx"}, (err) => { if (err) throw err; }); }); }); } function dataInterval() { timer = setInterval( () => { loadData(); mqtt.publish("agt/sensor/bme280/temp", temp); mqtt.publish("agt/sensor/bme280/pressure", pressure); mqtt.publish("agt/sensor/bme280/humidity", humidity); }, 10000); }
-
• #10
Mon 2020.01.20
I like the improved, reordered layout of the code block in revison at #9 post. Nicely done example for others requiring MQTT.
ref last pp. of post #7
It should be noted that, L6 and L15 will execute within microseconds of each other, not 2 seconds apart, attempting to force Espruino into potential WiFi setup conflict. This can be proven using the debugger and WebIDE to reveal this fact.
EDIT: Tue 2020.01.21 clarification regarding Gordon's comment in #11 at least one person is actually reading and following along ;-) Edits crossed paths and left out the qualifier: 'should the 'connected' event L15 occur near simultaneously after the first L6 setTimeout() interval has started' This would then be attempting to set up the AP at the approximate same time the server is attempted to be set up. A lot to do with WiFi simultaneously! Although that reference wasn't the perfect example, my point shouldn't be overlooked. Two seconds is not passing waiting for L8 and then L14. During development, I get around this by using prime numbers with trailing '000' so they most likely won't overlap and as to more easily track which timer trips and in which order. Having identical interval values will cloud that understanding.
The code block suggestion that @Gordon graciously whipped together in post #8 was what I implied in the pseudo code reference in 2nd to last pp. in #7 post.
Here is an excellent explanation:
Although your setup may work satisfactory, using the logic layout in Gordon's code block and tweaking the timeouts, should allow for a solid working state in around a second or so, and not the ten seconds currently required.
-
• #11
Glad it's working now! It'd be interesting to know which commands were causing the issue as it's possible I might be able to do something in the driver to work around it...
Just from a design perspective, it does make sense to do certain things if and only if the wifi actually made a successful connection
That should actually be the case in the handler supplied to
wifi.connect(
- when you get called anderr
is not set, you're properly connected with an IP address.L6 and L15 will execute within microseconds of each other,
This is not the case at all, since
'connected'
is called when everything is connected. -
• #12
My results are far from scientific. This exercise has involved a lot of trial-and-error, some shotgun approaches, finger-crossing, and several desperate prayers to Binarius, the Greek god of bits.
But upon the very valid request to know which commands were causing the issue, I've pared the code down, and removed most of the delays, and got it reliably working. In my rigorous testing (by that I mean 6 times), I found that I only needed to delay starting the WiFi AP and Station a second after the SPI device.
This makes the relevant code now look like this:
function onInit() { SPI1.setup({mosi:A7, miso:A6, sck:A5}); bme = require("BME280").connectSPI(SPI1, B1); setTimeout(startApAndStation, 1000); wifi.on('disconnected', () => { clearInterval(timer); }); wifi.on('connected', () => { require("http").createServer(pageReq).listen(8080); mqtt = require("MQTT").connect({host: "xxx.xxx.xxx.xxx"}); dataInterval(); }); } function startApAndStation() { wifi.startAP('AgilatechESP', { password: 'xxxxxxxx', authMode: 'wpa2' }, (err) => { if (err) throw err; else wifi.setAPIP({ip:"10.0.132.1", gw:"10.0.132.1", netmask:"255.255.255.0"}, (err) => { if (err) throw err; else wifi.connect("HeartJS", {password : "xxxxxxxxxx"}, (err) => { if (err) throw err; }); }); }); }
Why? I dunno, but without the delay on the 'startApAndStation' I get the Uncaught CWMODE failed: Timeout error nearly every time.
-
• #13
Great, thanks for paring that right back! I'll look into it. It's possible the command gets executed before the ESP8266 has booted up completely.
edit: Just a quick note but it actually seems to work almost all the time here with
setTimeout
removed. Maybe it was some strange interaction with the BME280
I've got an Espruino Wifi updated to 2v04.
I am having trouble when trying to start an AP, AND connect to mqtt AND creating an http server.
The error:
Here's my onInit():
And this is apparently the offending chunk of code:
But, to make things even more interesting, this DOES NOT happen immediately following a 'save()' command. Sometimes if power if removed long enough, it will start without errors on the next boot, but even this has no rhyme or reason. Why should the device act differently immediately after a 'save()' and why does this behavior seem so non-deterministic? Thoughts?