├── LoRaSMS-D2D ├── LoRaSMS-D2D.h ├── LoRaSMS-D2D.ino ├── debug.h ├── htmlServer.h └── htmlServer.ino └── README.md /LoRaSMS-D2D/LoRaSMS-D2D.h: -------------------------------------------------------------------------------- 1 | extern void sendMessage(uint8_t *PayLoad, uint8_t Size); 2 | extern uint16_t numMessages(void); 3 | extern uint8_t getMessage(uint16_t msgIndex, uint8_t *printStr); 4 | 5 | /* 6 | enum 7 | { 8 | typeACK = 0x00, 9 | typeTX = 0x01, 10 | typeRX = 0x02, 11 | typeCMD = 0x03, 12 | }; 13 | */ 14 | 15 | #define PACKET_LENGTH 32 16 | #define HEADER_LENGTH 9 17 | #define SMS_LENGTH (PACKET_LENGTH - HEADER_LENGTH) 18 | 19 | -------------------------------------------------------------------------------- /LoRaSMS-D2D/LoRaSMS-D2D.ino: -------------------------------------------------------------------------------- 1 | #define ESP_TARGET 2 | 3 | #include 4 | #include 5 | #include "htmlServer.h" 6 | #include "crc16.h" 7 | //#include 8 | #include 9 | 10 | using namespace std; 11 | 12 | 13 | // Make sure these are wired up correctly! 14 | #ifdef ESP_TARGET 15 | #define RFM95_RST 26 16 | #define RFM95_CS 21 17 | #define RFM95_INT 25 18 | #else 19 | #define RFM95_RST 4 20 | #define RFM95_CS 5 21 | #define RFM95_INT 3 22 | #define BUILTIN_LED 13 23 | #endif 24 | 25 | /* ESP32 wiring as in my video. 26 | MISO purple 27 | MOSI gray 28 | CLK brown 29 | CS/NSS red 30 | RESET orange 31 | DIO0 yellow 32 | Vcc white 33 | Gnd black 34 | */ 35 | 36 | // Change to 434.0 or other frequency, must match RX's freq! 37 | #define RF95_FREQ 434.0 38 | 39 | // Singleton instance of the radio driver 40 | RH_RF95 rf95(RFM95_CS, RFM95_INT); 41 | 42 | // An arbitrary fixed maximum length of 128 bytes. You could increase it, but be careful of RX_BUFF_SIZE 43 | // Increasing both will overflow RAM easily. 44 | #define PACKET_LENGTH 128 45 | #define HEADER_LENGTH 8 46 | #define SMS_LENGTH (PACKET_LENGTH - HEADER_LENGTH) 47 | 48 | // I call this a "flexible union structure". Most messages will be under-sized. 49 | // So I make sure the payload is right at the end. Then I only transmit what I need 50 | // and not the full PACKET_LENGTH. 51 | union smsEntry 52 | { 53 | struct 54 | { 55 | uint8_t Type; // Message type. 56 | uint8_t Size; // Size of payload. 57 | uint16_t Index; // Incremented counter of packets. 58 | uint16_t Time; // Time sent. 59 | uint16_t CRC; // CRC of payload. 60 | uint8_t payload[SMS_LENGTH]; 61 | }; 62 | 63 | uint8_t packet[PACKET_LENGTH]; // This array is used to push out throught the LoRa module. 64 | }; 65 | 66 | // Hold message types. 67 | enum 68 | { 69 | typeACK = 0x00, 70 | typeTX = 0x01, 71 | typeRX = 0x02, 72 | typeCMD = 0x03, 73 | }; 74 | 75 | 76 | // We have great gobs of RAM on the ESP32. You could increase this to 1024, but be careful! 77 | #ifdef ESP_TARGET 78 | #define RX_BUFF_SIZE 256 79 | #define TX_BUFF_SIZE 256 80 | #else 81 | #define RX_BUFF_SIZE 5 82 | #define TX_BUFF_SIZE 5 83 | #endif 84 | 85 | // I gave up on the CircularBuffer library. 86 | //CircularBuffer RXbuffer; 87 | //CircularBuffer TXbuffer; 88 | RingBufCPP RXbuffer; 89 | RingBufCPP TXbuffer; 90 | 91 | smsEntry *Order[RX_BUFF_SIZE + TX_BUFF_SIZE]; // Keep track of the order of messages, not a nice way. Needs to change. 92 | uint16_t Counter = 0x00; // Used to keep track of total messages. 93 | 94 | 95 | signed int recPacket(uint16_t WaitFor); 96 | uint16_t CRC(uint8_t *Str, uint8_t Size); 97 | uint8_t addRx(smsEntry *msg); 98 | uint8_t addTx(uint8_t *PayLoad, uint8_t Size); 99 | void sendMessage(uint8_t *PayLoad, uint8_t Size); 100 | uint8_t getRx(uint8_t Index, uint8_t *printStr); 101 | void BlinkError(void); 102 | void BlinkNoReply(void); 103 | bool checkACK(uint8_t txIndex, uint8_t rxIndex); 104 | void rxWWW(void); 105 | void sendACK(uint8_t msgIndex); 106 | bool sendPacket(uint8_t msgIndex, uint16_t WaitFor); 107 | void sendTest(void); 108 | uint8_t getMessage(uint16_t msgIndex, uint8_t *printStr); 109 | 110 | 111 | void setup() 112 | { 113 | pinMode(BUILTIN_LED, OUTPUT); 114 | pinMode(RFM95_RST, OUTPUT); 115 | digitalWrite(RFM95_RST, HIGH); 116 | 117 | // while (!Serial); 118 | Serial.begin(115200); 119 | delay(100); 120 | 121 | #ifdef ESP_TARGET 122 | setup_wifi(); 123 | #endif 124 | PRINTLN("MickMake LoRa SMS"); 125 | 126 | // manual reset 127 | digitalWrite(RFM95_RST, LOW); 128 | delay(10); 129 | digitalWrite(RFM95_RST, HIGH); 130 | delay(10); 131 | 132 | while (!rf95.init()) 133 | { 134 | PRINTLN("LoRa radio init failed"); 135 | while(1); 136 | } 137 | PRINTLN("LoRa radio init OK!"); 138 | 139 | // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM 140 | if (!rf95.setFrequency(RF95_FREQ)) 141 | { 142 | PRINTLN("setFrequency failed"); 143 | while(1); 144 | } 145 | PRINT("Set Freq to: "); PRINTLN(RF95_FREQ); 146 | 147 | // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on 148 | 149 | // The default transmitter power is 13dBm, using PA_BOOST. 150 | // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 151 | // you can set transmitter powers from 5 to 23 dBm: 152 | rf95.setTxPower(5, false); 153 | 154 | rf95.setModemConfig(RH_RF95::Bw125Cr48Sf4096); 155 | //rf95.setModemConfig(RH_RF95::Bw500Cr45Sf128); 156 | //rf95.setModemConfig(RH_RF95::Bw125Cr45Sf128); 157 | //rf95.printRegisters(); 158 | rf95.setPreambleLength(8); 159 | 160 | #ifdef ESP_TARGET 161 | //sendMessage((uint8_t *)"abcdefghijkl", 12); 162 | #endif 163 | } 164 | 165 | 166 | void loop() 167 | { 168 | #ifdef ESP_TARGET 169 | wifi_loop(); 170 | #endif 171 | 172 | //sendMessage((uint8_t *)"abcdefgh", 8); 173 | signed int pIndex = recPacket(100); 174 | if (pIndex != -1) 175 | { 176 | sendACK(pIndex); 177 | PRINT("RxE:"); PRINT(RXbuffer.numElements()); 178 | PRINT(" TxE:"); PRINTLN(TXbuffer.numElements()); 179 | } 180 | 181 | //delay(2000); 182 | } 183 | 184 | 185 | void sendMessage(uint8_t *PayLoad, uint8_t Size) 186 | { 187 | uint8_t txI = addTx(PayLoad, Size); 188 | sendPacket(txI, 5000); 189 | 190 | //uint8_t foo[128]; 191 | //getMessage(txI, foo); 192 | //PRINT("SMS:"); 193 | //PRINTLN((char *)foo); 194 | } 195 | 196 | 197 | uint8_t addTx(uint8_t *PayLoad, uint8_t Size) 198 | { 199 | smsEntry TXmsg; 200 | 201 | TXmsg.Type = typeTX; 202 | TXmsg.Size = Size; 203 | TXmsg.Index = Counter; 204 | TXmsg.Time = (uint16_t)millis(); 205 | TXmsg.CRC = (uint16_t)CRC(PayLoad, Size); 206 | memcpy((uint8_t *)TXmsg.payload, (uint8_t *)PayLoad, Size); 207 | TXbuffer.add(TXmsg); 208 | Order[Counter] = TXbuffer.peek(TXbuffer.numElements() - 1); 209 | PRINT("Size:"); PRINTLN((uint8_t)TXmsg.Size); 210 | 211 | // PRINT("Tx "); printPacket(&TXmsg); 212 | // PRINT("TxE:"); PRINTLN(TXbuffer.numElements()); 213 | 214 | Counter++; 215 | // Need to reduce ring buffer to avoid over-run. 216 | if (TXbuffer.isFull()) 217 | { 218 | TXbuffer.pull(&TXmsg); 219 | //Counter--; 220 | } 221 | 222 | return(TXbuffer.numElements()-1); // Return the last index in the RB. 223 | } 224 | 225 | 226 | uint8_t addRx(smsEntry *msg) 227 | { 228 | smsEntry RXmsg; 229 | 230 | if (msg->Type != typeACK) 231 | RXmsg.Type = typeRX; 232 | else 233 | RXmsg.Type = msg->Type; 234 | 235 | RXmsg.Size = (uint8_t)msg->Size; 236 | RXmsg.Index = Counter; 237 | RXmsg.Time = (uint16_t)msg->Time; 238 | RXmsg.CRC = (uint16_t)msg->CRC; 239 | memcpy((uint8_t *)RXmsg.payload, (uint8_t *)(msg->payload), msg->Size); 240 | RXbuffer.add(RXmsg); 241 | Order[Counter] = RXbuffer.peek(RXbuffer.numElements()-1); 242 | 243 | // smsEntry *msg2 = RXbuffer.peek(RXbuffer.numElements()-1); PRINT("Rx "); printPacket(msg2); 244 | // PRINT("RxE:"); PRINTLN(RXbuffer.numElements()); 245 | 246 | Counter++; 247 | // Need to reduce ring buffer to avoid over-run. 248 | if (RXbuffer.isFull()) 249 | { 250 | RXbuffer.pull(&RXmsg); 251 | //Counter--; 252 | } 253 | 254 | return(RXbuffer.numElements()-1); // Return the last index in the RB. 255 | } 256 | 257 | 258 | void sendACK(uint8_t msgIndex) 259 | { 260 | if (RXbuffer.isEmpty()) 261 | return; 262 | 263 | smsEntry *RXmsg = RXbuffer.peek(msgIndex); 264 | PRINT("ACK "); printPacket(RXmsg); 265 | 266 | if (RXmsg->Type == typeACK) 267 | { 268 | //RXbuffer.pull(RXmsg); 269 | //Counter--; 270 | return; 271 | } 272 | 273 | smsEntry TXmsg; 274 | TXmsg.Type = typeACK; 275 | TXmsg.Size = (uint8_t)RXmsg->Size; 276 | TXmsg.Index = (uint16_t)RXmsg->Index; 277 | TXmsg.Time = (uint16_t)RXmsg->Time; 278 | TXmsg.CRC = (uint16_t)RXmsg->CRC; 279 | memcpy((uint8_t *)TXmsg.payload, (uint8_t *)(RXmsg->payload), RXmsg->Size); 280 | PRINT("Tx(ACK) "); printPacket(&TXmsg); 281 | 282 | rf95.send((uint8_t *)(TXmsg.packet), HEADER_LENGTH + TXmsg.Size); 283 | 284 | digitalWrite(BUILTIN_LED, LOW); 285 | rf95.waitPacketSent(); 286 | } 287 | 288 | 289 | uint8_t getRx(uint8_t msgIndex, uint8_t *printStr) 290 | { 291 | if (RXbuffer.isEmpty()) 292 | return(0); 293 | 294 | smsEntry *RXmsg = RXbuffer.peek(msgIndex); 295 | memcpy((char *)printStr, (uint8_t *)(RXmsg->payload), RXmsg->Size); 296 | return(1); 297 | } 298 | 299 | 300 | uint8_t getMessage(uint16_t msgIndex, uint8_t *printStr) 301 | { 302 | uint8_t Type; 303 | 304 | if (RXbuffer.isEmpty() && TXbuffer.isEmpty()) 305 | return(0); 306 | 307 | if (Order[msgIndex]->Type != typeACK) 308 | { 309 | memcpy(printStr, Order[msgIndex]->payload, Order[msgIndex]->Size); 310 | printStr[Order[msgIndex]->Size + 0x00] = 0x00; 311 | Type = Order[msgIndex]->Type; 312 | } 313 | 314 | return(Type); 315 | } 316 | 317 | 318 | uint16_t numMessages(void) 319 | { 320 | return(Counter); 321 | } 322 | 323 | 324 | bool sendPacket(uint8_t msgIndex, uint16_t WaitFor) 325 | { 326 | if (TXbuffer.isEmpty()) 327 | return(0); 328 | 329 | smsEntry *TXmsg = TXbuffer.peek(msgIndex); 330 | PRINT("Tx "); printPacket(TXmsg); 331 | 332 | rf95.send((uint8_t *)TXmsg->packet, HEADER_LENGTH + TXmsg->Size); 333 | 334 | digitalWrite(BUILTIN_LED, HIGH); 335 | rf95.waitPacketSent(); 336 | 337 | signed int rxI = recPacket(WaitFor); 338 | //PRINT("F:"); PRINTLN(rxI); 339 | if (rxI == -1) 340 | { 341 | PRINTLN("Timeout on ACK!"); 342 | return(0); 343 | } 344 | else 345 | { 346 | if (!checkACK(msgIndex, rxI)) 347 | return(0); 348 | } 349 | 350 | digitalWrite(BUILTIN_LED, LOW); 351 | 352 | return(1); 353 | } 354 | 355 | 356 | bool checkACK(uint8_t txIndex, uint8_t rxIndex) 357 | { 358 | if (TXbuffer.isEmpty() || RXbuffer.isEmpty()) 359 | return(0); 360 | 361 | smsEntry *TXmsg = TXbuffer.peek(txIndex); 362 | smsEntry *RXmsg = RXbuffer.peek(rxIndex); 363 | 364 | // PRINT("check Tx("); PRINT(txIndex); PRINT("):"); printPacket(TXmsg); PRINT("check Rx("); PRINT(rxIndex); PRINT("):"); printPacket(RXmsg); 365 | 366 | if (RXmsg->Type == typeACK) 367 | { 368 | if ((TXmsg->Time == RXmsg->Time) && (TXmsg->Size == RXmsg->Size) && (TXmsg->CRC == RXmsg->CRC)) 369 | { 370 | if (strncmp((char *)(RXmsg->payload), (char *)(TXmsg->payload), TXmsg->Size) == 0x00) 371 | { 372 | PRINT("ACK: OK "); 373 | PRINTLN(RXbuffer.numElements()); 374 | //smsEntry RXmsg; 375 | //RXbuffer.pull(&RXmsg); 376 | //Counter--; 377 | return(1); 378 | } 379 | PRINTLN("ACK: NOK1"); 380 | } 381 | PRINTLN("ACK: NOK2"); 382 | //smsEntry RXmsg; 383 | //RXbuffer.pull(&RXmsg); 384 | //Counter--; 385 | } 386 | PRINT("ACK: NOK3 "); 387 | 388 | return(0); 389 | } 390 | 391 | 392 | signed int recPacket(uint16_t WaitFor) 393 | { 394 | uint8_t buf[SMS_LENGTH]; 395 | uint8_t len = sizeof(buf); 396 | signed int RetVal = -1; 397 | WaitFor /= 10; 398 | smsEntry RXmsg; 399 | 400 | while(WaitFor--) 401 | { 402 | if (rf95.waitAvailableTimeout(10)) 403 | { 404 | if (rf95.recv((uint8_t *)(RXmsg.packet), &len)) 405 | { 406 | RetVal = addRx(&RXmsg); 407 | WaitFor = 0; 408 | } 409 | } 410 | } 411 | 412 | return(RetVal); 413 | } 414 | 415 | 416 | void printPacket(smsEntry *msg) 417 | { 418 | PRINT(" t:"); 419 | PRINT(msg->Type, HEX); 420 | PRINT(" s:"); 421 | PRINT(msg->Size, HEX); 422 | PRINT(" i:"); 423 | PRINT(msg->Index, HEX); 424 | PRINT(" w:"); 425 | PRINT(msg->Time, HEX); 426 | PRINT(" c:"); 427 | PRINT(msg->CRC, HEX); 428 | PRINT(" p:"); 429 | uint8_t p_size = HEADER_LENGTH + msg->Size; 430 | 431 | for(uint8_t i = 0x00; (i < p_size); i++) 432 | { 433 | char temp[4]; 434 | sprintf(temp, "%.2X:", (uint8_t)(msg->packet[i])); 435 | PRINT(temp); 436 | } 437 | PRINTLN(""); 438 | } 439 | 440 | 441 | void BlinkError(void) 442 | { 443 | for(int i=0; (i<3); i++) 444 | { 445 | digitalWrite(BUILTIN_LED, HIGH); 446 | delay(100); 447 | digitalWrite(BUILTIN_LED, LOW); 448 | delay(100); 449 | } 450 | } 451 | 452 | 453 | void BlinkNoReply(void) 454 | { 455 | for(int i=0; (i<3); i++) 456 | { 457 | digitalWrite(BUILTIN_LED, HIGH); 458 | delay(300); 459 | digitalWrite(BUILTIN_LED, LOW); 460 | delay(200); 461 | } 462 | } 463 | 464 | 465 | uint16_t CRC(uint8_t *Str, uint8_t Size) 466 | { 467 | CRC16 crc; 468 | char CRCcheck[SMS_LENGTH]; 469 | memcpy((char *)CRCcheck, (uint8_t *)Str, Size); 470 | 471 | crc.processBuffer((char *)CRCcheck, Size); 472 | 473 | return(crc.getCrc()); 474 | } 475 | 476 | -------------------------------------------------------------------------------- /LoRaSMS-D2D/debug.h: -------------------------------------------------------------------------------- 1 | // #define DEBUG 2 | 3 | #ifdef DEBUG 4 | #define PRINTLN(fmt, ...) 5 | #define PRINT(fmt, ...) 6 | #else 7 | #define PRINTLN(fmt, ...) Serial.println(fmt, ##__VA_ARGS__) 8 | #define PRINT(fmt, ...) Serial.print(fmt, ##__VA_ARGS__) 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /LoRaSMS-D2D/htmlServer.h: -------------------------------------------------------------------------------- 1 | extern void wifi_loop(void); 2 | extern void setup_wifi(void); 3 | 4 | -------------------------------------------------------------------------------- /LoRaSMS-D2D/htmlServer.ino: -------------------------------------------------------------------------------- 1 | #define ESP_TARGET 2 | 3 | // This define allows us to use this code on non-ESP based boards. 4 | #ifdef ESP_TARGET 5 | 6 | #ifdef ESP8266 7 | #include 8 | #include 9 | #include 10 | #include 11 | ESP8266WebServer server ( 80 ); 12 | #else 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | WebServer server ( 80 ); 19 | #endif 20 | 21 | #include "LoRaSMS-D2D.h" 22 | 23 | // Create aREST instance - not using it yet! 24 | //aREST rest = aREST(); 25 | 26 | 27 | const char *ssid = "ESP32ap"; 28 | const char *password = "12345678"; 29 | 30 | const char strHeader[] = "LoRa SMS"; 31 | const char strCSS[] = "body {font-family:\"Helvetica Neue\";font-size:20px;font-weight:normal;}\ 32 | section {max-width:450px;margin:50px auto;}\ 33 | section div {max-width:255px;word-wrap:break-word;margin-bottom:12px;line-height:24px;}\ 34 | section div:after {content:\"\";display:table;clear:both;}\ 35 | .clear {clear:both;}\ 36 | .TX {position:relative;padding:10px 20px;color:white;background:#0B93F6;border-radius:25px;float:right;}\ 37 | .TX:before {content:\"\";position:absolute;z-index:-1;bottom:-2px;right:-7px;height:20px;border-right:20px solid #0B93F6;border-bottom-left-radius:16px 14px;-webkit-transform:translate(0, -2px);}\ 38 | .TX:after {content:\"\";position:absolute;z-index:1;bottom:-2px;right:-56px;width:26px;height:20px;background:white;border-bottom-left-radius:10px;-webkit-transform:translate(-30px, -2px);}\ 39 | .RX {position:relative;padding:10px 20px;background:#E5E5EA;border-radius:25px;color:black;float:left;}\ 40 | .RX:before {content:\"\";position:absolute;z-index:2;bottom:-2px;left:-7px;height:20px;border-left:20px solid #E5E5EA;border-bottom-right-radius:16px 14px;-webkit-transform:translate(0, -2px);}\ 41 | .RX:after {content:\"\";position:absolute;z-index:3;bottom:-2px;left:4px;width:26px;height:20px;background:white;border-bottom-right-radius:10px;-webkit-transform:translate(-30px, -2px);}\ 42 | "; 43 | const char strTitle[] = "

