Skip to content

Instantly share code, notes, and snippets.

@justind000
Last active May 4, 2020 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justind000/d515c6f8190b9d7192bda25220f5552b to your computer and use it in GitHub Desktop.
Save justind000/d515c6f8190b9d7192bda25220f5552b to your computer and use it in GitHub Desktop.
Helium - uFire Project with E-paper display
/* Prior to compiling, install the following libraries:
Isolated EC Probe Interface v1.2.1
Isolated ISE Probe Interface v1.2.0
ArduinoJson v6.14.1
ESP32_LoRaWAN https://github.com/HelTecAutomation/ESP32_LoRaWAN
click links below in the #include section
Follow directions to install the development repository here:
https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series
Select Wireless Stick Lite from the Board list in the Tools menu
and select the appropriate options from the Tools menu
*/
#include <ESP32_LoRaWAN.h>
#include <uFire_EC.h> // http://librarymanager/All#Isolated_EC_Probe_Interface
#include <uFire_pH.h> // http://librarymanager/All#Isolated_ISE_Probe_Interface
#include <uFire_ORP.h> // http://librarymanager/All#Isolated_ISE_Probe_Interface
#include <ArduinoJson.h> // http://librarymanager/All#ArduinoJson
#include "paper.h"
// license for Heltec ESP32 LoRaWAN
uint32_t license[4] = { 0x93401581, 0x1DC4DF49, 0x1637CA6F, 0x1D40CA9F };
// OTAA para
uint8_t DevEui[] = { };
uint8_t AppEui[] = { };
uint8_t AppKey[] = { };
// ABP para
uint8_t NwkSKey[] = {};
uint8_t AppSKey[] = {};
uint32_t DevAddr;
// configure our device for the Helium LoRaWAN network
DeviceClass_t loraWanClass = CLASS_A; // Class A = a battery operated device
uint32_t appTxDutyCycle = 21600000; // how often to wake-up, take and send a measurement, in ms
bool overTheAirActivation = true; // We want to use over the air activation rather than ABP https://www.lairdconnect.com/support/faqs/what-difference-between-abp-and-otaa
bool loraWanAdr = false; // we don't want to use Adaptive Data Rate https://www.thethingsnetwork.org/docs/lorawan/adaptive-data-rate.html
bool isTxConfirmed = false; // we also don't want to use confirmed transmissions
uint8_t appPort = 2; // always 2
uint8_t confirmedNbTrials = 8; // the number of re-transmits, this isn't used with isTxConfirmed = false, but the library still wants to know
uint8_t debugLevel = LoRaWAN_DEBUG_LEVEL; // how much extra data the library can print on serial to debug connection issues, this is set in the Tools menu
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION; // For the US, we must use US915, it's set in the Tools menu
#define VEXT_PIN 21 // the board's VExt pin, used to power our sensors
#define BATT_VOLTAGE_PIN 13 // the Heltec board allows you to read the lithium battery voltage without any extra hardware or wires
// ArduinoJson library variables
// https://arduinojson.org/v6/assistant/
const size_t capacity = JSON_OBJECT_SIZE(5) + 10;
DynamicJsonDocument payload(capacity);
// uFire sensor classes
uFire_EC ec;
uFire_pH ph;
uFire_ORP orp;
static void prepareTxFrame(uint8_t port)
{
// this is called to prepare the payload, so encode our `payload` object into MsgPack here
// figure out how bit it is and set the appropriate variables
serializeMsgPack(payload, appData);
appDataSize = measureMsgPack(payload);//AppDataSize max value is 64
}
void setup()
{
// init the board
pinMode(VEXT_PIN, OUTPUT);
digitalWrite(VEXT_PIN, LOW);
delay(800);
// ::begin everything
Serial.begin(115200);
display.init();
Wire.begin(32, 33); //32=sda, 33=scl
SPI.begin(SCK, MISO, MOSI, SS);
ec.begin();
ph.begin();
orp.begin(0x3e); // use a non-default i2c address
// take all our sensor measurements
payload["bV"] = ReadVoltage(BATT_VOLTAGE_PIN) * 2.3857143;
payload["t_C"] = ec.measureTemp();
payload["ec_mS"] = ec.measureEC(ec.tempC);
payload["ph"] = ph.measurepH(ec.tempC);
payload["orp"] = orp.measuremV();
// print things for us to see
serializeJsonPretty(payload, Serial);
displayMeasurements(ec.mS, "mS", ph.pH, "pH", orp.mV, "ORP", ec.tempC, "C");
// start the LoRa radio
Mcu.init(SS, RST_LoRa, DIO0, DIO1, license);
// set the device state, which is used in loop() to figure out what to do next
deviceState = DEVICE_STATE_INIT;
}
void loop()
{
switch ( deviceState )
{
case DEVICE_STATE_INIT:
{
LoRaWAN.init(loraWanClass, loraWanRegion);
break;
}
case DEVICE_STATE_JOIN:
{
LoRaWAN.join();
break;
}
case DEVICE_STATE_SEND:
{
prepareTxFrame( appPort );
LoRaWAN.send(loraWanClass);
deviceState = DEVICE_STATE_CYCLE;
break;
}
case DEVICE_STATE_CYCLE:
{
// Schedule next packet transmission
txDutyCycleTime = appTxDutyCycle + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
LoRaWAN.cycle(txDutyCycleTime);
deviceState = DEVICE_STATE_SLEEP;
break;
}
case DEVICE_STATE_SLEEP:
{
// the device will then go to sleep and we want current use to be as low as possible
low_power();
LoRaWAN.sleep(loraWanClass, debugLevel);
break;
}
default:
{
deviceState = DEVICE_STATE_INIT;
break;
}
}
}
void low_power() {
// go to sleep:
// change pins to input
// turn off power to sensors and radio
digitalWrite(VEXT_PIN, HIGH);
pinMode(VEXT_PIN, INPUT);
pinMode(5, INPUT);
pinMode(14, INPUT);
pinMode(15, INPUT);
pinMode(16, INPUT);
pinMode(17, INPUT);
pinMode(19, INPUT);
pinMode(27, INPUT);
pinMode(32, INPUT);
pinMode(33, INPUT);
}
// a more accurate ADC read to within 1%
// https://github.com/HelTecAutomation/Heltec_ESP32/blob/master/examples/ESP32/ADC_Read_Voltage/ADC_Read_Accurate/ADC_Read_Accurate.ino
double ReadVoltage(byte pin) {
double reading = analogRead(pin);
return -0.000000000000016 * pow(reading, 4) + 0.000000000118171 * pow(reading, 3) - 0.000000301211691 * pow(reading, 2) + 0.001109019271794 * reading + 0.034143524634089;
}
#include <GxEPD2_BW.h> // click to install: http://librarymanager/All#GxEPD2
#include <GxEPD2_3C.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> display(GxEPD2_290(/*CS*/ 18, /*DC*/ 10, /*RST*/ 9, /*BUSY*/ 12));
void displayMeasurements(float measure1, String unit1, float measure2, String unit2, float measure3, String unit3, float measure4, String unit4)
{
display.setRotation(0);
display.setTextColor(GxEPD_BLACK);
// measurement 1
display.setFont(&FreeSansBold18pt7b);
int16_t mtbx1, mtby1; uint16_t mtbw1, mtbh1;
display.getTextBounds((String)measure1, 0, 0, &mtbx1, &mtby1, &mtbw1, &mtbh1);
uint16_t mx1 = ((display.width() - mtbw1) / 2) - mtbx1;
uint16_t my1 = 60;
// unit 1
display.setFont(&FreeSans9pt7b);
int16_t utbx1, utby1; uint16_t utbw1, utbh1;
display.getTextBounds(unit1, 0, 0, &utbx1, &utby1, &utbw1, &utbh1);
uint16_t ux1 = ((display.width() - utbw1) / 2) - utbx1;
uint16_t uy1 = my1 + 20;
// measurement 2
display.setFont(&FreeSansBold18pt7b);
int16_t mtbx2, mtby2; uint16_t mtbw2, mtbh2;
display.getTextBounds((String)measure2, 0, 0, &mtbx2, &mtby2, &mtbw2, &mtbh2);
uint16_t mx2 = ((display.width() - mtbw2) / 2) - mtbx2;
uint16_t my2 = 120;
// unit 2
display.setFont(&FreeSans9pt7b);
int16_t utbx2, utby2; uint16_t utbw2, utbh2;
display.getTextBounds(unit2, 0, 0, &utbx2, &utby2, &utbw2, &utbh2);
uint16_t ux2 = ((display.width() - utbw2) / 2) - utbx2;
uint16_t uy2 = my2 + 20;
// measurement 3
display.setFont(&FreeSansBold18pt7b);
int16_t mtbx3, mtby3; uint16_t mtbw3, mtbh3;
display.getTextBounds((String)measure3, 0, 0, &mtbx3, &mtby3, &mtbw3, &mtbh3);
uint16_t mx3 = ((display.width() - mtbw3) / 2) - mtbx3;
uint16_t my3 = 180;
// unit 3
display.setFont(&FreeSans9pt7b);
int16_t utbx3, utby3; uint16_t utbw3, utbh3;
display.getTextBounds(unit3, 0, 0, &utbx3, &utby3, &utbw3, &utbh3);
uint16_t ux3 = ((display.width() - utbw3) / 2) - utbx3;
uint16_t uy3 = my3 + 20;
// measurement 4
display.setFont(&FreeSansBold18pt7b);
int16_t mtbx4, mtby4; uint16_t mtbw4, mtbh4;
display.getTextBounds((String)measure4, 0, 0, &mtbx4, &mtby4, &mtbw4, &mtbh4);
uint16_t mx4 = ((display.width() - mtbw4) / 2) - mtbx4;
uint16_t my4 = 240;
// unit 4
display.setFont(&FreeSans9pt7b);
int16_t utbx4, utby4; uint16_t utbw4, utbh4;
display.getTextBounds(unit4, 0, 0, &utbx4, &utby4, &utbw4, &utbh4);
uint16_t ux4 = ((display.width() - utbw4) / 2) - utbx4;
uint16_t uy4 = my4 + 20;
display.setFullWindow();
display.fillScreen(GxEPD_WHITE);
display.setCursor(25, 15);
display.setFont(&FreeSansBold9pt7b);
display.println("uFire LLC");
if (measure1)
{
display.setCursor(mx1, my1);
display.setFont(&FreeSansBold18pt7b);
display.println(measure1);
display.setFont(&FreeSans9pt7b);
display.setCursor(ux1, uy1);
display.print(unit1);
}
if (measure2)
{
display.setCursor(mx2, my2);
display.setFont(&FreeSansBold18pt7b);
display.println(measure2);
display.setFont(&FreeSans9pt7b);
display.setCursor(ux2, uy2);
display.print(unit2);
}
if (measure3)
{
display.setCursor(mx3, my3);
display.setFont(&FreeSansBold18pt7b);
display.println(measure3);
display.setFont(&FreeSans9pt7b);
display.setCursor(ux3, uy3);
display.print(unit3);
}
if (measure4)
{
display.setCursor(mx4, my4);
display.setFont(&FreeSansBold18pt7b);
display.println(measure4);
display.setFont(&FreeSans9pt7b);
display.setCursor(ux4, uy4);
display.print(unit4);
}
display.setCursor(1, uy4 + 30);
display.display(false);
display.hibernate();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment