sudomesh / disaster-radio

A (paused) work-in-progress long-range, low-bandwidth wireless disaster recovery mesh network powered by the sun.
https://disaster.radio
1.04k stars 110 forks source link

Support for Bluetooth LE #45

Closed samuk closed 4 years ago

samuk commented 4 years ago

It would be interesting to me to have an Android app that could communicate with Disaster Radio nodes using Bluetooth LE and could:

This App.apk could be distributed locally from the SD card over the Wifi

In the future when the OLED works to read messages, it might be interesting to use a mini bluetooth keyboard to enter messages directly to the device with no phone involved. I don't know if this is feasible, or should influence design decisions now, but I thought I'd mention it.

Presumably, you could only have one Bluetooth 'mode' working at any one time?

beegee-tokyo commented 4 years ago

I just had a look into the current active branch and into the pull request https://github.com/sudomesh/disaster-radio/pull/44

In the current active branch it will be difficult to add the BLE functionality as everything is somehow mixed into the main code. But with the pull request #44 it should be straight forward to write a BleClient module and use it instead of the Telnet/Websocket client.

Regarding BLE and WiFi active at the same time, that will most likely not work. I got both working (with some additional functionality) only on a ESP32 Wrover module with additional 4MB PSRAM. A ESP32 Wroom modules do not have enough RAM to run both + the main application.

Therefor dynamically switching between BLE and WiFi will be not an easy task.

For the start I would make the selection of BLE or WiFi static (compiler option ???) to be able to implement a BLE client module and test it.

Once it works the next step would be to select the client with a setupClient(uint8_t clientType) function. The clientType could be stored in a configuration file (or ESP32 Preferences).

Basically replace

  setupWiFi();
  setupMDNS();
  setupSD();
  setupSPIFFS();
  setupHTTPSever();

  setupTelnet();
  setupWebSocket();

With

#define WiFiClient 0
#define BleClient 1

  Preferences preferences;
  preferences.begin("Settings", false);
  uint8_t clientType = preferences.getShort("c_type", WiFiClient ); // defaults to WiFi if preference is not set

  setupClient(clientType);

And then initialize the requested clients within setupClient().

samuk commented 4 years ago

For the start I would make the selection of BLE or WiFi static (compiler option ???)

Thanks for your thoughts and input on this, it seems like a sensible approach.

If I understand correctly, ultimately It could be possible to switch the Wifi off/ BlueTooth on from within the Wifi/web interface? To switch the Bluetooth off/ wifi on from the Android/BlueTooth interface? Either action would trigger a reboot?

I can see that some kind of credential/ password system would be nice for changing the mode, otherwise, my carefully calibrated Bluetooth solar node could be turned to Wifi by a third party and battery run down.

paidforby commented 4 years ago

Regarding BLE and WiFi active at the same time, that will most likely not work. I got both working (with some additional functionality) only on a ESP32 Wrover module with additional 4MB PSRAM. A ESP32 Wroom modules do not have enough RAM to run both + the main application.

Yes, I recall reading about this limitation.

For the start I would make the selection of BLE or WiFi static (compiler option ???) to be able to implement a BLE client module and test it.

Adding a build flag would be easy. And could be handled similarly to LoRa pins and OLED pins are with config.h and platformio.ini in the refactor.

Once it works the next step would be to select the client with a setupClient(uint8_t clientType) function. The clientType could be stored in a configuration file (or ESP32 Preferences).

This also looks good. Creating a user preferences system is in the plans, and this would be a great reason to start working on it.

It could be possible to switch the Wifi off/ BlueTooth on from within the Wifi/web interface? To switch the Bluetooth off/ wifi on from the Android/BlueTooth interface? Either action would trigger a reboot?

@samuk I believe you are correct. You can compile and flash both WiFi +BLE to an ESP32 Wroom, you just can't run both at the same time. So if we have a way of editing the user preferences from either the web gui, android app, or telnet/console interface, it should be possible to trigger a reboot after changing preferences and boot back up into BLE or WiFi mode.

beegee-tokyo commented 4 years ago

@samuk @paidforby

Good news. I have setup 2 LoRa nodes. One is a Sparkfun 1ch gateway with a RFM95 module using the BLE client and the second one is a custom ESP32 Wrover board with an Semtech SX1262 transceiver using the WebSocket client. BLE client is implemented and working. I am using BLE UART at the moment, so I can test easily from an Android BLE Terminal program.

