Skip to content

Commit

Permalink
feature/lora: Add LoraWAN communications on supported hardware
Browse files Browse the repository at this point in the history
* Allows OpenEVSE units (with a LoraWAN compatible modem)
  to announce their status up to several miles to TheThingsNetwork
  or Helium IoT gateways.
* Must be using a ESP32 with a built-in LoRa modem, or by
  manually attaching a LoRA modem to a stock ESP32
* Needs gui changes too.. however holding on that until better tested
  • Loading branch information
kallisti5 committed Feb 2, 2023
1 parent 751d837 commit 86de7ba
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 1 deletion.
21 changes: 21 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ build_flags =
build_partitions = min_spiffs.csv
build_partitions_debug = min_spiffs_debug.csv

# Used for LoRaWAN builds
# https://github.com/mcci-catena/arduino-lmic#selecting-the-lorawan-region-configuration
lora_lib =
MCCI LoRaWAN LMIC library
aparcar/CayenneLPP@^1.3.0
lora_build_flags =
-D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS
-D hal_init=LMICHAL_init ; Workaround mcci arduino-lmic bug 714 on esp32
-D CFG_us915 ; USA 915Mhz

neopixel_lib = adafruit/Adafruit [email protected]


Expand Down Expand Up @@ -316,9 +326,13 @@ upload_speed = 921600

[env:openevse_esp32-heltec-wifi-lora-v2]
board = heltec_wifi_lora_32_V2
lib_deps =
${common.lib_deps}
${common.lora_lib}
build_flags =
${common.build_flags}
${common.src_build_flags}
${common.lora_build_flags}
${common.version}.dev
-D DEBUG_PORT=Serial
-D WIFI_LED=25
Expand All @@ -328,3 +342,10 @@ build_flags =
-D RAPI_PORT=Serial1
-D RX1=25
-D TX1=27
-D CFG_sx1276_radio ; SX1275 radio
-D ENABLE_LORA=1
-D LORA_NSS=18
-D LORA_RST=14
-D LORA_DIO0=26
-D LORA_DIO1=35
-D LORA_DIO2=34
23 changes: 23 additions & 0 deletions src/app_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ String mqtt_vehicle_range;
String mqtt_vehicle_eta;
String mqtt_announce_topic;

// LoraWAN network settings
String lora_deveui;
String lora_appeui;
String lora_appkey;

// OCPP 1.6 Settings
String ocpp_server;
String ocpp_chargeBoxId;
Expand Down Expand Up @@ -181,6 +186,11 @@ ConfigOpt *opts[] =
// RFID storage
new ConfigOptDefenition<String>(rfid_storage, "", "rfid_storage", "rs"),

// Lora settings
new ConfigOptDefenition<String>(lora_deveui, "", "lora_deveui", "lde"),
new ConfigOptDefenition<String>(lora_appeui, "", "lora_appeui", "lae"),
new ConfigOptDefenition<String>(lora_appeui, "", "lora_appkey", "lak"),

#if RGB_LED
// LED brightness
new ConfigOptDefenition<uint8_t>(led_brightness, LED_DEFAULT_BRIGHTNESS, "led_brightness", "lb"),
Expand Down Expand Up @@ -439,6 +449,19 @@ config_save_ohm(bool enable, String qohm)
user_config.commit();
}

void
config_save_lora(bool enable, String devEui, String appEui, String appKey)
{
uint32_t newflags = flags & ~CONFIG_LORA;
if(enable)
newflags |= CONFIG_LORA;

user_config.set("flags", newflags);
user_config.set("lora_deveui", devEui);
user_config.set("lora_appeui", appEui);
user_config.set("lora_appkey", appKey);
}

