219 lines
6.1 KiB
C++
219 lines
6.1 KiB
C++
#include "configuration.h"
|
|
#include "defines.h"
|
|
#include "LoRaWan_APP.h"
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <Wire.h>
|
|
#include "HT_SSD1306Wire.h"
|
|
|
|
#include <WiFi.h>
|
|
#include <WiFiClient.h>
|
|
#include <WebServer.h>
|
|
#include <WiFiAP.h>
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <iomanip>
|
|
|
|
|
|
|
|
|
|
const uint32_t MAGIC = 0xaffe0001;
|
|
|
|
|
|
config_t myConfig = {
|
|
.magic = MAGIC,
|
|
.appEui = { 0xa0, 0x57, 0x81, 0x00, 0x01, 0x12, 0xaa, 0xf3 },
|
|
.appKey = { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 },
|
|
.modbus_poll_slots = {
|
|
{ .typ = INPUT_REGISTERS, .id = 7, .address = 0x01 },
|
|
{ .typ = INPUT_REGISTERS, .id = 7, .address = 0x02 },
|
|
{ .typ = UNASSIGNED, .id = 0, .address = 0 },
|
|
}
|
|
};
|
|
|
|
uint8_t devEui[8];
|
|
uint8_t appEui[8];
|
|
uint8_t appKey[16];
|
|
uint8_t nwkSKey[16];
|
|
uint8_t appSKey[16];
|
|
uint32_t devAddr;
|
|
LoRaMacRegion_t loraWanRegion;
|
|
bool overTheAirActivation;
|
|
|
|
|
|
SSD1306Wire confDisplay(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED);; // addr , freq , i2c group , resolution , rst
|
|
WebServer server(80);
|
|
|
|
const char *ssid = "ModbusLoraConf";
|
|
|
|
#define TEST_PASSWORD
|
|
#ifndef TEST_PASSWORD
|
|
char password[12];
|
|
#else
|
|
const char *password = "test1234";
|
|
#endif
|
|
|
|
extern uint8_t devEui[8];
|
|
|
|
bool configSaved = false;
|
|
int configParsingFailed = 0;
|
|
enum { PARSING_OK, PARSING_INVALID, PARSING_TOO_MANY, PARSING_TOO_FEW } configParsingError;
|
|
|
|
static void displayStatus(uint8_t numOfStations) {
|
|
confDisplay.clear();
|
|
confDisplay.display();
|
|
confDisplay.drawString(1, 0, "Config Mode running");
|
|
char buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "SSID: %s", ssid);
|
|
confDisplay.drawString(1, 12, buf);
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "Key: %s", password);
|
|
confDisplay.drawString(1, 24, buf);
|
|
confDisplay.display();
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "Num of stations: %d", numOfStations);
|
|
confDisplay.drawString(1, 36, buf);
|
|
confDisplay.display();
|
|
}
|
|
|
|
static void displayInit() {
|
|
digitalWrite(Vext,LOW);
|
|
confDisplay.init();
|
|
confDisplay.setFont(ArialMT_Plain_10);
|
|
confDisplay.setTextAlignment(TEXT_ALIGN_LEFT);
|
|
confDisplay.screenRotate(ANGLE_180_DEGREE);
|
|
confDisplay.clear();
|
|
confDisplay.drawString(1, 0, "Config Mode starting");
|
|
confDisplay.display();
|
|
}
|
|
|
|
|
|
static void handleRoot() {
|
|
// bool configValid = (configBlock.magic == MAGIC);
|
|
std::stringstream buffer;
|
|
buffer << "<!doctype html"
|
|
"<html lang=\"en\">"
|
|
" <head>"
|
|
" <title>Modbus LoRaWAN Gateway</title>"
|
|
" </head>"
|
|
" <body>"
|
|
" <h1>Modbus LoRaWAN Gateway - Configuration Page</h1>";
|
|
if (configSaved) {
|
|
configSaved = false;
|
|
buffer << "<h2>Configuration saved</h2>";
|
|
}
|
|
if (configParsingFailed) {
|
|
configParsingFailed = 0;
|
|
buffer << "<h2>Error when parsing field " << configParsingFailed << ", error " << configParsingError << "</h2>";
|
|
}
|
|
buffer << " <form action=\"config\" method=\"POST\">"
|
|
" <table>"
|
|
" <tr>"
|
|
" <td>";
|
|
buffer << " <textarea name=\"config\" cols=\"50\" rows=\"40\">";
|
|
|
|
buffer << "# Configuration of the Modbus LoRaWAN Gateway";
|
|
|
|
buffer << " </textarea";
|
|
buffer << " </td><td valign=\"top\">"
|
|
" Edit the textarea beside. <br/>"
|
|
" Make sure to keep all parameters within and follow the shown format.<br/>"
|
|
" You can use the # char to introduce line-based comments.<br/>"
|
|
" </td>"
|
|
" </tr>"
|
|
" <tr><td colspan=\"2\">"
|
|
" <button type=\"submit\">Save</button>"
|
|
" </td></tr>"
|
|
" </table>"
|
|
" </form>"
|
|
" </body"
|
|
"</html";
|
|
|
|
server.send(200, "text/html", buffer.str().c_str());
|
|
}
|
|
|
|
static void parseField(char* arg, int fieldNum, int reqTokens, uint8_t *dest) {
|
|
int i = 0;
|
|
for (char *part = strtok(arg, ":"); part; part = strtok(NULL, ":")) {
|
|
Serial.println(part);
|
|
char *errPtr;
|
|
long v = strtol(part, &errPtr, 16);
|
|
if (errPtr && *errPtr) {
|
|
Serial.printf("some error happened, %p %02x\n\r", errPtr, *errPtr);
|
|
configParsingFailed = fieldNum;
|
|
configParsingError = PARSING_INVALID;
|
|
break;
|
|
}
|
|
if (i >= reqTokens) {
|
|
Serial.println("too many tokens");
|
|
configParsingFailed = fieldNum;
|
|
configParsingError = PARSING_TOO_MANY;
|
|
break;
|
|
}
|
|
dest[i] = v;
|
|
i++;
|
|
}
|
|
if ((i != reqTokens) && (configParsingFailed == 0)) {
|
|
Serial.println("too few tokens");
|
|
configParsingFailed = fieldNum;
|
|
configParsingError = PARSING_TOO_FEW;
|
|
}
|
|
|
|
}
|
|
|
|
static void handleConfigSave() {
|
|
char *configText = (char*)server.arg("config").c_str();
|
|
Serial.println(configText);
|
|
|
|
|
|
configSaved = true;
|
|
server.sendHeader("Location", String("/"), true);
|
|
server.send(302, "text/plain", "");
|
|
}
|
|
|
|
|
|
void configurationSetup() {
|
|
#ifndef TEST_PASSWORD
|
|
memset(password, 0, sizeof(password));
|
|
for (int i = 0; i < sizeof(password) - 1; i++) {
|
|
password[i] = random(65, 90);
|
|
}
|
|
#endif
|
|
|
|
WiFi.softAP(ssid, password);
|
|
IPAddress myIP = WiFi.softAPIP();
|
|
|
|
displayInit();
|
|
displayStatus(0);
|
|
|
|
LoRaWAN.generateDeveuiByChipID();
|
|
server.on("/", handleRoot);
|
|
server.on("/config", handleConfigSave);
|
|
server.begin();
|
|
}
|
|
|
|
|
|
void configurationLoop() {
|
|
static uint8_t numOfStations = 255;
|
|
uint8_t currentNumOfStations = WiFi.softAPgetStationNum();
|
|
if (numOfStations != currentNumOfStations) {
|
|
displayStatus(currentNumOfStations);
|
|
numOfStations = currentNumOfStations;
|
|
}
|
|
|
|
server.handleClient();
|
|
}
|
|
|
|
|
|
|
|
void configLoad() {
|
|
memcpy(appEui, myConfig.appEui, sizeof(appEui));
|
|
memcpy(appKey, myConfig.appKey, sizeof(appKey));
|
|
loraWanRegion = LORAMAC_REGION_EU868;
|
|
overTheAirActivation = true;
|
|
} |