-
Hey, @allObjects thanks for your response.
Since the devices were quite cheap and on the market for a while I guess it's all old-fashioned BLE 4.
My guess was also that a connection actually might have been avoided by the creators since it means complexity and drawing more battery power.
But my question is this:
Does the quoted advertisement data contain everything the device has to tell us?
Where is the device name that my LightBlue BLE sniffer app shows?
How can I obtain any data from the advertised service and its characteristics when I cannot connect to the device?PS: I just found the same item on aliexpress and they explicitly say it's BLE 4 - look: https://www.aliexpress.com/item/33041131063.html
-
Disclaimer: I have played around with Espruino some while ago and my JS and BLE knowledge may have gotten a bit rusty - so sorry for any stupid questions in advance.
I got a Chinese TPMS (tire pressure monitoring system) consisting of 4 coincell powered BLE enabled oversized valve caps and an iOS / Android App.
The sensors support pressures over 10 Bar, but the App doesn't allow warning thresholds beyond 6,4 and since I want to monitor my bike tires I am stuck here.
Espruino to the rescue! (so I thought) - so I started hacking.I can see the Sensors appearing in the iOS LightBlue app from time to time. Name "TPMSn-xxxxx" with n being 1..4 and xxxxxx being the hex suffix of the device ID.
Each device reports 1 service, but any connection attempt times out.So I got out my old Nordic development board with the Espruino port loaded and finally used this:
NRF.setScan(function(d){ console.log(d); }, { filters:[{ services: ['fbb0'] }] });
to log this:
BluetoothDevice: { "id": "82:ea:ca:30:bc:95 public", "rssi": -53, "data": new Uint8Array([2, 1, 5, 3, 3, 176, 251, 19, 255, 0, 1, 130, 234, 202, 48, 188, 149, 0, 0, 0, 0, 121, 9, 0, 0, 85, 1]).buffer, "manufacturer": 256, "manufacturerData": new Uint8Array([130, 234, 202, 48, 188, 149, 0, 0, 0, 0, 121, 9, 0, 0, 85, 1]).buffer, "services": [ "fbb0" ] } BluetoothDevice: { "id": "83:ea:ca:40:bc:fb public", "rssi": -55, "data": new Uint8Array([2, 1, 5, 3, 3, 176, 251, 19, 255, 0, 1, 131, 234, 202, 64, 188, 251, 0, 0, 0, 0, 134, 9, 0, 0, 75, 1]).buffer, "manufacturer": 256, "manufacturerData": new Uint8Array([131, 234, 202, 64, 188, 251, 0, 0, 0, 0, 134, 9, 0, 0, 75, 1]).buffer, "services": [ "fbb0" ] } ...
From time to time devices appear. But this is it.
When I actually try to connect a device using this code:
var gatt; NRF.requestDevice({ filters: [{ namePrefix: 'TPMS' }], timeout: 60000 }).then(function(device) { console.log(device); return device.gatt.connect(); }).then(function(g) { gatt = g; return gatt.getPrimaryService("fbb0"); }).then(function(service) { console.log("Service:" + service); return service.getCharacteristics(); }).then(function(characteristics) { return console.log(characteristics); }).then(function() { gatt.disconnect(); console.log("Done!"); });
- even with the service id as filter - I always time out.
I guess the device avoids connections to save power.
Or at least makes connections as brief as possible.So my questions:
- Is there anything more I can try?
- Is it possible that the whole sensor data is contained in either "data" or "manufacturerData" already and the advertised service is not needed at all?
- even with the service id as filter - I always time out.
-
@allObjects thanks for the clarification.
Actually it's not for a motorized vehicle but for a velomobile - a fully faired three wheeled recumbent. Because there is no dynamo you need a battery to power lights, indicators and horn.
And if that goes down on longer trips you are busted, so I'd like to know how much is left and how much I currently draw.
Because it is easy to do I'd also like to measure speed, cadence and distance - and store them in an EEPROM (see other thread). And the best time to store is when the battery is pulled. -
-
Not quite.
The 12V pulls the pin high, but the (external) diode shortens that to 3,3V.
I just measured the voltage to be about 3.8V on the pin.
Do you think this is still an issue?For pulldown (in case of power loss) I used the internal resistor via
pinMode()
As with the flickering this may have been cause by wrongly wired MOSFETs or a shortage between 3,3V and 5V PIN.
Prototyping got a bit messy lately... ;-) -
-
I just wired it up.
Used a 10k resistor to pull D7 against Vin (about 12,5 V from my fully charged LiPo).
Capacitor is a 1000uF / 25V.Strange thing: The display flickers and blacks out rhythmically while D7 is not configured.
I used this script to test powerdown detection:
function onInit() { pinMode( D7, "input_pulldown" ); setWatch(function(e) { //LED.set(); console.log( "power out" ); var t=0; setInterval(function() { console.log( "t=", t++ ); }, 1000 ); }, D7, { edge: "falling"}); }
It gives me about 6 seconds console feedback before dying with the LED off:
power out t= 0 t= 1 t= 2 t= 3 t= 4 t= 5 t= 6
With LED set immediately after power down it is only two seconds (still enough for some cleanup probably).
So basically it seems to work.
@Gordon: is it normal that it behaves strange when certain pins are used as inputs bot not configured yet?
-
Thanks, @AkosLukacs.
I was considering the CR2032 backup, but was scared to mess with two inputs.
Thanks for sorting that our.Actually it would be enough to have some cycles left for a EEPROM write, so a powercap or elko could just do.
What about another pulldown between Din and GND?
EDIT: I just found input_pulldown in thepinMode()
documentation. That would do the trick, wouldn't it?It would be nice to use a
setWatch()
instead of polling external hardware.
Maybe I'll give t a try later...The NRF52LL reference looks interesting, too. Did not know about it.
Thanks. -
I am using a Pixl.js for a bike (velomobile) computer project.
It should monitor the onboard LiPo battery with 11.1 V and is also driven by it.In case off immediate power loss (battery is dying or just disconnected) there should be a triggered routine to save some current data to flash.
I was considering this:
Battery power is monitored by an INA226.
So one way is to regularly poll and detect an undervoltage.
But this is slow and could only work for a slowly dying battery - no sudden disconnect.So another idea is this:
Put a capacitor parallel to the Vin Pins to gain some time after power loss.
Put a diode in front so the capacitor only feeds the Pixl, nothing outside.
Wire the feeding current (before the doide) with a resistor (~10k) to a digital input.
Put another small diode between 3,3 V and the input so the voltage will not rise above 3,3 V. (Maybe a Z-diode of about 3,3 V between GND and the input would be better?)
As soon as the outside voltage drops, the pin will change to low and trigger a watch set on that pin.Will that work?
Anyone tried something similar or has a better idea?Some ASCII Art sketch for clarification:
Din O---+--[R]----+ | | V diode | - | | | 3v3 O---+ | diode | Vin O---+---|<----+-------------O +| + LiPo === capacitor | - LiPo GND O---+-----------------------O
-
Hehe :)
It is really nice that all of this is open source. I was already considering this.
But currently I am far away from learning this.Maybe if the VMC (velomobile computer) takes off and everyone wants to have one we can do a kickstarter project on this...
The only hardware missing would be a few sensors (voltage/current, maybe accelleration and gyro), a few inputs (switches, wheel and crank sensors) and some MOSFET outputs (light, PWM(!), indicators etc.)
Could be quite compact on a custom PCB. -
-
The displays from China just arrived.
First I unlocked the connector and pulled back the ribbon cable.
Unsoldering the backlight pins war no issue, since there wer only two:
First dipping a bit fresh solder on, then heating up one by one prying the LCD off with my fingernails.
The funny sound is the sticky tape coming loose.
There is another strip of tape on the top that will just come off if you lever it up.Cleaning the holes from the backlight connectors with desoldering wick, then putting in the new display, just using the sticky tape on the top, because the lower part is already held by the solder.
Re-solder the two pins, putting the connector back in.Voila! - Looks as new.
Thanks for the tips and the link. Delivery was quicker than expected and it just went through customs without an issue.
See the picture of the box. They even provided the connector terminals for each display...
-
Sorry. My bad.
Found it by trying around - maybe it's just the lack of understanding the internals... especially what belongs toonInit()
and what does not.I removed the
E.setBootcode()
completely and moved theBluetooth.setConsole(true)
fromonInit()
to just the first line of the code.
Now the splash screen is complete.Thanks @Gordon
It's working now! (yay)
And I also upgraded my bangle pledge to the early ones delivered in December by the way ;-)
-
-
Thanks @Gordon for the quick reply.
But there is no issue withg.drawImage()
- it shows correctly.It even is drawn after a power cycle - only the bottom few lines (or left part if you hold the Pixl in portrait mode) are missing (see photo).
-
Thanks @Gordon.
I just picked up on this, compiled+compiled a new firmware.
And it works - but not just as expected.Using a 90°CCW rotated flash image (see code) below.
Showing the image withg.drawImage()
seems okay.Maybe I am doing something wrong here, but two things happen after flashing:
- after storing the flash and power-cycling the left lines (or lower lines if not rotated) of the image are blank - maybe this is something about the terminal you mentioned
doing a manual
reset()
after installing also gives an error message:>reset() =undefined ____ _ | __|___ ___ ___ _ _|_|___ ___ | __|_ -| . | _| | | | | . | |____|___| _|_| |___|_|_|_|___| |_| espruino.com 2v04.228 (c) 2019 G.Williams Uncaught Error: Expecting first argument to a valid Image
This is my complete code, including the 90°CCW rotated image:
var buf = E.toArrayBuffer(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+AAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAA//wAAAAAAAAAAAAAAPgAAf/8AAAAAAAAAAA////4AAP//gAAAAAAAAAAP///+AAD//4AAAAAAAAAAD////gAB//+AAAAAAAAAAA////4AAf//wAAAAAAAAAAP///+AAP//8AAAAAAAAAAD/gAAAAD///AAAAAAAAAAA/8AAAAA///wAAAAAAAAAAD/wAAAAf//8AAAAAAAAAAAP/AAAAH///AAAAAAAAAAAB/8AAAB///wAAAAAAAAAAAH/wAAAf//8AAAAAAAAAAAAf/AAAP///AAAAAAAAAAAAB/4AAD///wAAAAAAAAAAAAH/wAA///8AMAAAAAAAAAAAf+AAP///AHwAAAAAAAAAAB/4AD///wB8AAAAAAAAAAB/+AB///8AfAAPgAAAAAAD//gAf///AHwAB+AAAAAA///4AH///wB8AAPgAAAAB///wAB///8AfAAD8AAAAD//+AAAf///AHwAAfAAAAA//4AAAH///gB8AAHwAAAAP/AAAAD///4AfAAB8AAAAD/gAAAA//4DAHwAAfgAAAA//AAAAP/4AYB8AAH4AAAAP/+AAAD/+ADAfgAB+AAAAD//8AAA//AAYH4AAfgAAAAH//4AAP/wAGB/AAH4AAAAAP//wAD/4ABgPwAB+AAAAAAf//gA//AAYD+AA/AAAAAAA//4AP/wAGAf4AfwAAAAAAB/+AB/+ADAH/AP8AAAAAAAD/gAP/wBgA///+AAAAAAAAH4AD//BwAH///AAAAAMAAAOAAf//gAA///wAAAADwAAAAAP//4AAH//4AAAAA/AAAAAC//8AAAf/4AAAAAP+AAAAAv//AAAB/4AAAAAD/4AAAAT//wAAAAAAAAAAA//gAAAE//4AAAAAAAAAAAD/+AAABP/+AAAAAAAAAAAAH/4AAAj//gAAAAAAAAAAAAf/gAAJ//wAAAAAAAAAAAAB/+AAOf/8AAAAAAAAAAAAAH/4AFv//AAAAAAAAAAAAAAP/wGf//wAAAAAAAAAAAAAA/+BP//8AAAAAAAAAAAAAB//gf///gAAAAAAAAAAAB///4H///sAAAAAAAAAAAP///+A///xAAAAAAAAAAAD///4AH//8QAAAAAAAAAAA///AAAf/+MAAAAAAAAAAAP/4AAAA//mAAAAAAAAAAAD/AAAAAH/zAAAAAAAAAAAA4AAAAAB//AAAAAAAAAAAAAAAAAAAP/AAAAAAAAAAAAAAAAAAAA/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")); // just a test to show that the image works g.clear(); g.drawImage({width:128, height:64, bpp:1, buffer: buf}); g.flip(); // write to flash require("Storage").write(".splash", buf); E.setBootCode('delete global["\xff"].gfx'); function onInit() { Bluetooth.setConsole( true ); }
- after storing the flash and power-cycling the left lines (or lower lines if not rotated) of the image are blank - maybe this is something about the terminal you mentioned
-
This is just great.
Watched your talk on Youtube.
I was not aware that you actually "hacked" the hardware yourself in the sourcing phase.
So what did the suppliers think of you taking over their product?
You said "well, I like your hardware, but the app sucks - so I'll give you my own firmware here and just order a few hundred"?
Sounds like a win-win... ;-)Can't await to get it.
The previous Espruino gadgets were fun, but this is even something to show off, carry on your wrist and have a hacking session whenever you like...No wonder the goal was reached so fast. Who doesn't want one of these?
-
-
-
-
-
Back to the original question:
Is bitwise deletion possible?After studying the datasheet of the 24c32 that is coming with my RTC module the answer is: No
It only supports byte-write or page-write meaning 32 bytes in one of the 256 pages.
Regarding space management:
Since it's an odometer and values should only go up, it should be quite simple.What about this simpler method:
(the example shows 4 pages with 1 byte each, but can be scaled to 256 pages of 32 bytes easily)- initialization: write odometer value to first page, set rest to 0: [ 1, 0, 0, 0 ]
- current element: is the one with the highest number (the last one written)
- write new value: a) find current element, move one page forward (wrap around the ends like a ring buffer), write new value to that element: [1, 2, 0, 0]
Next iterations: [1, 2, 3, 0] [1, 2, 3, 4], [5, 2, 3, 4] etc...
This way wear is spread across all pages evenly, there is no need of any extra space management structure.
Since the highest value marks the current element there can be a payload with history data just after the actual odometer value:
[ [1, p], [2, p], [3, p], ...]Also writing can be reduced to something like: once every N minutes or K kilometers, whichever comes first.
If there is a power fail, only the last N minutes or K kilometers are lost.The datasheet for 24C32 states 1,000,000 write cycles endurance - so without any levelling and writing every minute or km that would be 1000000 km (which is more than enough) or 694 days, nearly 2 years of non-stop riding.
Spreading this over 256 pages should multiply these values by 256 and keep me on the safe side... - initialization: write odometer value to first page, set rest to 0: [ 1, 0, 0, 0 ]
-
Hi @allObjects, thanks for your extensive reply.
Actually what you wrote would be my fallback solution in case the intended one was not possible.The question was: Could a write done without an erase? Meaning: only turning off single bits one by one, because that's what my car odometer internally does.
Starting with a huge bitfield like FF FF FF FF FF FF FF FF (whatever size + redundant copies) after N kilometers one bit is deleted: FF FF FF FF FF FF FF FE, after another N the next bit: FF FF FF FF FF FF FE FE (and the same on the redundant copy or copies).
The exact algorithm is not yet known to me and maybe there are cases when parts are actually erased and re-written again, but it seems that clearing only single bits step by step reduces wear.
This is somewhat confirmed in this discussion here: https://www.microchip.com/forums/m550855.aspxAnd it was the reason for my question.
-
From this page http://www.espruino.com/AT24 I understand you can write whatever number of bytes to an EEPROM.
What I understood about EEPROMs that before writing a certain area (page) needs to be erased first (resulting in all FF) before it is written.
But what happens if I write only a byte?The reason for my question is this: I want to store an odometer value in an EEPROM and avoid writing too often reducing wear.
What I know from the automotive world is that they are using a certain area all set to FF and (redundantly) clearing single bits in a certain pattern without erasing anything.Is it possible with Espruino to do the same:
a) Erase a certain EEPROM area (set it to all FF) only once during initialization.
b) Later during lifetime only write single bytes in a way that only zero bits are cleared without doing an erase again?Something like:
// init (once) eeprom.write(address,data,true); // run (every N kilometers / hours) eeprom.write(address,data,false);
Update:
Doing some more research I found this:
https://raspberrypi.stackexchange.com/questions/76959/how-to-decode-tpms-sensor-data-through-rpi3-bluetooth
Maybe it is the same protocol, maybe similar.
At least the temperature reading would make sense in my case - not sure with the pressure yet.
So it most probably is about the
manufacturerData
and no connection is required.That would be quite simple to do, I'll just get my Pixl.js ready to take over from there... ;-)