void
config_save_rfid(bool enable, String storage){
uint32_t newflags = flags & ~CONFIG_RFID;
Expand Down
15 changes: 15 additions & 0 deletions src/app_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ extern String mqtt_vehicle_range;
extern String mqtt_vehicle_eta;
extern String mqtt_announce_topic;

// LoraWAN Settings
extern String lora_deveui;
extern String lora_appeui;
extern String lora_appkey;

// OCPP 1.6 Settings
extern String ocpp_server;
extern String ocpp_chargeBoxId;
Expand Down Expand Up @@ -97,6 +102,7 @@ extern uint32_t flags;
#define CONFIG_OCPP_AUTO_AUTH (1 << 22)
#define CONFIG_OCPP_OFFLINE_AUTH (1 << 23)
#define CONFIG_THREEPHASE (1 << 24)
#define CONFIG_LORA (1 << 25)


inline bool config_emoncms_enabled() {
Expand Down Expand Up @@ -127,6 +133,10 @@ inline bool config_mqtt_reject_unauthorized() {
return 0 == (flags & CONFIG_MQTT_ALLOW_ANY_CERT);
}

inline bool config_lora_enabled() {
return CONFIG_LORA == (flags & CONFIG_LORA);
}

inline bool config_ocpp_enabled() {
return CONFIG_SERVICE_OCPP == (flags & CONFIG_SERVICE_OCPP);
}
Expand Down Expand Up @@ -232,6 +242,11 @@ extern void config_save_ohm(bool enable, String qohm);
// -------------------------------------------------------------------
extern void config_save_rfid(bool enable, String storage);

// -------------------------------------------------------------------
// Save Lora settings
// -------------------------------------------------------------------
extern void config_save_lora(bool enable, String devEui, String appEui, String appKey);

// -------------------------------------------------------------------
// Save the flags
// -------------------------------------------------------------------
Expand Down
182 changes: 182 additions & 0 deletions src/lora.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
* Copyright (c) 2019-2020 Alexander von Gluck IV for OpenEVSE
*
* -------------------------------------------------------------------
*
* Additional Adaptation of OpenEVSE ESP Wifi
* by Trystan Lea, Glyn Hudson, OpenEnergyMonitor
* All adaptation GNU General Public License as below.
*
* -------------------------------------------------------------------
*
* This file is part of Open EVSE.
* Open EVSE is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
* Open EVSE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Open EVSE; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef ENABLE_LORA

#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>

#include "emonesp.h"
#include "input.h"
#include "lora.h"

#include "app_config.h"


#define LORA_HTOI(c) ((c<='9')?(c-'0'):((c<='F')?(c-'A'+10):((c<='f')?(c-'a'+10):(0))))
#define LORA_TWO_HTOI(h, l) ((LORA_HTOI(h) << 4) + LORA_HTOI(l))
#define LORA_HEX_TO_BYTE(a, h, n) { for (int i = 0; i < n; i++) (a)[i] = LORA_TWO_HTOI(h[2*i], h[2*i + 1]); }
#define LORA_DEVADDR(a) (uint32_t) ((uint32_t) (a)[3] | (uint32_t) (a)[2] << 8 | (uint32_t) (a)[1] << 16 | (uint32_t) (a)[0] << 24)

#define ANNOUNCE_INTERVAL 30 * 1000 // (In Milliseconds)


// LoRa module pin mapping
const lmic_pinmap lmic_pins = {
.nss = LORA_NSS,
.rxtx = LMIC_UNUSED_PIN,
.rst = LORA_RST,
.dio = {LORA_DIO0, LORA_DIO1, LORA_DIO2},
};

// Used for OTAA, not used (yet)
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }

void
create_rapi_cayennelpp(EvseManager* _evse, CayenneLPP* lpp)
{
if (_evse == NULL) {
DBUGF("Corrupt EvseManager!")
return;
}
if (lpp == NULL) {
DBUGF("Corrupt CayenneLPP buffer!")
return;
}

lpp->reset();
lpp->addDigitalInput(0, _evse.getEvseState());
lpp->addAnalogInput(1, _evse.getVoltage());
lpp->addAnalogInput(2, _evse.getAmps());
lpp->addAnalogInput(3, _evse.getChargeCurrent());
lpp->addDigitalInput(4, _evse.getSessionElapsed() / 60);
if(evse.isTemperatureValid(EVSE_MONITOR_TEMP_MONITOR))
lpp->addTemperature(5, _evse->getTemperature(EVSE_MONITOR_TEMP_MONITOR) * TEMP_SCALE_FACTOR);
if(evse.isTemperatureValid(EVSE_MONITOR_TEMP_MAX))
lpp->addTemperature(6, _evse->getTemperature(EVSE_MONITOR_TEMP_MAX) * TEMP_SCALE_FACTOR);
}

/// Reset LoRa modem. Reload LoRaWAN keys
void onEvent(ev_t ev) {
switch (ev) {
case EV_TXCOMPLETE:
DBUGF("LoRa: TX Complete.");
// LoRaWAN transmission complete
if (LMIC.txrxFlags & TXRX_ACK) {
// Received ack
DBUGF("LoRa: TX ack.");
}
break;
case EV_TXSTART:
DBUGF("LoRa: TX Begin.");
break;
default:
// Ignore anything else for now
break;
}
}

LoraTask::LoraTask()
:
MicroTasks::Task()
{
}

void
LoraTask::begin(EvseManager &evse)
{
_evse = &evse;
MicroTask.startTask(this);
}

/// Initial setup of LoRa modem.
void
LoraTask::setup()
{
Profile_Start(LoraTask::setup);

os_init();
modem_reset();

Profile_End(LoraTask::setup, 1);
}

/// Reset LoRa modem. Reload LoRaWAN keys
void
LoraTask::modem_reset()
{
Profile_Start(LoraTask::modem_reset);
// LoRaWAN credentials to use
uint8_t DEVADDR[4];
uint8_t NWKSKEY[16];
uint8_t APPSKEY[16];

LORA_HEX_TO_BYTE(DEVADDR, lora_deveui.c_str(), 4);
LORA_HEX_TO_BYTE(NWKSKEY, lora_appeui.c_str(), 16);
LORA_HEX_TO_BYTE(APPSKEY, lora_appkey.c_str(), 16);

LMIC_reset();
LMIC_setSession (0x13, LORA_DEVADDR(DEVADDR), NWKSKEY, APPSKEY);
LMIC_setAdrMode(0);
LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100);
LMIC_selectSubBand(1);
LMIC_setLinkCheckMode(0);
LMIC.dn2Dr = DR_SF7;
Profile_End(LoraTask::modem_reset, 1);
}