I am seeing all messages sent from one node to the other node on the BLE terminal and I can send messages to the other node as well with e.g. 00c|Hello from BLE.

Before I dig into the Android application, a few questions: 1) (Most important) Is there a better specification for the data packages than Websocket? I cannot find a clear explanation of how the packages (beside of the chat and map messages) are looking like? I see binary data arriving, which I guess is the mesh information, but I have no idea about the format. To show Mesh information in the Android app and maybe users that have joined, I need to know the exact format of all packages. 2) On the web application running on the ESP32, I can see messages, join the network and send messages, but the Active Nodes | Hops | Metric is never updated. Is that the current status or am I doing something wrong? 3) On the Android app I will as a first step implement something similar to the web application. Just a list box with the messages, an input box for sending messages and a list of Active Nodes | Hops | Metric. Would that be a good start? When sending messages, it seems the messages go to all connected nodes right now. How is a specific recipient selected?

rhodey commented 4 years ago

hi all, I have been watching this project from afar for awhile now and I am not sure that bluetooth is the correct frequency to use for a "disaster". also if this is only a radio for disaster it wouldn't make sense to try and use it for other things. when disaster comes the first people on the scene are "first responders" but I like to think of them as lifeguards sometimes.

the Earth is different all over, I don't believe one single frequency will ever cover the entire planet, and I worry that it is a bad idea to attempt this.

I have a handful of ideas about which frequencies would be best for lifeguard.fm and additionally which protocols & modulation scheme but I believe the topic requires further study. I'd be happy to compare notes some time :)

rhodey commented 4 years ago

think about the sharks in the ocean, they use the electromagnetic spectrum, if you muddy up their senses you might get bit!

beegee-tokyo commented 4 years ago

@rhodey
You are misunderstanding what this issue is about.

This is not an approach to replace the LoRa for the communication between nodes.

It is an approach to have an alternative to the WiFi connection between the LoRa node and the phone (where you type your messages) by using a BLE connection. BLE is using way less energy, so the batteries of the phone and the LoRa node can last longer.

rhodey commented 4 years ago

I don’t claim to know any of the knitty gritty details, but I do see that you’re trying to make use of the electromagnetic spectrum & y’all seem to be moving too fast to be honest.

the animals did not split up the audio spectrum in one day or one year, slowly over time everyone found a place for themselves.

Humans have no idea where they belong in the electromagnetic spectrum because often we think we got here first. false.

I have thought carefully about this for a number of years and I still do not have all the answers, but they continue to reveal themselves in time.

take care, — rhodey

On Tue, Feb 4, 2020 at 6:50 AM, Bernd Giesecke notifications@github.com wrote:

@rhodey You are misunderstanding what this issue is about.

This is not an approach to replace the LoRa for the communication between nodes.

It is an approach to have an alternative to the WiFi connection between the LoRa node and the phone (where you type your messages) by using a BLE connection. BLE is using way less energy, so the batteries of the phone and the LoRa node can last longer.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

paidforby commented 4 years ago

@beegee-tokyo sounds like you are making good progress! To address your questions,

1) (Most important) Is there a better specification for the data packages than Websocket? I cannot find a clear explanation of how the packages (beside of the chat and map messages) are looking like? I see binary data arriving, which I guess is the mesh information, but I have no idea about the format. To show Mesh information in the Android app and maybe users that have joined, I need to know the exact format of all packages.

This is a point of some contention and confusion as it stands. You can read into this in issue #48. I'm planning on adjusting the message structure based on that discussion. Currently the chat app only accepts messages formatted as such,

struct Message {
    uint8_t id[2];  // equivalent to uint16_t, running count of messages sent by server
    uint8_t type; // ASCII char corresponding to content/use of message
    uint8_t delimiter; // always '|' (pipe)
    uint8_t data[236]; // content of message
};

However, the refactored code works in Strings which creates some difficulty using the real msg_id. Right now, I would suggest to try to use the same workaround that is used by the Websocket here and creating a pseudo-msg_id.

The mesh information you are seeing is probably the routing table, the message structure for that is documented here, https://github.com/sudomesh/disaster-radio/wiki/Protocol#routing-table-packets and could be used to display the routing table in the Android app.

2) On the web application running on the ESP32, I can see messages, join the network and send messages, but the Active Nodes | Hops | Metric is never updated. Is that the current status or am I doing something wrong?

I believe that is the current status because of the aforementioned String-struct issue, that routing table is entirely binary data, whose meaning is lost when converted to a String.

