sudomesh / LoRaLayer2

Layer 2 routing protocol for LoRa connected devices
86 stars 29 forks source link

Reading Messages #17

Closed robgil closed 3 years ago

robgil commented 3 years ago

This might be obvious, but I can't seem to parse the messages being sent via LL2->writeData. I haven't written c++ since college, but I don't think its my code, but likely I'm using the library? shrug

The setup is simple.

Sender Address: c0d3f00d Receiver Address: c0d3f00c

Receiver Side

void loop() {
  LL2->daemon();

  char routes[128];
  char neighbors[128];
  char config[128];

  LL2->getRoutingTable(routes);
  Serial.println(routes);

  LL2->getNeighborTable(neighbors);
  Serial.println(neighbors);

  LL2->getCurrentConfig(config);
  Serial.println(config);

  struct Packet packet = LL2->readData();
  Serial.print(((char *)packet.datagram.message)); <-- this should print something like '{"temperature: 70.0000, "humidity": 50.000}'

  delay(1000);
}

Output as follows

Routing Table: total routes 8
1 hops from c0d3f00d via c0d3f00d metric 255 <--- This is the sender... yay! Who is everyone else?!
1 hops from ffff00c0 via ffff00c0 metric 254 
2 hops from d3f00d00 via ffff00c0 metric   0 
64 hops from 48ce804b via ffff00c0 metric   7 
255 hops from 00000000 via ffff00c0 metric   0 
1 hops from 444344f8 via 444344f8 metric 254 
2 hops from 13412824 via 444344f8 metric   0 
41 hops from 1159063d via 444344f8 metric  24 

Neighbor Table:
c0d3f00d 255 
ffff00c0 254 
444344f8 254 

Current LoRa Config
Local Addr: c0d3f00c <-- Receiver.. yay!
Initialized: 1
CS, RST, DIO: 18, 14, 26
SPI Freq: 100000
LoRa Freq: 915000000
SF: 9
TxPwr: 20
Routing mode: auto
Routing interval: 10000ms
Duty Cycle: 0.100000

BTW, shocked to see all these other neighbors.

Sender Side

void loop() {
  LL2->daemon();
  char data[128] = {0};
  char counter_str[128] = {0};
  int msglen = 0;
  int packetsize = 0;
  sprintf(counter_str, "%i", counter);

  u8x8.drawString(0, 3, "Packets: ");
  u8x8.drawString(9, 3, counter_str);

  #ifdef ENABLE_DEBUG
  digitalWrite(LED, HIGH);
  #endif

  delay(1000);

  #ifdef ENABLE_DEBUG
  digitalWrite(LED, LOW);
  #endif

  float temperature = dht.readTemperature(true);
  float humidity = dht.readHumidity();

  // Build Packet
  msglen = sprintf(data, "{\"temperature\": %f, \"humidity\": %f}", temperature, humidity);
  #ifdef ENABLE_DEBUG
  Serial.println(data);
  #endif

  struct Datagram packet = buildDatagram((uint8_t *)GATEWAY, 'c', (uint8_t *)data, msglen); <-- GATEWAY in this case is the receiver node (char GATEWAY[9] = "c0d3f00c";)
  packetsize = msglen + HEADER_LENGTH;

  // Send packet
  LL2->writeData(packet, packetsize);  <--
  counter++;

  // After processing, go to sleep
  #ifdef ENABLE_DEBUG
  Serial.println("Going to sleep...");
  #endif

  esp_deep_sleep_start();
}
paidforby commented 3 years ago

What is happening in buildDatagram? It needs to do something like, based on disaster radio Console.cpp

uint8_t GATEWAY[ADDR_LENGTH] = {0xc0, 0xd3, 0xf0, 0x0c};
struct Datagram datagram;
char msgBuff[128];   // get your temp and humidity data into here, or sprintf it directly into datagram.message
msgLen = sprintf((char *)datagram.message, "%s", msgBuff);
memcpy(datagram.destination, GATEWAY, ADDR_LENGTH);
datagram.type = 'c';