Send a LoRa SMS

"; 44 | const char strForm[] = "

Text:

"; 45 | const char strFooter[] = ""; 46 | const char strSep[] = "
"; 47 | const char strTxPre[] = "

"; 48 | const char strRxPre[] = "

"; 49 | const char strPost[] = "

"; 50 | 51 | 52 | void handleRoot(void) 53 | { 54 | char html[2500] = {0x00}; 55 | char uptime[32]; 56 | int sec = millis() / 1000; 57 | int min = sec / 60; 58 | int hr = min / 60; 59 | 60 | // Print out the web page with historic messages, regardless of state. 61 | strcat(html, strHeader); 62 | strcat(html, "

"); strcat(html, ssid); strcat(html, "

"); 63 | strcat(html, ""); 66 | //strcat(html, strTitle); 67 | //snprintf(uptime, 32, "

Uptime: %02d:%02d:%02d

", hr, min % 60, sec % 60); 68 | //strcat(html, uptime); 69 | strcat(html, "
"); 70 | 71 | PRINT("NM:"); PRINTLN(numMessages()); 72 | // Loop through the messages. 73 | for(uint8_t msgIndex = 0x00; (msgIndex < numMessages()); msgIndex++) 74 | { 75 | PRINT("I:"); PRINTLN(msgIndex); 76 | uint8_t printStr[SMS_LENGTH]; 77 | uint8_t Type; 78 | Type = getMessage(msgIndex, printStr); 79 | 80 | if (Type != typeACK) 81 | { 82 | strcat(html, strSep); 83 | if (Type == typeTX) 84 | strcat(html, strTxPre); 85 | else if (Type == typeRX) 86 | strcat(html, strRxPre); 87 | strcat(html, (char *)printStr); 88 | strcat(html, strPost); 89 | } 90 | } 91 | 92 | strcat(html, strSep); 93 | strcat(html, strForm); 94 | strcat(html, "
"); 95 | strcat(html, strFooter); 96 | 97 | 98 | // HTML POST processing. 99 | if (server.method() == HTTP_GET) 100 | { 101 | PRINTLN("# GET /"); 102 | } 103 | else if (server.method() == HTTP_POST) 104 | { 105 | PRINTLN("# POST /"); 106 | if (server.hasArg("sms")) 107 | { 108 | PRINT("SMS:"); 109 | PRINTLN(server.arg("sms")); 110 | sendMessage((uint8_t *)(server.arg("sms").c_str()), server.arg("sms").length()); 111 | } 112 | } 113 | else if (server.method() == HTTP_POST) 114 | { 115 | PRINTLN("# ?"); 116 | } 117 | 118 | server.send(200, "text/html", html); 119 | } 120 | 121 | 122 | // Throw a 404. 123 | void handleNotFound(void) 124 | { 125 | String message = "File Not Found\n\n"; 126 | message += "URI: "; 127 | message += server.uri(); 128 | message += "\nMethod: "; 129 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 130 | message += "\nArguments: "; 131 | message += server.args(); 132 | message += "\n"; 133 | 134 | for(uint8_t i = 0; i < server.args(); i++) 135 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 136 | 137 | server.send(404, "text/plain", message); 138 | } 139 | 140 | 141 | // Handle all the Wiffeee setup. I said, Wiffeeee. 142 | void setup_wifi(void) 143 | { 144 | WiFi.mode(WIFI_AP); 145 | WiFi.softAP(ssid, password); 146 | IPAddress myIP = WiFi.softAPIP(); 147 | PRINT("AP IP address: "); 148 | PRINTLN(myIP); 149 | WiFi.softAPsetHostname(ssid); 150 | WiFi.setHostname(ssid); 151 | 152 | if (MDNS.begin("esp32")) 153 | { 154 | PRINTLN("MDNS responder started"); 155 | } 156 | 157 | server.on("/", handleRoot); 158 | server.on("/style.css", []() {PRINTLN("# GET /style.css"); server.sendHeader("Cache-Control","max-age=2592000, public"); server.send(200, "text/css", strCSS);}); 159 | server.on("/inline", []() {server.send(200, "text/plain", "this works as well");}); 160 | server.onNotFound(handleNotFound); 161 | 162 | Serial.setDebugOutput(true); 163 | PRINT("ESP32 SDK: "); 164 | PRINTLN(ESP.getSdkVersion()); 165 | 166 | server.begin(); 167 | PRINTLN("HTTP server started"); 168 | } 169 | 170 | 171 | void wifi_loop(void) 172 | { 173 | server.handleClient(); 174 | } 175 | 176 | #endif 177 | 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The LoRaSMS Device 2 Device code. 2 | Source code for my LoRaSMS project. Current features. 3 | - [x] Single to many messages. 4 | - [x] Simple web interface, (no need for an app). 5 | 6 | Features I'll be adding: 7 | - [ ] Better security, (currently it's open). 8 | - [ ] Addressing. 9 | - [ ] Low Power modes. 10 | - [ ] SPI flash for storing messages. 11 | - [ ] Better web interface. 12 | - [ ] Maybe even an app! 13 | 14 | Check out [my tutorial video](https://www.youtube.com/watch?v=b_RPwtxtNdc) on how I made it. 15 | Also check out [my website](https://www.mickmake.com/archives/4640) for further details. 16 | 17 | 18 | ## Support :+1: 19 | If you want to support me, then head on over to [my Patreon page](http://patreon.com/MickMake). 20 | 21 | 22 | ## License 23 | This source code is covered under the GPL! Use it, destroy it, make it better. 24 | 25 | 26 | --------------------------------------------------------------------------------