3) On the Android app I will as a first step implement something similar to the web application. Just a list box with the messages, an input box for sending messages and a list of Active Nodes | Hops | Metric. Would that be a good start? When sending messages, it seems the messages go to all connected nodes right now. How is a specific recipient selected?

That would be a great start! Yes, currently we just broadcast to all neighboring nodes. The logic exists to route packets. A routed packet extends the header by appending the next hop of the packet to the end of the LoRaLayer2 header. This is documented here, https://github.com/sudomesh/disaster-radio/wiki/Protocol#routed-packets.

Unfortunately, the chat app has not been updated to be able to handle routed packets. I have been meaning to revisit the routed packet logic to make it usable in more than the simulation environment.

I started writing an update to address #48 but got sidetracked by conceptual issues. I hope to make some progress on it this weekend.

beegee-tokyo commented 4 years ago

I have a first version now ready. The Android code is based on the Nordic nRF Toolbox which is public domain SW. On the Android side it parses the packages and shows them in a chat window. The user can setup a username (saved on the phone) and send messages. I agree with #48 to switch to a structure for the messages instead of strings. BLE is sending the data anyway as byte arrays. I could't decode the routing messages. They are 10 bytes long only, so definitly not valid. When receiving this packages on the ESP32 to send them over BLE, the raw data in HEX looks like: 20 20 2D DF 3A 8D 01 5A 00 00

I need to clean up the source codes now (both ESP32 and Android) and then I will push a merge request for the ESP32 part. I was thinking to put the BLE stuff in a new branch for now. For the Android I will put the source code in my github as public repo, but if you agree, I can as well publish the whole app in Google Play Store.

beegee-tokyo commented 4 years ago

Merge request is up #53. Android source code is available disaster-radio-android. I rebased it on a different source code which is less bloated than the Nordic nRFtoolbox.

Needs more testing. I have a strange error in the callback. When