It's a a little weird because the address needs to be raw bytes, not ASCII chars, which can be difficult because arduino wants to make everything strings. But if the destination address is wrong, then the packet will appear to never be received by the receiver, since it's the address in the datagram does not match its local address. This reminds me that I need to write some sender/receiver example code.

And don't get too excited about all those neighbors, its a known bug reported here, https://github.com/sudomesh/disaster-radio/issues/81#issuecomment-691722197, I'll take a look at that as soon as I get a chance.

robgil commented 3 years ago

@paidforby Thanks! I figured it out and am now receiving messages on the receiver.

Turns out I had to set the following for it to work.

char MAC[9] = "c0d3f00c";
uint8_t LOCAL_ADDRESS[ADDR_LENGTH] = {0xc0, 0xd3, 0xf0, 0x0c}; <-- Datagram wants the hex here.
uint8_t GATEWAY[ADDR_LENGTH] = {0xc0, 0xd3, 0xf0, 0x0c};

LL2->setLocalAddress(MAC); <-- was looking for char here

It would be handy if the Datagram accepted the char array vs. the raw. I see there are a bunch of functions for converting this already. This will definitely hang other folks up, so I'm glad we figured it out. I'll give a swing at writing a simple client (I already did most of it!)

robgil commented 3 years ago

Added https://github.com/sudomesh/LoRaLayer2/pull/18 :)

paidforby commented 3 years ago

Great! Glad you figured it out. Yeah, it's a funny mix of chars and bytes. The reason the Datagram struct takes bytes is because it's actually the datagram that is copied into the packet so making it take individual chars for each hex character would waste bytes. I guess, some conversion could be done in the LL2->writeData function. I was actually thinking of changing that writeData function so it doesn't take a datagram, but instead takes the inputs to a datagram, something like, LL2->writeData(char *destination, char type, char * message). That would make sense, right?

And thanks for that pull request, I'll review and merge it (i looked good to me and is certainly better than nothing).

robgil commented 3 years ago

@paidforby yeah, I think the change to of LL2->writeData makes sense. Especially as a higher level function.

paidforby commented 3 years ago

I also noticed another issue, not sure if it is possible cause of your routing table being filled imprroperly, but you should not use delay() in the loop, this is a blocking function that prevent the LL2 daemon from polling often enough. Instead of delay(), write a poll that waits for a certain timeout to execute its function, like what is done inside of LL2->daemon() here, https://github.com/sudomesh/LoRaLayer2/blob/master/src/LoRaLayer2.cpp#L671

paidforby commented 3 years ago

I made a number of changes in these two recent commits including,

  1. Updated to latest LoRaLayer2 commit in the platformio.ini for both examples

  2. Added LL2->init() that was missing, I'm not sure it this was causing many problems because all it does is initialize some timeout counters, but still it should be called.

  3. Corrected the packetsize length to be the datagramsize, you were adding HEADER_LENGTH (i.e. the LL2 packet header) instead of DATAGRAM_HEADER (just the datagram portion header), so there was extra data appended to the end of your hello messages.

  4. Both the sender and receiver had the same local address, I corrected this by changing the receiver to something more distinct.

These changes seem to have fixed the issue with the routing table filling with garbage, though it's difficult to prove the negative, so all I can say is that I haven't observed that problem since making these changes (specifically, updating LL2 seemed to fix most of the routing table weirdness).

Sorry that I didn't take a look at this sooner.

robgil commented 3 years ago

@paidforby excellent thanks! Good catch on the packetsize and init bits. I'll try updating these and see if I still get a mess of random neighbors.

Delay/Loop

I see your point about using delay(). The example you mentioned makes perfect sense when running in active mode all the time. I'll open a separate issue / example for doing deep sleep which is really where that comes from. I used delay() to emulate an ESP32 deep sleep. This unfortunately prevents packets from being sent since they seem to be getting sent asyncronously. We may need a way to flush the packet buffer before going to sleep.