V148
Fork of RadioHead-148 by
RHMesh.cpp@0:ab4e012489ef, 2015-10-15 (annotated)
- Committer:
- davidr99
- Date:
- Thu Oct 15 01:27:00 2015 +0000
- Revision:
- 0:ab4e012489ef
Messy start, but a port for RadioHead.; Currently the SPI modulus are the only ones that work.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
davidr99 | 0:ab4e012489ef | 1 | // RHMesh.cpp |
davidr99 | 0:ab4e012489ef | 2 | // |
davidr99 | 0:ab4e012489ef | 3 | // Define addressed datagram |
davidr99 | 0:ab4e012489ef | 4 | // |
davidr99 | 0:ab4e012489ef | 5 | // Part of the Arduino RH library for operating with HopeRF RH compatible transceivers |
davidr99 | 0:ab4e012489ef | 6 | // (see http://www.hoperf.com) |
davidr99 | 0:ab4e012489ef | 7 | // RHDatagram will be received only by the addressed node or all nodes within range if the |
davidr99 | 0:ab4e012489ef | 8 | // to address is RH_BROADCAST_ADDRESS |
davidr99 | 0:ab4e012489ef | 9 | // |
davidr99 | 0:ab4e012489ef | 10 | // Author: Mike McCauley (mikem@airspayce.com) |
davidr99 | 0:ab4e012489ef | 11 | // Copyright (C) 2011 Mike McCauley |
davidr99 | 0:ab4e012489ef | 12 | // $Id: RHMesh.cpp,v 1.9 2015/08/13 02:45:47 mikem Exp $ |
davidr99 | 0:ab4e012489ef | 13 | |
davidr99 | 0:ab4e012489ef | 14 | #include <RHMesh.h> |
davidr99 | 0:ab4e012489ef | 15 | |
davidr99 | 0:ab4e012489ef | 16 | uint8_t RHMesh::_tmpMessage[RH_ROUTER_MAX_MESSAGE_LEN]; |
davidr99 | 0:ab4e012489ef | 17 | |
davidr99 | 0:ab4e012489ef | 18 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 19 | // Constructors |
davidr99 | 0:ab4e012489ef | 20 | RHMesh::RHMesh(RHGenericDriver& driver, uint8_t thisAddress) |
davidr99 | 0:ab4e012489ef | 21 | : RHRouter(driver, thisAddress) |
davidr99 | 0:ab4e012489ef | 22 | { |
davidr99 | 0:ab4e012489ef | 23 | } |
davidr99 | 0:ab4e012489ef | 24 | |
davidr99 | 0:ab4e012489ef | 25 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 26 | // Public methods |
davidr99 | 0:ab4e012489ef | 27 | |
davidr99 | 0:ab4e012489ef | 28 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 29 | // Discovers a route to the destination (if necessary), sends and |
davidr99 | 0:ab4e012489ef | 30 | // waits for delivery to the next hop (but not for delivery to the final destination) |
davidr99 | 0:ab4e012489ef | 31 | uint8_t RHMesh::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address, uint8_t flags) |
davidr99 | 0:ab4e012489ef | 32 | { |
davidr99 | 0:ab4e012489ef | 33 | if (len > RH_MESH_MAX_MESSAGE_LEN) |
davidr99 | 0:ab4e012489ef | 34 | return RH_ROUTER_ERROR_INVALID_LENGTH; |
davidr99 | 0:ab4e012489ef | 35 | |
davidr99 | 0:ab4e012489ef | 36 | if (address != RH_BROADCAST_ADDRESS) |
davidr99 | 0:ab4e012489ef | 37 | { |
davidr99 | 0:ab4e012489ef | 38 | RoutingTableEntry* route = getRouteTo(address); |
davidr99 | 0:ab4e012489ef | 39 | if (!route && !doArp(address)) |
davidr99 | 0:ab4e012489ef | 40 | return RH_ROUTER_ERROR_NO_ROUTE; |
davidr99 | 0:ab4e012489ef | 41 | } |
davidr99 | 0:ab4e012489ef | 42 | |
davidr99 | 0:ab4e012489ef | 43 | // Now have a route. Contruct an application layer message and send it via that route |
davidr99 | 0:ab4e012489ef | 44 | MeshApplicationMessage* a = (MeshApplicationMessage*)&_tmpMessage; |
davidr99 | 0:ab4e012489ef | 45 | a->header.msgType = RH_MESH_MESSAGE_TYPE_APPLICATION; |
davidr99 | 0:ab4e012489ef | 46 | memcpy(a->data, buf, len); |
davidr99 | 0:ab4e012489ef | 47 | return RHRouter::sendtoWait(_tmpMessage, sizeof(RHMesh::MeshMessageHeader) + len, address, flags); |
davidr99 | 0:ab4e012489ef | 48 | } |
davidr99 | 0:ab4e012489ef | 49 | |
davidr99 | 0:ab4e012489ef | 50 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 51 | bool RHMesh::doArp(uint8_t address) |
davidr99 | 0:ab4e012489ef | 52 | { |
davidr99 | 0:ab4e012489ef | 53 | // Need to discover a route |
davidr99 | 0:ab4e012489ef | 54 | // Broadcast a route discovery message with nothing in it |
davidr99 | 0:ab4e012489ef | 55 | MeshRouteDiscoveryMessage* p = (MeshRouteDiscoveryMessage*)&_tmpMessage; |
davidr99 | 0:ab4e012489ef | 56 | p->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST; |
davidr99 | 0:ab4e012489ef | 57 | p->destlen = 1; |
davidr99 | 0:ab4e012489ef | 58 | p->dest = address; // Who we are looking for |
davidr99 | 0:ab4e012489ef | 59 | uint8_t error = RHRouter::sendtoWait((uint8_t*)p, sizeof(RHMesh::MeshMessageHeader) + 2, RH_BROADCAST_ADDRESS); |
davidr99 | 0:ab4e012489ef | 60 | if (error != RH_ROUTER_ERROR_NONE) |
davidr99 | 0:ab4e012489ef | 61 | return false; |
davidr99 | 0:ab4e012489ef | 62 | |
davidr99 | 0:ab4e012489ef | 63 | // Wait for a reply, which will be unicast back to us |
davidr99 | 0:ab4e012489ef | 64 | // It will contain the complete route to the destination |
davidr99 | 0:ab4e012489ef | 65 | uint8_t messageLen = sizeof(_tmpMessage); |
davidr99 | 0:ab4e012489ef | 66 | // FIXME: timeout should be configurable |
davidr99 | 0:ab4e012489ef | 67 | unsigned long starttime = millis(); |
davidr99 | 0:ab4e012489ef | 68 | int32_t timeLeft; |
davidr99 | 0:ab4e012489ef | 69 | while ((timeLeft = RH_MESH_ARP_TIMEOUT - (millis() - starttime)) > 0) |
davidr99 | 0:ab4e012489ef | 70 | { |
davidr99 | 0:ab4e012489ef | 71 | if (waitAvailableTimeout(timeLeft)) |
davidr99 | 0:ab4e012489ef | 72 | { |
davidr99 | 0:ab4e012489ef | 73 | if (RHRouter::recvfromAck(_tmpMessage, &messageLen)) |
davidr99 | 0:ab4e012489ef | 74 | { |
davidr99 | 0:ab4e012489ef | 75 | if ( messageLen > 1 |
davidr99 | 0:ab4e012489ef | 76 | && p->header.msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE) |
davidr99 | 0:ab4e012489ef | 77 | { |
davidr99 | 0:ab4e012489ef | 78 | // Got a reply, now add the next hop to the dest to the routing table |
davidr99 | 0:ab4e012489ef | 79 | // The first hop taken is the first octet |
davidr99 | 0:ab4e012489ef | 80 | addRouteTo(address, headerFrom()); |
davidr99 | 0:ab4e012489ef | 81 | return true; |
davidr99 | 0:ab4e012489ef | 82 | } |
davidr99 | 0:ab4e012489ef | 83 | } |
davidr99 | 0:ab4e012489ef | 84 | } |
davidr99 | 0:ab4e012489ef | 85 | YIELD; |
davidr99 | 0:ab4e012489ef | 86 | } |
davidr99 | 0:ab4e012489ef | 87 | return false; |
davidr99 | 0:ab4e012489ef | 88 | } |
davidr99 | 0:ab4e012489ef | 89 | |
davidr99 | 0:ab4e012489ef | 90 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 91 | // Called by RHRouter::recvfromAck whenever a message goes past |
davidr99 | 0:ab4e012489ef | 92 | void RHMesh::peekAtMessage(RoutedMessage* message, uint8_t messageLen) |
davidr99 | 0:ab4e012489ef | 93 | { |
davidr99 | 0:ab4e012489ef | 94 | MeshMessageHeader* m = (MeshMessageHeader*)message->data; |
davidr99 | 0:ab4e012489ef | 95 | if ( messageLen > 1 |
davidr99 | 0:ab4e012489ef | 96 | && m->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE) |
davidr99 | 0:ab4e012489ef | 97 | { |
davidr99 | 0:ab4e012489ef | 98 | // This is a unicast RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE messages |
davidr99 | 0:ab4e012489ef | 99 | // being routed back to the originator here. Want to scrape some routing data out of the response |
davidr99 | 0:ab4e012489ef | 100 | // We can find the routes to all the nodes between here and the responding node |
davidr99 | 0:ab4e012489ef | 101 | MeshRouteDiscoveryMessage* d = (MeshRouteDiscoveryMessage*)message->data; |
davidr99 | 0:ab4e012489ef | 102 | addRouteTo(d->dest, headerFrom()); |
davidr99 | 0:ab4e012489ef | 103 | uint8_t numRoutes = messageLen - sizeof(RoutedMessageHeader) - sizeof(MeshMessageHeader) - 2; |
davidr99 | 0:ab4e012489ef | 104 | uint8_t i; |
davidr99 | 0:ab4e012489ef | 105 | // Find us in the list of nodes that were traversed to get to the responding node |
davidr99 | 0:ab4e012489ef | 106 | for (i = 0; i < numRoutes; i++) |
davidr99 | 0:ab4e012489ef | 107 | if (d->route[i] == _thisAddress) |
davidr99 | 0:ab4e012489ef | 108 | break; |
davidr99 | 0:ab4e012489ef | 109 | i++; |
davidr99 | 0:ab4e012489ef | 110 | while (i++ < numRoutes) |
davidr99 | 0:ab4e012489ef | 111 | addRouteTo(d->route[i], headerFrom()); |
davidr99 | 0:ab4e012489ef | 112 | } |
davidr99 | 0:ab4e012489ef | 113 | else if ( messageLen > 1 |
davidr99 | 0:ab4e012489ef | 114 | && m->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE) |
davidr99 | 0:ab4e012489ef | 115 | { |
davidr99 | 0:ab4e012489ef | 116 | MeshRouteFailureMessage* d = (MeshRouteFailureMessage*)message->data; |
davidr99 | 0:ab4e012489ef | 117 | deleteRouteTo(d->dest); |
davidr99 | 0:ab4e012489ef | 118 | } |
davidr99 | 0:ab4e012489ef | 119 | } |
davidr99 | 0:ab4e012489ef | 120 | |
davidr99 | 0:ab4e012489ef | 121 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 122 | // This is called when a message is to be delivered to the next hop |
davidr99 | 0:ab4e012489ef | 123 | uint8_t RHMesh::route(RoutedMessage* message, uint8_t messageLen) |
davidr99 | 0:ab4e012489ef | 124 | { |
davidr99 | 0:ab4e012489ef | 125 | uint8_t from = headerFrom(); // Might get clobbered during call to superclass route() |
davidr99 | 0:ab4e012489ef | 126 | uint8_t ret = RHRouter::route(message, messageLen); |
davidr99 | 0:ab4e012489ef | 127 | if ( ret == RH_ROUTER_ERROR_NO_ROUTE |
davidr99 | 0:ab4e012489ef | 128 | || ret == RH_ROUTER_ERROR_UNABLE_TO_DELIVER) |
davidr99 | 0:ab4e012489ef | 129 | { |
davidr99 | 0:ab4e012489ef | 130 | // Cant deliver to the next hop. Delete the route |
davidr99 | 0:ab4e012489ef | 131 | deleteRouteTo(message->header.dest); |
davidr99 | 0:ab4e012489ef | 132 | if (message->header.source != _thisAddress) |
davidr99 | 0:ab4e012489ef | 133 | { |
davidr99 | 0:ab4e012489ef | 134 | // This is being proxied, so tell the originator about it |
davidr99 | 0:ab4e012489ef | 135 | MeshRouteFailureMessage* p = (MeshRouteFailureMessage*)&_tmpMessage; |
davidr99 | 0:ab4e012489ef | 136 | p->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE; |
davidr99 | 0:ab4e012489ef | 137 | p->dest = message->header.dest; // Who you were trying to deliver to |
davidr99 | 0:ab4e012489ef | 138 | // Make sure there is a route back towards whoever sent the original message |
davidr99 | 0:ab4e012489ef | 139 | addRouteTo(message->header.source, from); |
davidr99 | 0:ab4e012489ef | 140 | ret = RHRouter::sendtoWait((uint8_t*)p, sizeof(RHMesh::MeshMessageHeader) + 1, message->header.source); |
davidr99 | 0:ab4e012489ef | 141 | } |
davidr99 | 0:ab4e012489ef | 142 | } |
davidr99 | 0:ab4e012489ef | 143 | return ret; |
davidr99 | 0:ab4e012489ef | 144 | } |
davidr99 | 0:ab4e012489ef | 145 | |
davidr99 | 0:ab4e012489ef | 146 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 147 | // Subclasses may want to override |
davidr99 | 0:ab4e012489ef | 148 | bool RHMesh::isPhysicalAddress(uint8_t* address, uint8_t addresslen) |
davidr99 | 0:ab4e012489ef | 149 | { |
davidr99 | 0:ab4e012489ef | 150 | // Can only handle physical addresses 1 octet long, which is the physical node address |
davidr99 | 0:ab4e012489ef | 151 | return addresslen == 1 && address[0] == _thisAddress; |
davidr99 | 0:ab4e012489ef | 152 | } |
davidr99 | 0:ab4e012489ef | 153 | |
davidr99 | 0:ab4e012489ef | 154 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 155 | bool RHMesh::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags) |
davidr99 | 0:ab4e012489ef | 156 | { |
davidr99 | 0:ab4e012489ef | 157 | uint8_t tmpMessageLen = sizeof(_tmpMessage); |
davidr99 | 0:ab4e012489ef | 158 | uint8_t _source; |
davidr99 | 0:ab4e012489ef | 159 | uint8_t _dest; |
davidr99 | 0:ab4e012489ef | 160 | uint8_t _id; |
davidr99 | 0:ab4e012489ef | 161 | uint8_t _flags; |
davidr99 | 0:ab4e012489ef | 162 | if (RHRouter::recvfromAck(_tmpMessage, &tmpMessageLen, &_source, &_dest, &_id, &_flags)) |
davidr99 | 0:ab4e012489ef | 163 | { |
davidr99 | 0:ab4e012489ef | 164 | MeshMessageHeader* p = (MeshMessageHeader*)&_tmpMessage; |
davidr99 | 0:ab4e012489ef | 165 | |
davidr99 | 0:ab4e012489ef | 166 | if ( tmpMessageLen >= 1 |
davidr99 | 0:ab4e012489ef | 167 | && p->msgType == RH_MESH_MESSAGE_TYPE_APPLICATION) |
davidr99 | 0:ab4e012489ef | 168 | { |
davidr99 | 0:ab4e012489ef | 169 | MeshApplicationMessage* a = (MeshApplicationMessage*)p; |
davidr99 | 0:ab4e012489ef | 170 | // Handle application layer messages, presumably for our caller |
davidr99 | 0:ab4e012489ef | 171 | if (source) *source = _source; |
davidr99 | 0:ab4e012489ef | 172 | if (dest) *dest = _dest; |
davidr99 | 0:ab4e012489ef | 173 | if (id) *id = _id; |
davidr99 | 0:ab4e012489ef | 174 | if (flags) *flags = _flags; |
davidr99 | 0:ab4e012489ef | 175 | uint8_t msgLen = tmpMessageLen - sizeof(MeshMessageHeader); |
davidr99 | 0:ab4e012489ef | 176 | if (*len > msgLen) |
davidr99 | 0:ab4e012489ef | 177 | *len = msgLen; |
davidr99 | 0:ab4e012489ef | 178 | memcpy(buf, a->data, *len); |
davidr99 | 0:ab4e012489ef | 179 | |
davidr99 | 0:ab4e012489ef | 180 | return true; |
davidr99 | 0:ab4e012489ef | 181 | } |
davidr99 | 0:ab4e012489ef | 182 | else if ( _dest == RH_BROADCAST_ADDRESS |
davidr99 | 0:ab4e012489ef | 183 | && tmpMessageLen > 1 |
davidr99 | 0:ab4e012489ef | 184 | && p->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST) |
davidr99 | 0:ab4e012489ef | 185 | { |
davidr99 | 0:ab4e012489ef | 186 | MeshRouteDiscoveryMessage* d = (MeshRouteDiscoveryMessage*)p; |
davidr99 | 0:ab4e012489ef | 187 | // Handle Route discovery requests |
davidr99 | 0:ab4e012489ef | 188 | // Message is an array of node addresses the route request has already passed through |
davidr99 | 0:ab4e012489ef | 189 | // If it originally came from us, ignore it |
davidr99 | 0:ab4e012489ef | 190 | if (_source == _thisAddress) |
davidr99 | 0:ab4e012489ef | 191 | return false; |
davidr99 | 0:ab4e012489ef | 192 | |
davidr99 | 0:ab4e012489ef | 193 | uint8_t numRoutes = tmpMessageLen - sizeof(MeshMessageHeader) - 2; |
davidr99 | 0:ab4e012489ef | 194 | uint8_t i; |
davidr99 | 0:ab4e012489ef | 195 | // Are we already mentioned? |
davidr99 | 0:ab4e012489ef | 196 | for (i = 0; i < numRoutes; i++) |
davidr99 | 0:ab4e012489ef | 197 | if (d->route[i] == _thisAddress) |
davidr99 | 0:ab4e012489ef | 198 | return false; // Already been through us. Discard |
davidr99 | 0:ab4e012489ef | 199 | |
davidr99 | 0:ab4e012489ef | 200 | // Hasnt been past us yet, record routes back to the earlier nodes |
davidr99 | 0:ab4e012489ef | 201 | addRouteTo(_source, headerFrom()); // The originator |
davidr99 | 0:ab4e012489ef | 202 | for (i = 0; i < numRoutes; i++) |
davidr99 | 0:ab4e012489ef | 203 | addRouteTo(d->route[i], headerFrom()); |
davidr99 | 0:ab4e012489ef | 204 | if (isPhysicalAddress(&d->dest, d->destlen)) |
davidr99 | 0:ab4e012489ef | 205 | { |
davidr99 | 0:ab4e012489ef | 206 | // This route discovery is for us. Unicast the whole route back to the originator |
davidr99 | 0:ab4e012489ef | 207 | // as a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE |
davidr99 | 0:ab4e012489ef | 208 | // We are certain to have a route there, because we just got it |
davidr99 | 0:ab4e012489ef | 209 | d->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE; |
davidr99 | 0:ab4e012489ef | 210 | RHRouter::sendtoWait((uint8_t*)d, tmpMessageLen, _source); |
davidr99 | 0:ab4e012489ef | 211 | } |
davidr99 | 0:ab4e012489ef | 212 | else if (i < _max_hops) |
davidr99 | 0:ab4e012489ef | 213 | { |
davidr99 | 0:ab4e012489ef | 214 | // Its for someone else, rebroadcast it, after adding ourselves to the list |
davidr99 | 0:ab4e012489ef | 215 | d->route[numRoutes] = _thisAddress; |
davidr99 | 0:ab4e012489ef | 216 | tmpMessageLen++; |
davidr99 | 0:ab4e012489ef | 217 | // Have to impersonate the source |
davidr99 | 0:ab4e012489ef | 218 | // REVISIT: if this fails what can we do? |
davidr99 | 0:ab4e012489ef | 219 | RHRouter::sendtoFromSourceWait(_tmpMessage, tmpMessageLen, RH_BROADCAST_ADDRESS, _source); |
davidr99 | 0:ab4e012489ef | 220 | } |
davidr99 | 0:ab4e012489ef | 221 | } |
davidr99 | 0:ab4e012489ef | 222 | } |
davidr99 | 0:ab4e012489ef | 223 | return false; |
davidr99 | 0:ab4e012489ef | 224 | } |
davidr99 | 0:ab4e012489ef | 225 | |
davidr99 | 0:ab4e012489ef | 226 | //////////////////////////////////////////////////////////////////// |
davidr99 | 0:ab4e012489ef | 227 | bool RHMesh::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags) |
davidr99 | 0:ab4e012489ef | 228 | { |
davidr99 | 0:ab4e012489ef | 229 | unsigned long starttime = millis(); |
davidr99 | 0:ab4e012489ef | 230 | int32_t timeLeft; |
davidr99 | 0:ab4e012489ef | 231 | while ((timeLeft = timeout - (millis() - starttime)) > 0) |
davidr99 | 0:ab4e012489ef | 232 | { |
davidr99 | 0:ab4e012489ef | 233 | if (waitAvailableTimeout(timeLeft)) |
davidr99 | 0:ab4e012489ef | 234 | { |
davidr99 | 0:ab4e012489ef | 235 | if (recvfromAck(buf, len, from, to, id, flags)) |
davidr99 | 0:ab4e012489ef | 236 | return true; |
davidr99 | 0:ab4e012489ef | 237 | YIELD; |
davidr99 | 0:ab4e012489ef | 238 | } |
davidr99 | 0:ab4e012489ef | 239 | } |
davidr99 | 0:ab4e012489ef | 240 | return false; |
davidr99 | 0:ab4e012489ef | 241 | } |
davidr99 | 0:ab4e012489ef | 242 | |
davidr99 | 0:ab4e012489ef | 243 | |
davidr99 | 0:ab4e012489ef | 244 |