drBleClient.startServer([](BleDrClient *drBleClient) {
    radio->connect(new WelcomeMessage())
        ->connect(new HistoryReplay(history))
        ->connect(drBleClient);

Is called, the client in WelcomeMessage is NULL, but is correct in HistoryReplay and in connect(drBleClient). No idea why.

samuk commented 4 years ago

Great stuff! I just had a go with it and can confirm it works on the TTG0 V2 boards. My platformio.ini looks like

[platformio]
src_dir = firmware/esp32
data_dir = web/static
default_envs= 
     ttgo-lora32-v2
    ;sparkfun-lora

[env]
platform = espressif32
framework = arduino
upload_port = /dev/ttyUSB0
monitor_port = /dev/ttyUSB0
;upload_port = /dev/cu.SLAB_USBtoUART
;monitor_port = /dev/cu.SLAB_USBtoUART
monitor_speed = 115200
lib_deps =
    AsyncTCP
    ESP Async WebServer@1.2.3
    LoRa@0.6.1
    https://github.com/sudomesh/LoRaLayer2#8509821d2a04b5edccaaaab7a6ef3260aa7b2cba
    https://github.com/paidforby/AsyncSDServer#13375c6be978cb34180378ecf4042a3a4a1f5eab
    ESP8266 and ESP32 OLED driver for SSD1306 displays
    TinyGPSPlus@1.0.2

[env:ttgo-lora32-v1]
board = ttgo-lora32-v1
build_flags = -DTTGO_LORA_V1

[env:ttgo-lora32-v2]
board = ttgo-lora32-v1
build_flags =
    -DTTGO_LORA_V2
    -DUSE_BLE
board_build.partitions = custompart.csv

If anyone fancies installing an unsigned .APK from a random on the internet then there is one here: https://archive.org/download/app-release_202002/app-release.apk

BTpower

The power consumption for these on the TTGO with Bluetooth is now in the region of 100mA, peaking to 160mA when packets are transmitted. This is a ~100mA improvement over the WiFi.
beegee-tokyo commented 4 years ago

I will put a signed version of the Android app on Google Play store tomorrow.

Please keep me updated with your tests, I had some problems with the BLE connection, but it was very random and seldom.

beegee-tokyo commented 4 years ago

Android app is live in Google Play Store DisasterRadio BLE Chatbox.

For the power consumption, if you do not need long range, you can try to play with the BLEDevice::setPower(ESP_PWR_LVL_P7); setting in BLEClient.cpp line 254.

Right now it is set to maximum TX power, but you can test with lower settings. Possible values:

/**
 * @brief Bluetooth TX power level(index), it's just a index corresponding to power(dbm).
 */
typedef enum {
    ESP_PWR_LVL_N12 = 0,                /*!< Corresponding to -12dbm */
    ESP_PWR_LVL_N9  = 1,                /*!< Corresponding to  -9dbm */
    ESP_PWR_LVL_N6  = 2,                /*!< Corresponding to  -6dbm */
    ESP_PWR_LVL_N3  = 3,                /*!< Corresponding to  -3dbm */
    ESP_PWR_LVL_N0  = 4,                /*!< Corresponding to   0dbm */
    ESP_PWR_LVL_P3  = 5,                /*!< Corresponding to  +3dbm */
    ESP_PWR_LVL_P6  = 6,                /*!< Corresponding to  +6dbm */
    ESP_PWR_LVL_P9  = 7,                /*!< Corresponding to  +9dbm */
    ESP_PWR_LVL_N14 = ESP_PWR_LVL_N12,  /*!< Backward compatibility! Setting to -14dbm will actually result to -12dbm */
    ESP_PWR_LVL_N11 = ESP_PWR_LVL_N9,   /*!< Backward compatibility! Setting to -11dbm will actually result to  -9dbm */
    ESP_PWR_LVL_N8  = ESP_PWR_LVL_N6,   /*!< Backward compatibility! Setting to  -8dbm will actually result to  -6dbm */
    ESP_PWR_LVL_N5  = ESP_PWR_LVL_N3,   /*!< Backward compatibility! Setting to  -5dbm will actually result to  -3dbm */
    ESP_PWR_LVL_N2  = ESP_PWR_LVL_N0,   /*!< Backward compatibility! Setting to  -2dbm will actually result to   0dbm */
    ESP_PWR_LVL_P1  = ESP_PWR_LVL_P3,   /*!< Backward compatibility! Setting to  +1dbm will actually result to  +3dbm */
    ESP_PWR_LVL_P4  = ESP_PWR_LVL_P6,   /*!< Backward compatibility! Setting to  +4dbm will actually result to  +6dbm */
    ESP_PWR_LVL_P7  = ESP_PWR_LVL_P9,   /*!< Backward compatibility! Setting to  +7dbm will actually result to  +9dbm */
} esp_power_level_t;
samuk commented 4 years ago

Great work, thanks! In the next version would it be possible to make the disaster.radio a link in the 'about' section? Or won't it accept links?

beegee-tokyo commented 4 years ago

Yes, can be done.

Next version will show active nodes, hops and metrics as well. But I can only test with two nodes, so the list always contains only one node.

beegee-tokyo commented 4 years ago

@paidforby I am still playing around with the Android app. 2 things:

a) The routing table sent is not destroyed. But it has a strange format: xx-nnnhm where xx is a always 0x00 0x00 then instead of the message identifier c or m it has a - the separator | is missing nnn is the node address h is the number of hops m is the metrics not sure where this info comes from and why it is in that format.

b) Map information I was thinking that I can use the phone's GPS information to get the location, which is working fine. And then have a button to send the location. The location information is described as 0400m|<juul>{request:{tile:[[2.115, -59.28],[2.345,-59.05]]} here but if I look into the GPS client I see a message created that looks like xxc|<username>latitude,longitude Which one is correct? And if the later one is correct, how to distinguish between a chat message and a location message?

paidforby commented 4 years ago

a) Interesting. It is good to know that the routing table packet survives the conversion to a string. I believe those additional bytes are due to the msg_id workaround that came with the refactor. See here. Since the routing table comes from LoRaLayer2, it is pushed through same buffer as the chat messages. The refactor treats those messages the same, hence the added bytes. I'm still not sure how that pipe is getting in there since there is no pipe in the routing table message. I need to rewrite the client-server communication so it is consistent across applications.

b) You'll love the answer to this. Neither of those are "correct."