/// Announce our status to LoraWAN if it's time
void
LoraTask::publish(CayenneLPP* lpp)
{
Profile_Start(LoraTask::publish);
DBUGF("LoRa: Starting LoRaWAN broadcast...");
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
DBUGF("LoRa: Modem busy. Retry later");
return;
}
LMIC_setTxData2(1, lpp->getBuffer(), lpp->getSize(), false);
Profile_End(LoraTask::publish, 1);
}

unsigned long
LoraTask::loop(MicroTasks::WakeReason reason)
{
if (!config_lora_enabled())
return 1000;

CayenneLPP lpp(24);
create_rapi_cayennelpp(_evse, &lpp);
lora.publish(&lpp);
return ANNOUNCE_INTERVAL;
}

LoraTask lora;

#endif /* ENABLE_LORA */
55 changes: 55 additions & 0 deletions src/lora.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2019-2023 Alexander von Gluck IV for OpenEVSE
*
* -------------------------------------------------------------------
*
* Additional Adaptation of OpenEVSE ESP Wifi
* by Trystan Lea, Glyn Hudson, OpenEnergyMonitor
* All adaptation GNU General Public License as below.
*
* -------------------------------------------------------------------
*
* This file is part of Open EVSE.
* Open EVSE is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
* Open EVSE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Open EVSE; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef ENABLE_LORA
#ifndef _LORA_H
#define _LORA_H

#include <CayenneLPP.h>
#include <MicroTasks.h>

#include "evse_man.h"

void create_rapi_cayennelpp(EvseManager* _evse, CayenneLPP* lpp);

class LoraTask : public MicroTasks::Task {
private:
EvseManager* _evse;

protected:
void setup();
unsigned long loop(MicroTasks::WakeReason reason);
void publish(CayenneLPP* lpp);
void modem_reset();

public:
LoraTask();
void begin(EvseManager &evse);
};

extern LoraTask lora;

#endif // _LORA_H
#endif /* ENABLE_LORA */
8 changes: 7 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "divert.h"
#include "ota.h"
#include "lcd.h"
#include "lora.h"
#include "openevse.h"
#include "root_ca.h"
#include "espal.h"
Expand Down Expand Up @@ -156,6 +157,11 @@ void setup()

ocpp.begin(evse, lcd, eventLog, rfid);

#ifdef ENABLE_LORA
// initialise LoRA if supported
lora.begin(evse);
#endif

shaper.begin(evse);

lcd.display(F("OpenEVSE WiFI"), 0, 0, 0, LCD_CLEAR_LINE);
Expand Down Expand Up @@ -335,4 +341,4 @@ void handle_serial()
DEBUG_PORT.printf("{\"code\":200,\"msg\":\"%s\"}\n", config_modified ? "done" : "no change");
}
}
}
}

0 comments on commit 86de7ba

Please sign in to comment.