-
You could definitely bypass that by taking apart the watch and reading the flash chip externally. You could possibly also bypass that by running something over the SWD pins that dumps the file contents. Also, I don't know whether Bluetooth passkey and whitelist are still active even after a reboot without loading code or in DFU mode.
-
Reflective LCDs work by reflecting ambient light. They are readable without a backlight, but cannot be backlit well if at all. They work well if there's enough light, but not very well in the dark. At best, you might have a frontlight that lights the screen unevenly.
Transmissive LCDs instead pass light through. They have a backlight to cover dark environments, but they are only properly visible with the backlight. In brighter environments, you need to turn up the brightness to compete with the ambient light. In sunlight, you end up needing to use a lot of power, if your backlight can even get that bright at all.
Transflective LCDs are capable of doing both reasonably well. In a dark environment, they can be lit evenly with a backlight. In a bright environment, the ambient light helps rather than competes, so the backlight can be turned off and the display remains visible. This saves a lot of power. As halemmerich has pointed out, we already have one on the Bangle 2. This is a huge reason why we can have multi-week battery life with an always-on display.
-
I do not know how to fix the error that the IDE is throwing. But once that's fixed, you can click the cylinder icon to view the list of files in the device storage, and then each file will have a couple buttons, including a trash can to delete it. Or, you can use the Storage module. Specifically,
require("Storage").erase(".boot0")
andrequire("Storage").erase(".bootcde")
. -
It sounds like you didn't actually break the firmware, just the JS code that gets run on boot. The files that get run are described here.
Follow the procedure to reset without loading any code. That should get you to a Banglejs logo screen. From there, use the IDE to delete any of the following that exist: .varimg, .boot0, .boot1, .boot2, .boot3, .bootrst, and .bootcde. (You probably only have .boot0 and .bootcde.) Then, go to the app loader and reinstall the Bootloader app. That should fix it.
-
I personally like using the "event" option for one-time reminders, and I delete them after. I'm thinking that adding a delete after expiration option just like the timers have would be a good idea, and I'm willing to attempt to implement it. To avoid duplicating work, is anyone else already doing that?
-
Got it. That makes perfect sense. I forgot about this post and submitted my PR anyways, but luckily I mostly complied with this. I have no changes to other apps to submit at this time, but I might in the future. I'll keep the one pull request per existing app change "rule" in mind.
The only thing is I think some of my changelogs are aweful or nonexistent, so I'll have to take a look at that. Depending on the quality of my commit messages, those might end up getting set back to version 0.01. (That should only affect me because I don't think anyone else uses my personal app loader so it will probably only affect me, and I'll know exactly what I'll have to reinstall.)
-
I have developed a few apps, and I'd like to contribute them back to the official app loader. Should I submit one pull request for each app, or a single pull request with all of them?
Additionally, some of them are not on their first version in my own repo. Can I leave it like this, or should I reset them back to version 0.1 before sending them in?
-
I haven't used StorageFiles anywhere in my own code, but maybe an app I installed used them. And yeah, my watch definitely was running 2v12 at some point, so the theory of old corruption being revealed now makes sense too.
The odd thing is normally the firmware would detect storage corruption when it booted and would automatically reset everything.
How long would this typically take, and is there a message during boot when this happens? I don't remember there being a message during boot and I didn't leave it on the logo for that long before attempting to reboot. Is it possible that I interrupted the process? Or is it more likely that it just didn't happen?
Even if I did interrupt it, there was clearly corruption left behind, so the mechanism isn't perfect.
And what would it be resetting to? Would it be completely blank and booting to the Bangle logo, or would it look like I had just taken it out of the box?
So hopefully it's very unlikely you'll see this again...
That's good. I called storage.eraseAll() just in case before connecting to the app loader and reloading the apps. (I used this as an opportunity to update the firmware and apps.) The watch is now back in service.
-
I believe my watch is running bootloader 2v12 and firmware 2v14.
Earlier today, my watch froze in the messages app and wouldn't respond to me trying to close it with the touch screen. I held down the button for a few seconds, and a loading box appeared. This loading box was the wrong color (black text on a white background even though I use my watch with a dark theme), and never disappeared. I eventually did a full reboot of the watch, and it stays on the light-themed Banglejs logo forever. If I run load() through the console, the loading box appears and never disappears. I noticed that manually trying to load() a file failed, so I called storage.list(). It appears that almost all of the files were deleted from my watch, with only the following remaining:
- widlock.wid.js
- widlock.info
- wid_a_battery_widget.wid.js
- wid_a_battery_widget.info
- calendar.img
- `ea,p $ (\"
- !AP
The Javascript and info files appear to be unharmed, calendar.img and `ea,p $ (" appear to be binary files. I'm not sure what is supposed to be in calendar.img, but I feel like `ea,p $ (" probably isn't supposed to be there because the name looks like a random chunk of minified code code. Interestingly, when I try to read !AP, I get undefined, even though the documentation says that this only happens when the file does not exist, and the file does show up in storage.list(). Am I misunderstanding something, or does this sound like a bug? Normally I'd just blame my own code for this and move on, but I've been running the same code on the watch for almost a month straight and a file showing up in storage.list() but not existing when I try to read it sounds like filesystem corruption to me.
I am aware that all I should need to do to fix this is go to the app loader and reload my apps. However, before I do that I want to recreate the filesystem just in case it's corrupted. Is there any way to do that? And before I do any of that, is there any information I might be able to dig from this that would be helpful?
- widlock.wid.js
-
In a way that's probably the best thing that can happen - the battery gets run down more quickly, the Bangle gets warmer internally and dries itself out quicker :)
Is the heat from the CPU and backlight really that significant?
But it's definitely worth giving it time - even if the Bangle comes back to life it doesn't mean it's 100% dry inside, so long term it's better to give it a good long time to dry out.
I guess I shall heat lamp it more. It's just hard to be without my watch for that long.
I'll reply in http://forum.espruino.com/conversations/378470/#comment16640058 in a second, but if you do end up having to replace the Bangle I'm very happy to offer a discount. I don't think I should lose money replacing a water-damaged Bangle when the docs say no swimming, but I also don't want to profit from your misfortune :)
Thanks for the offer. That sounds pretty fair to me. I'm going to keep my Bangle for now, but if it dies I'll let you know.
-
-
Thanks for pointing out bthrm. (It's not a thing I personally use, so I never thought to check it out.) It does seem to have the same goal (modifying Bangle.on), but it goes about it in a different way. bthrm uses Bangle.origOn, and my own app uses AUTOSWP2CLK.on. I think both approaches have some advantages and disadvantages. With bthrm's approach, we don't create confusion about which object is getting an event listener and we don't need to use .call to force the event listener to be assigned to something it wouldn't normally be assigned to. (We clearly do have that issue with my approach.) However, my approach is less likely to create naming conflicts with other boot code, because it seems far more likely to me that another app will want to create an object called Bangle.origOn than an object called AUTOSWP2CLK.
I went for a combination of the two approaches: create more children of Bangle rather than a top level object, but call them something like AUTOSWP2CLKORIG rather than just orig. This works as expected.
Is there a proper convention for names of objects created by boot code, or am I too paranoid about naming conflicts? I feel like they should have the name of the app somewhere in there to allow different pieces of boot code to distinguish variables from each other, and also have some way to distinguish boot code variables from the main app's variables. Like maybe have boot code objects be named BOOT, and have the main apps avoid creating objects with names starting with BOOT_ which I don't think they'd really have any reason to anyways?
TLDR: Thanks, that fixed it.
-
I'm also glad mine is OK. Well, the battery did run down quickly while the watch was bootlooping, but that was probably because of the backlight being on and the CPU being active for several hours straight. Probably about 80% when it started and 30% when it ended a few hours later. Battery life has been normal ever since. So I'm guessing we have no short circuits anywhere.
And don't worry, even with the slight delay, the customer service is probably still the best I've ever experienced.
-
Your theory about the heat wave in Europe makes a lot of sense. Far more than my software update idea. I'm not in Europe (not sure what % of people are vs around the worlf), but I guess coincidentally the heat wave there happened during a family vacation to a lake, where the temptation to swim is strong.
My watch was bought from the Espruino shop (not Kickstarter) and appears to have the membrane. I think I blame the button.
-
There is an app (swp2clk) which allows the user to swipe back to the clock. To avoid interfering with apps that use the swipe gesture for their own purposes, you have to use the settings menu to configure which apps get the treatment. I like swiping back to the clock, but I find this configuration to be annoying (and don't like that swp2clk writes the app name to storage every time an app is loaded), so I'm trying to make a similar app that does this automatically: apps that do not register any swipe handlers will have swiping go back to the clock, and apps that do register swipe handlers will just have their swipe handlers run as usual.
To do this, I'm trying to override Bangle.on(). The custom Bangle.on will pass all calls with an event type other than 'swipe' to the native Bangle.on (which I will store somewhere else) to avoid unnecessary complexity, but when the event type is 'swipe' the supplied function will be stored in an array. The native (non-overridden) Bangle.on will be called with an event type of 'swipe' and a function that will either call load() if there are no functions in the array, or call each function in the array (and not call load) if there's at least one. This should result in things appearing mostly unchanged, except for the illusion of a "default" swipe handler that goes back to the clock.
I have developed the following code (autoswp2clk.boot.js) to try to achieve this:
const AUTOSWP2CLK = { on: Bangle.on, // Original, unmodified Bangle.on() removeListener: Bangle.removeListener, // Original Bangle.removeListener() removeAllListeners: Bangle.removeAllListeners, // Original Bangle.removeAllListeners() swipeHandlers: [] // Array of swipe handlers }; Bangle.on = (eventName, func) => { if (eventName == 'swipe') { AUTOSWP2CLK.swipeHandlers.push(func); } else { AUTOSWP2CLK.on(eventName, func); } }; Bangle.removeListener = (eventName, func) => { if (eventName == 'swipe') { AUTOSWP2CLK.swipeHandlers.splice(AUTOSWP2CLK.swipeHandlers.indexOf(func), 1); } else { AUTOSWP2CLK.removeListener(eventName, func); } }; Bangle.removeAllListeners = (eventName) => { if (eventName == 'swipe') { AUTOSWP2CLK.swipeHandlers = []; } else { AUTOSWP2CLK.removeAllListeners(eventName); } }; // Using the old Bangle.on rather than the new one AUTOSWP2CLK.on('swipe', (dirLR, dirUD) => { if (AUTOSWP2CLK.swipeHandlers.length == 0) { load(); } else { for (let func of AUTOSWP2CLK.swipeHandlers) { func(dirLR, dirUD); } } });
(I also have a removeListener and removeAllListeners for compatibility, but have not tested them yet.)
However, what actually happens is that this app breaks basically everything, because Bangle.on() no longer registers event listeners. If I manually call AUTOSWP2CLK.on() which should be the unmodified Bangle.on(), it also does nothing, implying that this has broken it. Is what I'm trying to do possible and I'm just doing it wrong, or is this not possible?
-
Mine actually dried out on its own over the course of a few hours while I was still wearing it. But just in case I ran down the battery and left it directly under an incandescent light for a few hours. It doesn't seem to be damaged, but I'm now going to take it off before going into the water again.
-
-
Was your Bangle in water recently? It seems like me and someone else have an issue where the MCU thinks the button is constantly pressed because of water intrusion.
-
I am also experiencing that issue now. My unit has probably been in the water for about 10 minutes at a time at most, and definitely never reached a meter deep. I did technically swim, though it was slow and I was using my legs because I thought that would be sufficient to protect the Bangle. (My Pinetime which is also IP67 survived doing that every day for about 2 weeks.)
Is it likely that my watch was defective and should not have been harmed, or is it likely that my watch isn't defective and I subjected it to something that it was not designed to handle? If it's the former I'll try to keep it away from water completely in the future (because it's still my favorite watch), and if it's the latter I'll treat it normally but just won't do that again.
It seems a bit weird to me that the poster of this thread had this issue at almost the exact same time as me, along with this person who has bootlooping but never mentioned water. My first thought is to blame the 2v14 update, but that doesn't seem likely either because an interpreter update shouldn't affect the sensitivity of the MCU to pin state changes, especially in the bootloader. Maybe it's just a coincidence.
-
There is no option in the UI at the moment, but you can do it with code. You want to use Bangle.setOptions(). You pass in an object with the properties that you want to change. The relevant properties are:
- lockTimeout: how many milliseconds before the screen locks (set this to 60000 for 60 seconds)
- backlightTimeout: how many milliseconds before the screen's backlight turns off (set this to 5000 for 5 seconds)
Don't confuse backlightTimeout with lcdPowerTimeout: lcdPowerTimeout turns off the entire screen. You want to leave that at zero so the screen stays on.
TLDR:
Bangle.setOptions({ lockTimeout: 60000, backlightTimeout: 5000 });
- lockTimeout: how many milliseconds before the screen locks (set this to 60000 for 60 seconds)
-
Yeah, that reasoning makes sense. And I think you do a good job managing RAM. That's actually why I came here from Wasp OS.
The filesystem uses journalling so actually the amount of 'rewrites' you watch will actually do is extremely small.
That's good. I'm still going to try to reduce my calls to storage.writeJSON as much as I can, but it's good that I don't have to worry.
-
They probably won't survive a reboot though. For example steps are stored in the Bangle and if you reboot (long press reboot) the watch you will lose a days step count as it gets reset to 0. I suspect an app switch does not cause these value to be reset in the ram used by the core Espruino operating system. But a reboot will.
It's perfectly fine if the data doesn't survive holding the button for 10 seconds. (If that's what you mean by a long-press reboot.) But yeah, it looks to me like the data is stored in the interpreter, based on https://github.com/espruino/Espruino/blob/d1f2ed061f7417c76587a6c4d6b641f74d22452d/libs/banglejs/jswrap_bangle.c. Modifying the interpreter is beyond my capabilities, and for good reason I don't think the app loader easily supports doing this, nor do I think users would like that.
What I suspect is needed long term is a way to request that a variable is made non volatile and stored in the Espruino Ram - that way it will survive an app switch BUT will get reset and cleared if the watch is rebooted. Something like
Bangle.writeMemory(INT, 'freds_int', 57);
andBangle.readMemory('freds_int');
might do it. Of course such a scheme would be open to abuse by app writers who could just hog all the ram at the point of boot. So it might not find favour with Gordon.That looks like exactly what I was hoping to find. I'm not sure whether it being able to be abused by malware is a very compelling argument against it because this is ultimately a device about giving users full control, and apps run without restriction so they can do plenty of other evil stuff like wiping the filesystem and attempting to hack other devices through Bluetooth. Plus, if someone does make an app that abuses it, there's always the option to hold the power button and reset without loading code to remove it. (Of course, others are free to disagree.) The more compelling argument against it is that it's probably quite a lot of work to implement a new feature just to make one user (who isn't providing any extra payment) happy when there are better uses of time.
There are apps that write state to flash using the onKill() event hook. I dont think a few writes a day to flash is going to massively reduce the life of the watch.
That's a good idea. I guess I'll go with one single write and recalculating the time from that rather than continuously updating, and try to only do that write when killed if necessary. This should only be a few writes per day.
Does anyone know what flash chip is in the watch/have a datasheet? Or is it a situation where the upstream manufacturer just uses whatever 8MB chip they can find that day and there's no way to know what I have?
-
Sorry if I'm being annoying. I'm trying to modify the pomodoro app to support working in the background. From looking at the code for qalarm, it seems like that app achieves this by storing the alarm settings to a file and then making some code run on boot/app change that loads that file and sets some timeouts to trigger an alarm when the alarm should happen. I thought about using this approach for the pomodoro timer as well, but I'm worried about wear. A user typically only sets alarms occasionally, maybe every few months, while a pomodoro timer will be changing the alarm time multiple times per hour for multiple hours per day, multiple days per week, and I don't know whether the flash is up for that.
I noticed that when loading another application, most variables are lost. However, things like the Gadgetbridge weather and health information do survive loading another application, but do not survive power off. This suggests to me that there is some way to store data in RAM, but still have it survive a call to
load()
. I think that is the ideal solution because it avoids having to write frequently to the flash, and I think users are unlikely to care whether their pomodoro timer is still running when they turn the watch back on hours to decades later. (In fact, it may be actively harmful as they'll get surprise buzzes in a few minutes.)Is it possible to take advantage of that capability to store whatever information I want that way? Or is it some special feature of the interpreter and I'd need to modify the interpreter itself to do it? If it's the latter, is the flash durable enough that this isn't a concern and I'm too worried? I thought of another approach where I store just the time the timer was initially started and calculate the next alarm time given the elapsed time since the timer was started. That would reduce flash writes to once per timer use, but the code to do that would be more complex, especially if I want to support pausing the timer and having user-configurable timer parameters.
-
Agreed. I personally far prefer using the touchscreen over buttons when I can, but having more buttons won't hurt anything when I can still use the touchscreen. It will only make the watch more usable with gloves. Having the watch be usable with gloves would be a major improvement and serious advantage over other smartwatches.