The first one,0400m|<juul>{request:{tile:[[2.115, -59.28],[2.345,-59.05]]}, was just an example of something that could be done, I will remove it from the README, since it clearly can cause confusion.

The second one, xxc|<username>latitude,longitude, was an example written by @tlrobinson as a proof of concept for a GPS enable dev board, which would just occasionally post coordinates to the chat app.

The actual disaster radio mapping web app is stored here https://github.com/sudomesh/disaster-radio-map, that was written by @Juul and I honestly have no idea of the format message are expected in. Based on this snippet, it looks like it may be a json object containing pos, type, and desc. I'd have to dig into it more to give you a correct answer, this "react-style" javascript is always very cryptic to me.

beegee-tokyo commented 4 years ago

:tongue: It looks like the format for the location is like {"pos" [lat, long]}

I put an issue with a question on the repo of @Juul .

For the routing table I might be wrong, because the delimiter sometimes changes from "-" to a 0x81 value. I can catch this in the Android app but if it is really corrupted a refactoring from String to byte[] array might be better.

Right now working on a map solution to show known nodes with their location (if they reports them) in the Android app with the BLE connection. Today I succeeded to show my own location on the map, the next days I will add positions of other nodes that report their location.

samuk commented 4 years ago

Great stuff! Exciting to hear about progress with a map.

A further feature request would be to add some (optional) notification sounds, so If I lose BT connection I get a beep, if someone @mentions my nick I get a beep. No idea how hard that is to do?

beegee-tokyo commented 4 years ago

Sounds are no big problem I think. It's just some effort to make them user-configurable, but for a start I can stick to the default notification sound of the Android system (which is different on each phone/manufacturer).

beegee-tokyo commented 4 years ago

Done.

map-tablet

The coordinates are sent every 1 minute if the node is connected to the Android phone via BLE. The WiFi node you see in the picture is a fake location. And the Huawei node is a slightly altered position, because both BLE and Huawei nodes are on my table and would show the same location. Switching between chat window and map window is done by a button. While viewing the map, the chat window is still updated with incoming messages. For the moment there is an additional button to share the location, but that might not be necessary in the future.

Sounds are done as well. If someone sends a message with @, a notification sound will be played. If the BLE connection to the node is broken, an alarm sound is played. The sounds used are the default notification and alarm sounds setup on your phone.

Need to do some clean-up and then I will update the repo and release a new version on Google Play.

beegee-tokyo commented 4 years ago

Oh, and there are more things I need to add. I am using MapBox, same as the disaster-radio-map web app I think.. Mapbox is free for a limited number of map updates per day only, after that they charge me. So I cannot just spread my access token with the app. The user needs to get his own access token and enter it in the app. On top of that, I need to add the option to download maps to be able to use the map without internet connection.

samuk commented 4 years ago

Might be interesting to use this map base layer at some point. https://wiki.openstreetmap.org/wiki/Humanitarian_map_style

beegee-tokyo commented 4 years ago

Good idea, I forgot about OpenStreetMap.

Will check tomorrow.

beegee-tokyo commented 4 years ago

Rolled out new version with map functionality and sound notification.

The sound notification uses the default notification sound of the phone for messages that start with @>>myName<< where >>myName<< is the name you joined the channel with. If the BLE connection to the LoRa node is lost the default alarm sound of the phone is played. TODO add settings to make the sounds user configurable.

The map functionality works only with nodes that use the BLE client at the moment as the GPSClient is not implemented yet. As the location message format is not finally defined yet I assumed it is as
04m|<user>{"pos":[48.75608,2.302038]} (Guessing from the javascript in disaster-radio-map

The maps view is based on OpenStreetMap osmdroid API. By default the app will load the required maps online based on usage. To use an offline map, you have to create a map that is compatible with OSM. You can use e.g. Mobile Atlas Creator (MOBAC) and create a map covering the region you need. At the moment only one map is supported. Read the osmdroid Wiki which map formats are supported, how to create a map and where to store it on your phone. The Wiki says to put the maps into /sdcard/osmdroid/. If you have a phone without SD card just put it into /osmdroid into the phone folder you see when opening the phone on your PC. On my Samsung Galaxy J2 Core the Windows path looks like Computer\SM-J260Y\Phone\osmdroid. On my Huawei M5 tablet the path looks like Computer\ThisIsMine\Internal storage\osmdroid. A sample map for Philippines, Metro Manila, Sucat road can be found in the maps folder. As DisasterRadio is designed to communicate in cases where none of the usual communication means are available, it is recommended that you create a map of your region in advance and save it on your phone.

samuk commented 4 years ago

Nice, love the new map stuff, very cool.

Using the latest from Play store I did get an error when first trying to connect to the TTGO which crashed the app. It was resolved once I installed the OSMdroid app and saved a map to the /storage/emulated path.

beegee-tokyo commented 4 years ago

@samuk, good finding. I need to check this. The app should not depend on OSMdroid installed.

samuk commented 4 years ago

@beegee-tokyo The latest version works for me. Thanks for your work on this, I'll close this ticket now as it's been merged into master.

The switching function between BT/ Wifi would be nice to have at some point, but I think this feature request is complete.