bring repo up-to-date

This commit is contained in:
Joerg Lehmann 2022-05-21 17:57:38 +02:00
parent cd7de5dc64
commit ff41037806
96 changed files with 224981 additions and 1 deletions

View File

@ -0,0 +1,43 @@
#include <HX711_ADC.h>
//HX711 constructor (dout pin, sck pin)
HX711_ADC LoadCell_1(A3, A2); //HX711 1
HX711_ADC LoadCell_2(A1, A0); //HX711 2
long t;
void setup() {
Serial.begin(9600); delay(10);
Serial.println();
Serial.println("Starting...");
LoadCell_1.begin();
LoadCell_2.begin();
long stabilisingtime = 2000; // tare preciscion can be improved by adding a few seconds of stabilising time
byte loadcell_1_rdy = 0;
byte loadcell_2_rdy = 0;
while ((loadcell_1_rdy + loadcell_2_rdy) < 2) { //run startup, stabilization and tare, both modules simultaniously
if (!loadcell_1_rdy) loadcell_1_rdy = LoadCell_1.startMultiple(stabilisingtime);
if (!loadcell_2_rdy) loadcell_2_rdy = LoadCell_2.startMultiple(stabilisingtime);
}
LoadCell_1.setCalFactor(100); // user set calibration value (float)
LoadCell_2.setCalFactor(100); // user set calibration value (float)
Serial.println("Startup + tare is complete");
}
void loop() {
//update() should be called at least as often as HX711 sample rate; >10Hz@10SPS, >80Hz@80SPS
//longer delay in scetch will reduce effective sample rate (be carefull with use of delay() in the loop)
LoadCell_1.update();
LoadCell_2.update();
//get smoothed value from data set + current calibration factor
if (millis() > t + 250) {
long a = LoadCell_1.getSingleConversionRaw();
long b = LoadCell_2.getSingleConversionRaw();
Serial.print("Load_cell 1 output val: ");
Serial.print(a);
Serial.print(" Load_cell 2 output val: ");
Serial.println(b);
t = millis();
}
}

View File

@ -0,0 +1,43 @@
#include <HX711_ADC.h>
//HX711 constructor (dout pin, sck pin)
HX711_ADC LoadCell_1(A3, A2); //HX711 1
HX711_ADC LoadCell_2(A1, A0); //HX711 2
long t;
void setup() {
Serial.begin(9600); delay(10);
Serial.println();
Serial.println("Starting...");
LoadCell_1.begin();
LoadCell_2.begin();
long stabilisingtime = 2000; // tare preciscion can be improved by adding a few seconds of stabilising time
byte loadcell_1_rdy = 0;
byte loadcell_2_rdy = 0;
while ((loadcell_1_rdy + loadcell_2_rdy) < 2) { //run startup, stabilization and tare, both modules simultaniously
if (!loadcell_1_rdy) loadcell_1_rdy = LoadCell_1.startMultiple(stabilisingtime);
if (!loadcell_2_rdy) loadcell_2_rdy = LoadCell_2.startMultiple(stabilisingtime);
}
LoadCell_1.setCalFactor(100); // user set calibration value (float)
LoadCell_2.setCalFactor(100); // user set calibration value (float)
Serial.println("Startup + tare is complete");
}
void loop() {
//update() should be called at least as often as HX711 sample rate; >10Hz@10SPS, >80Hz@80SPS
//longer delay in scetch will reduce effective sample rate (be carefull with use of delay() in the loop)
LoadCell_1.update();
LoadCell_2.update();
//get smoothed value from data set + current calibration factor
if (millis() > t + 250) {
long a = LoadCell_1.getSingleConversionRaw();
long b = LoadCell_2.getSingleConversionRaw();
Serial.print("Load_cell 1 output val: ");
Serial.print(a);
Serial.print(" Load_cell 2 output val: ");
Serial.println(b);
t = millis();
}
}

View File

@ -2,7 +2,7 @@
#include <hal/hal.h>
#include <SPI.h>
#include "HX711.h"
#include "Adafruit_Si7021.h"
//#include "Adafruit_Si7021.h"
#include "Adafruit_FRAM_SPI.h"
// Defines

View File

@ -0,0 +1,672 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_TxBuffer.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD
;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t *pSendJob);
static void warmupDoneCb(osjob_t *pSendJob);
static void txFailedDoneCb(osjob_t *pSendJob);
static void sleepDoneCb(osjob_t *pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 4;
static const uint8_t LORA_DATA_VERSION = 1;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
typedef struct {
uint8_t version; // Versionierung des Paketformats
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Zehntels-Prozent
uint8_t pressure; // Luftdruck in XXXX
uint8_t reading_offset[MAX_VALUES_TO_SEND]; // Zeit der Messung in Sekunden, erster Wert ist 0
int16_t weight_raw1[MAX_VALUES_TO_SEND]; // Reading (raw) der ersten Waegzelle
int16_t weight_raw2[MAX_VALUES_TO_SEND]; // Reading (raw) der zweiten Waegzelle
int16_t temperature[MAX_VALUES_TO_SEND]; // Temperatur in 1/10 Grad Celsius
} LORA_data;
// Global Variables
LORA_data lora_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed (Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK
);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t *pJob);
void setup(void)
{
gCatena.begin();
lora_data.version = LORA_DATA_VERSION;
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (! (gCatena.GetOperatingFlags() &
static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)))
{
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion))
);
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring)
);
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena))
{
gCatena.SafePrintf("failed\n");
}
else
{
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t *pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM * const pPlatform = gCatena.GetPlatform();
if (pPlatform)
{
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i)
{
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags
);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags()
);
}
else
{
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep))
{
fBme = true;
}
else
{
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
LoadCell_1.begin(A3, A2);
LoadCell_2.begin(A1, A0);
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS))
{
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else
{
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() &
static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest)))
{
if (! gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else
{
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
startSendingUplink();
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
/* for mfg test, don't tx, just fill -- this causes output to Serial */
if (gCatena.GetOperatingFlags() &
static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))
{
TxBuffer_t b;
fillBuffer(b);
delay(1000);
}
}
void ReadSensors()
{
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int) (vBat * 1000.0f));
lora_data.vbat = (vBat * 1000 / 20);
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int) (vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
if (fBme)
{
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int) m.Temperature,
(int) m.Pressure,
(int) m.Humidity
);
lora_data.temperature[0] = m.Temperature;
lora_data.humidity = m.Temperature;
lora_data.pressure = m.Pressure;
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.wait_ready_timeout(1000))
{
long w1 = LoadCell_1.read_average(5);
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not found.");
}
if (LoadCell_2.wait_ready_timeout(1000))
{
long w2 = LoadCell_2.read_average(5);
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not found.");
}
gCatena.SafePrintf("After Read Scales\n");
}
void fillBuffer(TxBuffer_t &b)
{
b.begin();
FlagsSensor2 flag;
flag = FlagsSensor2(0);
b.put(FormatSensor2); /* the flag for this record format */
uint8_t * const pFlag = b.getp();
b.put(0x00); /* will be set to the flags */
// vBat is sent as 5000 * v
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int) (vBat * 1000.0f));
b.putV(vBat);
flag |= FlagsSensor2::FlagVbat;
// vBus is sent as 5000 * v
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int) (vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
uint32_t bootCount;
if (gCatena.getBootCount(bootCount))
{
b.putBootCountLsb(bootCount);
flag |= FlagsSensor2::FlagBoot;
}
if (fBme)
{
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int) m.Temperature,
(int) m.Pressure,
(int) m.Humidity
);
b.putT(m.Temperature);
b.putP(m.Pressure);
b.putRH(m.Humidity);
flag |= FlagsSensor2::FlagTPH;
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.wait_ready_timeout(1000))
{
long w1 = LoadCell_1.read_average(5);
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not found.");
}
if (LoadCell_2.wait_ready_timeout(1000))
{
long w2 = LoadCell_2.read_average(5);
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not found.");
}
gCatena.SafePrintf("After Read Scales\n");
*pFlag = uint8_t(flag);
}
void startSendingUplink(void)
{
TxBuffer_t b;
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
fillBuffer(b);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16))
{
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
//gLoRaWAN.SendBuffer(b.getbase(), b.getn(), sendBufferDoneCb, NULL, fConfirmed);
gLoRaWAN.SendBuffer((uint8_t*) &lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
static void sendBufferDoneCb(
void *pContext,
bool fStatus
)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (! fStatus)
{
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else
{
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime()+sec2osticks(CATCFG_T_SETTLE),
pFn
);
}
static void txFailedDoneCb(
osjob_t *pSendJob
)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t *pSendJob
)
{
const bool fDeepSleep = checkDeepSleep();
if (! g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest)
{
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr())
{
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17))
{
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() &
static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0)
{
fDeepSleep = true;
}
else
{
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay
);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n)
{
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000)
{
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100)
{
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t *pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t *pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb
);
}
static void sleepDoneCb(
osjob_t *pJob
)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb
);
}
static void warmupDoneCb(
osjob_t *pJob
)
{
startSendingUplink();
}

View File

@ -0,0 +1,978 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
CATCFG_T_CYCLE_INITIAL = 30, // every 30 seconds initially
CATCFG_INTERVAL_COUNT_INITIAL = 30, // repeat for 15 minutes
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
CATCFG_T_MIN = CATCFG_T_OVERHEAD,
CATCFG_T_MAX = CATCFG_T_CYCLE < 60 * 60 ? 60 * 60 : CATCFG_T_CYCLE, // normally one hour max.
CATCFG_INTERVAL_COUNT = 30,
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// the cycle time to use
unsigned gTxCycle;
// remaining before we reset to default
unsigned gTxCycleCount;
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
static Arduino_LoRaWAN::ReceivePortBufferCbFn receiveMessage;
void setTxCycleTime(unsigned txCycle, unsigned txCount);
// Additional Commands
// forward reference to the command function
cCommandStream::CommandFn cmdHello;
cCommandStream::CommandFn cmdGetCalibrationSettings;
cCommandStream::CommandFn cmdGetSensorReadings;
cCommandStream::CommandFn cmdGetScale1;
cCommandStream::CommandFn cmdGetScale2;
cCommandStream::CommandFn cmdCalibrateZeroScale1;
cCommandStream::CommandFn cmdCalibrateZeroScale2;
cCommandStream::CommandFn cmdCalibrateScale1;
cCommandStream::CommandFn cmdCalibrateScale2;
// the individual commmands are put in this table
static const cCommandStream::cEntry sMyExtraCommmands[] =
{
{ "hello", cmdHello },
{ "get_calibration_settings", cmdGetCalibrationSettings },
{ "get_sensor_readings", cmdGetSensorReadings },
{ "calibrate_zero_scale1", cmdCalibrateZeroScale1 },
{ "calibrate_zero_scale2", cmdCalibrateZeroScale2 },
{ "calibrate_scale1", cmdCalibrateScale1 },
{ "calibrate_scale2", cmdCalibrateScale2 },
// other commands go here....
};
/* a top-level structure wraps the above and connects to the system table */
/* it optionally includes a "first word" so you can for sure avoid name clashes */
static cCommandStream::cDispatch
sMyExtraCommands_top(
sMyExtraCommmands, /* this is the pointer to the table */
sizeof(sMyExtraCommmands), /* this is the size of the table */
"application" /* this is the "first word" for all the commands in this table*/
);
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 8;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint8_t LORA_DATA_VERSION_FIRST_PACKAGE = 128;
static const uint32_t PRESSURE_OFFSET = 825;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
// must be 65 bytes long...
typedef struct {
long cal_w1_0; // 4 Bytes, Wert Waegezelle 1 ohne Gewicht
long cal_w2_0; // 4 Bytes, Wert Waegezelle 2 ohne Gewicht
float cal_w1_factor; // 4 Bytes,
float cal_w2_factor;
byte fill[49];
} __attribute__((packed)) CONFIG_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (0: <= 2510mV, 70: 3000mV, 170: 3700mV, 255: >= 4295mV [1 Einheit => 7mV])
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur (Startwert) in 1/10 Grad Celsius
int8_t temperature_change[MAX_VALUES_TO_SEND - 1]; // Unterschied Temperatur seit letztem Messwert in 1/10 Grad Celsius
uint8_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
uint16_t weight[MAX_VALUES_TO_SEND]; // Waegezelle Gesamtgewicht, in 5g
uint8_t offset_last_reading; // Zeitunterschied letzte zu erste Messung (in Minuten)
} __attribute__((packed)) LORA_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (0: <= 2510mV, 70: 3000mV, 170: 3700mV, 255: >= 4295mV [1 Einheit => 7mV])
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur in 1/10 Grad Celsius
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} __attribute__((packed)) LORA_data_first;
typedef struct {
uint8_t vbat; // Batteriespannung (0: <= 2510mV, 70: 3000mV, 170: 3700mV, 255: >= 4295mV [1 Einheit => 7mV])
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur in 1/10 Grad Celsius
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} SENSOR_data;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
LORA_data_first lora_data_first;
CONFIG_data config_data;
SENSOR_data last_sensor_reading;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
// Use D10 to regulate power
pinMode(D10, OUTPUT);
setup_platform();
setup_bme280();
//setup_scales();
/* for 4451, we need wider tolerances, it seems */
#if defined(ARDUINO_ARCH_STM32)
LMIC_setClockError(10 * 65536 / 100);
#endif
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
/* add our application-specific commands */
gCatena.addCommands(
sMyExtraCommands_top,
nullptr
);
// read config_data from fram...
gCatena.SafePrintf("Reading Calibration Config from FRAM...\n");
gCatena.getFram()->getField(cFramStorage::kBme680Cal, (uint8_t *)&config_data, sizeof(config_data));
gCatena.SafePrintf("cal_w1_0: %d\n", config_data.cal_w1_0);
gCatena.SafePrintf("cal_w2_0: %d\n", config_data.cal_w2_0);
gCatena.SafePrintf("cal_w1_factor: %d.%03d\n", (int)config_data.cal_w1_factor, (int)(config_data.cal_w1_factor * 1000) % 1000);
gCatena.SafePrintf("cal_w2_factor: %d.%03d\n", (int)config_data.cal_w2_factor, (int)(config_data.cal_w2_factor * 1000) % 1000);
gCatena.SafePrintf("Size of config_data: %d\n", sizeof(config_data));
// im Moment statisch...
//config_data.cal_w1_0 = 20000;
//config_data.cal_w2_0 = 20000;
//config_data.cal_w1_factor = 2.5;
//config_data.cal_w2_factor = 2.5;
// config_data speichern...
//gCatena.SafePrintf("Writing Calibration Config to FRAM...\n");
//gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gLoRaWAN.SetReceiveBufferBufferCb(receiveMessage);
setTxCycleTime(CATCFG_T_CYCLE_INITIAL, CATCFG_INTERVAL_COUNT_INITIAL);
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Enable Power
digitalWrite(D10, HIGH);
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A1, A0);
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
//LoadCell_1.power_down();
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(D12, A2);
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
//LoadCell_2.power_down();
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true, false);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
lora_data.temperature = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight[i] = 0;
if (i < (MAX_VALUES_TO_SEND - 1)) {
lora_data.temperature_change[i] = 0;
}
}
lora_data_first.version = LORA_DATA_VERSION_FIRST_PACKAGE;
lora_data_first.vbat = 0;
lora_data_first.humidity = 0;
lora_data_first.pressure = 0;
lora_data_first.weight1 = 0;
lora_data_first.weight2 = 0;
lora_data_first.weight = 0;
lora_data_first.temperature = 0;
my_position = 0;
}
void ShowLORAData(bool firstTime)
{
if (firstTime) {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n", lora_data_first.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n", lora_data_first.vbat);
gCatena.SafePrintf(" \"humidity\": \"%u\",\n", lora_data_first.humidity);
gCatena.SafePrintf(" \"pressure\": \"%u\",\n", lora_data_first.pressure);
gCatena.SafePrintf(" \"weight1\": \"%ld\",\n", lora_data_first.weight1);
gCatena.SafePrintf(" \"weight2\": \"%ld\",\n", lora_data_first.weight2);
gCatena.SafePrintf(" \"weight\": \"%u\",\n", lora_data_first.weight);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n", lora_data_first.temperature);
gCatena.SafePrintf("}\n");
} else {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n", lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n", lora_data.vbat);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n", lora_data.temperature);
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d", lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d", lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld", lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature_change\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND - 1; i++) {
gCatena.SafePrintf("%d", lora_data.temperature_change[i]);
if (i < (MAX_VALUES_TO_SEND - 2)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
}
uint8_t GetVBatValue(int millivolts)
{
uint8_t res;
if (millivolts <= 2510) {
res = 0;
} else if (millivolts >= 4295) {
res = 255;
} else {
res = (millivolts - 2510) / 7;
}
return res;
}
void ReadSensors(bool firstTime, bool readOnly)
{
int16_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
int16_t temp_last;
int16_t temp_change;
int32_t weight_current32;
uint16_t weight_current;
uint16_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (int16_t)((m.Temperature) * 10);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n", pressure_current);
}
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
// Setup Scales
setup_scales();
// Read Scales
gCatena.SafePrintf("Before Read Scales\n");
// Power-Up HX711
LoadCell_1.power_up();
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
// Power-Down HX711
//LoadCell_1.power_down();
// Power-Up HX711
LoadCell_2.power_up();
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
// Power-Down HX711
//LoadCell_2.power_down();
// Disable Power
digitalWrite(D10, LOW);
gCatena.SafePrintf("After Read Scales\n");
// Gewicht berechnen
weight_current32 = ((weight1_current - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((weight2_current - config_data.cal_w2_0) / config_data.cal_w2_factor);
if (weight_current32 < 0) {
weight_current32 = 0;
} else if (weight_current32 > UINT16_MAX) {
weight_current32 = UINT16_MAX;
}
weight_current = (uint16_t)weight_current32;
if (not(readOnly)) {
// calculate last value
weight_last = 0;
temp_last = lora_data.temperature;
for (int i = 0; i < my_position; i++) {
temp_last = temp_last + lora_data.temperature_change[i];
}
if (my_position > 0) {
weight_last = lora_data.weight[my_position - 1];
}
if (firstTime) {
lora_data_first.vbat = GetVBatValue((int)(vBat * 1000.0f));
lora_data_first.weight1 = weight1_current;
lora_data_first.weight2 = weight2_current;
lora_data_first.weight = weight_current;
lora_data_first.temperature = temp_current;
lora_data_first.humidity = humidity_current;
lora_data_first.pressure = pressure_current;
} else {
lora_data.vbat = GetVBatValue((int)(vBat * 1000.0f));
lora_data.weight[my_position] = weight_current;
if (my_position == 0) {
lora_data.temperature = temp_current;
} else {
temp_change = temp_current - temp_last;
if (temp_change > 127) {
temp_change = 127;
}
if (temp_change < -128) {
temp_change = -128;
}
lora_data.temperature_change[my_position - 1] = (uint8_t)temp_change;
}
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
}
if (my_position == 0) {
timer_pos0 = millis();
}
ShowLORAData(firstTime);
my_position++;
// Should we send the Data?
// we send data the first time the system is started, when the array is full
// or when the weight has fallen more than 100g or the first measurement is
// more than one hour old (which should not happen :-) )
if (firstTime || (my_position >= MAX_VALUES_TO_SEND) || ((weight_last - weight_current) > 20) || ((millis() - timer_pos0) > 3600000)) {
lora_data.offset_last_reading = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink(firstTime);
} else {
gCatena.SafePrintf("now going to sleep for 6 minutes...\n");
Serial.flush();
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
gCatena.Sleep(CATCFG_T_INTERVAL - 5);
}
}
last_sensor_reading.vbat = GetVBatValue((int)(vBat * 1000.0f));
last_sensor_reading.weight1 = weight1_current;
last_sensor_reading.weight2 = weight2_current;
last_sensor_reading.weight = weight_current;
last_sensor_reading.temperature = temp_current;
last_sensor_reading.humidity = humidity_current;
last_sensor_reading.pressure = pressure_current;
}
void startSendingUplink(bool firstTime)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
if (firstTime) {
gCatena.SafePrintf("SendBuffer firstTime\n");
gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed);
} else {
gCatena.SafePrintf("SendBuffer not firstTime\n");
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
gCatena.SafePrintf("sendBufferDoneCB before TimedCallback\n");
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
gCatena.SafePrintf("sendBufferDoneCB after TimedCallback\n");
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("settleDoneCb\n");
sleepDoneCb(pSendJob);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false, false);
}
static void receiveMessage(void *pContext, uint8_t port, const uint8_t *pMessage, size_t nMessage)
{
unsigned txCycle;
unsigned txCount;
gCatena.SafePrintf("receiveMessage was called!!!\n");
if (! (port == 1 && 2 <= nMessage && nMessage <= 3))
{
gCatena.SafePrintf("invalid message port(%02x)/length(%zx)\n",
port, nMessage
);
return;
}
txCycle = (pMessage[0] << 8) | pMessage[1];
if (txCycle < CATCFG_T_MIN || txCycle > CATCFG_T_MAX)
{
gCatena.SafePrintf("tx cycle time out of range: %u\n", txCycle);
return;
}
// byte [2], if present, is the repeat count.
// explicitly sending zero causes it to stick.
txCount = CATCFG_INTERVAL_COUNT;
if (nMessage >= 3)
{
txCount = pMessage[2];
}
// we print out the received message...
gCatena.SafePrintf("Received Data (Payload): \n");
for (byte i = 0; i < nMessage; i++) {
gCatena.SafePrintf("%c", pMessage[i]);
}
gCatena.SafePrintf("\n");
setTxCycleTime(txCycle, txCount);
}
void setTxCycleTime(unsigned txCycle, unsigned txCount)
{
if (txCount > 0)
gCatena.SafePrintf(
"message cycle time %u seconds for %u messages\n",
txCycle, txCount
);
else
gCatena.SafePrintf(
"message cycle time %u seconds indefinitely\n",
txCycle
);
gTxCycle = txCycle;
gTxCycleCount = txCount;
}
/* process "application hello" -- args are ignored */
// argv[0] is "hello"
// argv[1..argc-1] are the (ignored) arguments
cCommandStream::CommandStatus cmdHello(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
pThis->printf("Hello, world!\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetCalibrationSettings(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
pThis->printf("{\n");
pThis->printf(" \"cal_w1_0\": \"%d\",\n", config_data.cal_w1_0);
pThis->printf(" \"cal_w2_0\": \"%d\",\n", config_data.cal_w2_0);
pThis->printf(" \"cal_w1_factor\": \"%d.%03d\n", (int)config_data.cal_w1_factor, (int)abs(config_data.cal_w1_factor * 1000) % 1000);
pThis->printf(" \"cal_w2_factor\": \"%d.%03d\n", (int)config_data.cal_w2_factor, (int)abs(config_data.cal_w2_factor * 1000) % 1000);
pThis->printf("}\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetSensorReadings(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
pThis->printf("{\n");
pThis->printf(" \"weight\": \"%d\",\n", last_sensor_reading.weight);
pThis->printf(" \"weight1_raw\": \"%d\",\n", last_sensor_reading.weight1);
pThis->printf(" \"weight2_raw\": \"%d\",\n", last_sensor_reading.weight2);
pThis->printf(" \"temperature\": \"%d\",\n", last_sensor_reading.temperature);
pThis->printf(" \"humidity\": \"%d\",\n", last_sensor_reading.humidity);
pThis->printf(" \"pressure\": \"%d\",\n", last_sensor_reading.pressure);
pThis->printf(" \"batt\": \"%d\",\n", last_sensor_reading.vbat);
pThis->printf("}\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetScale1(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
pThis->printf("getscale1\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetScale2(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
pThis->printf("getscale2\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateZeroScale1(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
config_data.cal_w1_0 = last_sensor_reading.weight1;
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_zero_scale1 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateZeroScale2(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
config_data.cal_w2_0 = last_sensor_reading.weight2;
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_zero_scale2 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateScale1(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
String w1_gramm(argv[1]);
config_data.cal_w1_factor = (((float)last_sensor_reading.weight1 - config_data.cal_w1_0) / w1_gramm.toFloat());
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_scale1 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateScale2(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
String w2_gramm(argv[1]);
config_data.cal_w2_factor = (((float)last_sensor_reading.weight2 - config_data.cal_w2_0) / w2_gramm.toFloat());
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_scale2 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}

View File

@ -0,0 +1,988 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
CATCFG_T_CYCLE_INITIAL = 30, // every 30 seconds initially
CATCFG_INTERVAL_COUNT_INITIAL = 30, // repeat for 15 minutes
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
CATCFG_T_MIN = CATCFG_T_OVERHEAD,
CATCFG_T_MAX = CATCFG_T_CYCLE < 60 * 60 ? 60 * 60 : CATCFG_T_CYCLE, // normally one hour max.
CATCFG_INTERVAL_COUNT = 30,
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// the cycle time to use
unsigned gTxCycle;
// remaining before we reset to default
unsigned gTxCycleCount;
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
static Arduino_LoRaWAN::ReceivePortBufferCbFn receiveMessage;
void setTxCycleTime(unsigned txCycle, unsigned txCount);
// Additional Commands
// forward reference to the command function
cCommandStream::CommandFn cmdHello;
cCommandStream::CommandFn cmdGetCalibrationSettings;
cCommandStream::CommandFn cmdGetSensorReadings;
cCommandStream::CommandFn cmdGetScale1;
cCommandStream::CommandFn cmdGetScale2;
cCommandStream::CommandFn cmdCalibrateZeroScale1;
cCommandStream::CommandFn cmdCalibrateZeroScale2;
cCommandStream::CommandFn cmdCalibrateScale1;
cCommandStream::CommandFn cmdCalibrateScale2;
// the individual commmands are put in this table
static const cCommandStream::cEntry sMyExtraCommmands[] =
{
{ "hello", cmdHello },
{ "get_calibration_settings", cmdGetCalibrationSettings },
{ "get_sensor_readings", cmdGetSensorReadings },
{ "calibrate_zero_scale1", cmdCalibrateZeroScale1 },
{ "calibrate_zero_scale2", cmdCalibrateZeroScale2 },
{ "calibrate_scale1", cmdCalibrateScale1 },
{ "calibrate_scale2", cmdCalibrateScale2 },
// other commands go here....
};
/* a top-level structure wraps the above and connects to the system table */
/* it optionally includes a "first word" so you can for sure avoid name clashes */
static cCommandStream::cDispatch
sMyExtraCommands_top(
sMyExtraCommmands, /* this is the pointer to the table */
sizeof(sMyExtraCommmands), /* this is the size of the table */
"application" /* this is the "first word" for all the commands in this table*/
);
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 8;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint8_t LORA_DATA_VERSION_FIRST_PACKAGE = 128;
static const uint32_t PRESSURE_OFFSET = 825;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
// must be 65 bytes long...
typedef struct {
long cal_w1_0; // 4 Bytes, Wert Waegezelle 1 ohne Gewicht
long cal_w2_0; // 4 Bytes, Wert Waegezelle 2 ohne Gewicht
float cal_w1_factor; // 4 Bytes,
float cal_w2_factor;
byte fill[123];
} __attribute__((packed)) CONFIG_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (0: <= 2510mV, 70: 3000mV, 170: 3700mV, 255: >= 4295mV [1 Einheit => 7mV])
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur (Startwert) in 1/10 Grad Celsius
int8_t temperature_change[MAX_VALUES_TO_SEND - 1]; // Unterschied Temperatur seit letztem Messwert in 1/10 Grad Celsius
uint8_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
uint16_t weight[MAX_VALUES_TO_SEND]; // Waegezelle Gesamtgewicht, in 5g
uint8_t offset_last_reading; // Zeitunterschied letzte zu erste Messung (in Minuten)
} __attribute__((packed)) LORA_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (0: <= 2510mV, 70: 3000mV, 170: 3700mV, 255: >= 4295mV [1 Einheit => 7mV])
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur in 1/10 Grad Celsius
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} __attribute__((packed)) LORA_data_first;
typedef struct {
uint8_t vbat; // Batteriespannung (0: <= 2510mV, 70: 3000mV, 170: 3700mV, 255: >= 4295mV [1 Einheit => 7mV])
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur in 1/10 Grad Celsius
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} SENSOR_data;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
LORA_data_first lora_data_first;
CONFIG_data config_data;
SENSOR_data last_sensor_reading;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
// Use D10 to regulate power
pinMode(D10, OUTPUT);
setup_platform();
setup_bme280();
//setup_scales();
/* for 4451, we need wider tolerances, it seems */
#if defined(ARDUINO_ARCH_STM32)
LMIC_setClockError(10 * 65536 / 100);
#endif
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
/* add our application-specific commands */
gCatena.addCommands(
sMyExtraCommands_top,
nullptr
);
// read config_data from fram...
gCatena.SafePrintf("Reading Calibration Config from FRAM...\n");
gCatena.getFram()->getField(cFramStorage::kBme680Cal, (uint8_t *)&config_data, sizeof(config_data));
gCatena.SafePrintf("cal_w1_0: %d\n", config_data.cal_w1_0);
gCatena.SafePrintf("cal_w2_0: %d\n", config_data.cal_w2_0);
gCatena.SafePrintf("cal_w1_factor: %d.%03d\n", (int)config_data.cal_w1_factor, (int)(config_data.cal_w1_factor * 1000) % 1000);
gCatena.SafePrintf("cal_w2_factor: %d.%03d\n", (int)config_data.cal_w2_factor, (int)(config_data.cal_w2_factor * 1000) % 1000);
gCatena.SafePrintf("Size of config_data: %d\n", sizeof(config_data));
// im Moment statisch...
//config_data.cal_w1_0 = 20000;
//config_data.cal_w2_0 = 20000;
//config_data.cal_w1_factor = 2.5;
//config_data.cal_w2_factor = 2.5;
// config_data speichern...
//gCatena.SafePrintf("Writing Calibration Config to FRAM...\n");
//gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gLoRaWAN.SetReceiveBufferBufferCb(receiveMessage);
setTxCycleTime(CATCFG_T_CYCLE_INITIAL, CATCFG_INTERVAL_COUNT_INITIAL);
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Enable Power
digitalWrite(D10, HIGH);
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A1, A0);
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
//LoadCell_1.power_down();
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(D12, A2);
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
//LoadCell_2.power_down();
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true, false);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
lora_data.temperature = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight[i] = 0;
if (i < (MAX_VALUES_TO_SEND - 1)) {
lora_data.temperature_change[i] = 0;
}
}
lora_data_first.version = LORA_DATA_VERSION_FIRST_PACKAGE;
lora_data_first.vbat = 0;
lora_data_first.humidity = 0;
lora_data_first.pressure = 0;
lora_data_first.weight1 = 0;
lora_data_first.weight2 = 0;
lora_data_first.weight = 0;
lora_data_first.temperature = 0;
my_position = 0;
}
void ShowLORAData(bool firstTime)
{
if (firstTime) {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n", lora_data_first.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n", lora_data_first.vbat);
gCatena.SafePrintf(" \"humidity\": \"%u\",\n", lora_data_first.humidity);
gCatena.SafePrintf(" \"pressure\": \"%u\",\n", lora_data_first.pressure);
gCatena.SafePrintf(" \"weight1\": \"%ld\",\n", lora_data_first.weight1);
gCatena.SafePrintf(" \"weight2\": \"%ld\",\n", lora_data_first.weight2);
gCatena.SafePrintf(" \"weight\": \"%u\",\n", lora_data_first.weight);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n", lora_data_first.temperature);
gCatena.SafePrintf("}\n");
} else {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n", lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n", lora_data.vbat);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n", lora_data.temperature);
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d", lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d", lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld", lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature_change\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND - 1; i++) {
gCatena.SafePrintf("%d", lora_data.temperature_change[i]);
if (i < (MAX_VALUES_TO_SEND - 2)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
}
uint8_t GetVBatValue(int millivolts)
{
uint8_t res;
if (millivolts <= 2510) {
res = 0;
} else if (millivolts >= 4295) {
res = 255;
} else {
res = (millivolts - 2510) / 7;
}
return res;
}
void ReadSensors(bool firstTime, bool readOnly)
{
int16_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
int16_t temp_last;
int16_t temp_change;
int32_t weight_current32;
uint16_t weight_current;
uint16_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (int16_t)((m.Temperature) * 10);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n", pressure_current);
}
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
// Setup Scales
setup_scales();
// Read Scales
gCatena.SafePrintf("Before Read Scales\n");
// Power-Up HX711
LoadCell_1.power_up();
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
gCatena.SafePrintf("Load_cell 1 weight1_current: %ld\n", weight1_current);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
// Power-Down HX711
//LoadCell_1.power_down();
// Power-Up HX711
LoadCell_2.power_up();
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
gCatena.SafePrintf("Load_cell 2 weight2_current: %ld\n", weight2_current);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
// Power-Down HX711
//LoadCell_2.power_down();
// Disable Power
digitalWrite(D10, LOW);
gCatena.SafePrintf("After Read Scales\n");
// Gewicht berechnen
weight_current32 = (int32_t)((((weight1_current - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((weight2_current - config_data.cal_w2_0) / config_data.cal_w2_factor)) / 5.0);
if (weight_current32 < 0) {
weight_current32 = 0;
} else if (weight_current32 > UINT16_MAX) {
weight_current32 = UINT16_MAX;
}
weight_current = (uint16_t)weight_current32;
if (not(readOnly)) {
// calculate last value
weight_last = 0;
temp_last = lora_data.temperature;
for (int i = 0; i < my_position; i++) {
temp_last = temp_last + lora_data.temperature_change[i];
}
if (my_position > 0) {
weight_last = lora_data.weight[my_position - 1];
}
if (firstTime) {
lora_data_first.vbat = GetVBatValue((int)(vBat * 1000.0f));
lora_data_first.weight1 = weight1_current;
lora_data_first.weight2 = weight2_current;
lora_data_first.weight = weight_current;
lora_data_first.temperature = temp_current;
lora_data_first.humidity = humidity_current;
lora_data_first.pressure = pressure_current;
} else {
lora_data.vbat = GetVBatValue((int)(vBat * 1000.0f));
lora_data.weight[my_position] = weight_current;
if (my_position == 0) {
lora_data.temperature = temp_current;
} else {
temp_change = temp_current - temp_last;
if (temp_change > 127) {
temp_change = 127;
}
if (temp_change < -128) {
temp_change = -128;
}
lora_data.temperature_change[my_position - 1] = (uint8_t)temp_change;
}
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
}
if (my_position == 0) {
timer_pos0 = millis();
}
ShowLORAData(firstTime);
my_position++;
// Should we send the Data?
// we send data the first time the system is started, when the array is full
// or when the weight has fallen more than 100g or the first measurement is
// more than one hour old (which should not happen :-) )
if (firstTime || (my_position >= MAX_VALUES_TO_SEND) || ((weight_last - weight_current) > 20) || ((millis() - timer_pos0) > 3600000)) {
lora_data.offset_last_reading = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink(firstTime);
} else {
gCatena.SafePrintf("now going to sleep for 6 minutes...\n");
Serial.flush();
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
if (!fUsbPower) {
gCatena.Sleep(CATCFG_T_INTERVAL - 5);
}
}
}
last_sensor_reading.vbat = GetVBatValue((int)(vBat * 1000.0f));
last_sensor_reading.weight1 = weight1_current;
last_sensor_reading.weight2 = weight2_current;
last_sensor_reading.weight = weight_current;
last_sensor_reading.temperature = temp_current;
last_sensor_reading.humidity = humidity_current;
last_sensor_reading.pressure = pressure_current;
}
void startSendingUplink(bool firstTime)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
if (firstTime) {
gCatena.SafePrintf("SendBuffer firstTime\n");
gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed);
} else {
gCatena.SafePrintf("SendBuffer not firstTime\n");
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
gCatena.SafePrintf("sendBufferDoneCB before TimedCallback\n");
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
gCatena.SafePrintf("sendBufferDoneCB after TimedCallback\n");
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("settleDoneCb\n");
sleepDoneCb(pSendJob);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false, false);
}
static void receiveMessage(void *pContext, uint8_t port, const uint8_t *pMessage, size_t nMessage)
{
unsigned txCycle;
unsigned txCount;
gCatena.SafePrintf("receiveMessage was called!!!\n");
if (! (port == 1 && 2 <= nMessage && nMessage <= 3))
{
gCatena.SafePrintf("invalid message port(%02x)/length(%zx)\n",
port, nMessage
);
return;
}
txCycle = (pMessage[0] << 8) | pMessage[1];
if (txCycle < CATCFG_T_MIN || txCycle > CATCFG_T_MAX)
{
gCatena.SafePrintf("tx cycle time out of range: %u\n", txCycle);
return;
}
// byte [2], if present, is the repeat count.
// explicitly sending zero causes it to stick.
txCount = CATCFG_INTERVAL_COUNT;
if (nMessage >= 3)
{
txCount = pMessage[2];
}
// we print out the received message...
gCatena.SafePrintf("Received Data (Payload): \n");
for (byte i = 0; i < nMessage; i++) {
gCatena.SafePrintf("%c", pMessage[i]);
}
gCatena.SafePrintf("\n");
setTxCycleTime(txCycle, txCount);
}
void setTxCycleTime(unsigned txCycle, unsigned txCount)
{
if (txCount > 0)
gCatena.SafePrintf(
"message cycle time %u seconds for %u messages\n",
txCycle, txCount
);
else
gCatena.SafePrintf(
"message cycle time %u seconds indefinitely\n",
txCycle
);
gTxCycle = txCycle;
gTxCycleCount = txCount;
}
/* process "application hello" -- args are ignored */
// argv[0] is "hello"
// argv[1..argc-1] are the (ignored) arguments
cCommandStream::CommandStatus cmdHello(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
pThis->printf("Hello, world!\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetCalibrationSettings(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
pThis->printf("{\n");
pThis->printf(" \"cal_w1_0\": \"%d\",\n", config_data.cal_w1_0);
pThis->printf(" \"cal_w2_0\": \"%d\",\n", config_data.cal_w2_0);
pThis->printf(" \"cal_w1_factor\": \"%d.%03d\n", (int)config_data.cal_w1_factor, (int)abs(config_data.cal_w1_factor * 1000) % 1000);
pThis->printf(" \"cal_w2_factor\": \"%d.%03d\n", (int)config_data.cal_w2_factor, (int)abs(config_data.cal_w2_factor * 1000) % 1000);
pThis->printf("}\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetSensorReadings(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
pThis->printf("{\n");
pThis->printf(" \"weight\": \"%d\",\n", last_sensor_reading.weight);
pThis->printf(" \"weight1_raw\": \"%d\",\n", last_sensor_reading.weight1);
pThis->printf(" \"weight2_raw\": \"%d\",\n", last_sensor_reading.weight2);
pThis->printf(" \"temperature\": \"%d\",\n", last_sensor_reading.temperature);
pThis->printf(" \"humidity\": \"%d\",\n", last_sensor_reading.humidity);
pThis->printf(" \"pressure\": \"%d\",\n", last_sensor_reading.pressure);
pThis->printf(" \"batt\": \"%d\",\n", last_sensor_reading.vbat);
pThis->printf("}\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetScale1(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
pThis->printf("getscale1\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetScale2(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
pThis->printf("getscale2\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateZeroScale1(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
config_data.cal_w1_0 = last_sensor_reading.weight1;
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_zero_scale1 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateZeroScale2(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
config_data.cal_w2_0 = last_sensor_reading.weight2;
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_zero_scale2 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateScale1(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
String w1_gramm(argv[1]);
config_data.cal_w1_factor = (((float)last_sensor_reading.weight1 - config_data.cal_w1_0) / w1_gramm.toFloat());
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
gCatena.SafePrintf("last_sensor_reading.weight1: %ld\n", last_sensor_reading.weight1);
gCatena.SafePrintf("config_data.cal_w1_0: %ld\n", config_data.cal_w1_0);
gCatena.SafePrintf("w1_gramm: %s\n", w1_gramm);
gCatena.SafePrintf("w1_gramm (float): %d\n", (int)w1_gramm.toFloat());
gCatena.SafePrintf("config_data.cal_w1_factor: %d\n", (int)config_data.cal_w1_factor);
pThis->printf("{ \"msg\": \"calibrate_scale1 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateScale2(cCommandStream *pThis, void *pContext, int argc, char **argv)
{
ReadSensors(false, true);
String w2_gramm(argv[1]);
config_data.cal_w2_factor = (((float)last_sensor_reading.weight2 - config_data.cal_w2_0) / w2_gramm.toFloat());
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_scale2 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}

View File

@ -0,0 +1,850 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
CATCFG_T_CYCLE_INITIAL = 30, // every 30 seconds initially
CATCFG_INTERVAL_COUNT_INITIAL = 30, // repeat for 15 minutes
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
CATCFG_T_MIN = CATCFG_T_OVERHEAD,
CATCFG_T_MAX = CATCFG_T_CYCLE < 60 * 60 ? 60 * 60 : CATCFG_T_CYCLE, // normally one hour max.
CATCFG_INTERVAL_COUNT = 30,
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// the cycle time to use
unsigned gTxCycle;
// remaining before we reset to default
unsigned gTxCycleCount;
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
static Arduino_LoRaWAN::ReceivePortBufferCbFn receiveMessage;
void setTxCycleTime(unsigned txCycle, unsigned txCount);
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 8;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint8_t LORA_DATA_VERSION_FIRST_PACKAGE = 128;
static const uint32_t PRESSURE_OFFSET = 825;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
// must be 65 bytes long...
typedef struct {
long cal_w1_0; // 4 Bytes, Wert Waegezelle 1 ohne Gewicht
long cal_w2_0; // 4 Bytes, Wert Waegezelle 1 ohne Gewicht
float cal_w1_factor; // 4 Bytes,
float cal_w2_factor;
byte fill[49];
} __attribute__((packed)) CONFIG_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur (Startwert) in 1/10 Grad Celsius
int8_t temperature_change[MAX_VALUES_TO_SEND - 1]; // Unterschied Temperatur seit letztem Messwert in 1/10 Grad Celsius
uint8_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
uint16_t weight[MAX_VALUES_TO_SEND]; // Waegezelle Gesamtgewicht, in 5g
uint8_t offset_last_reading; // Zeitunterschied letzte zu erste Messung (in Minuten)
} __attribute__((packed)) LORA_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur in 1/10 Grad Celsius
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} __attribute__((packed)) LORA_data_first;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
LORA_data_first lora_data_first;
CONFIG_data config_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
/* for 4451, we need wider tolerances, it seems */
#if defined(ARDUINO_ARCH_STM32)
LMIC_setClockError(10 * 65536 / 100);
#endif
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
// read config_data from fram...
gCatena.SafePrintf("Reading Calibration Config from FRAM...\n");
gCatena.getFram()->getField(cFramStorage::kBme680Cal, (uint8_t *)&config_data, sizeof(config_data));
gCatena.SafePrintf("cal_w1_0: %d\n",config_data.cal_w1_0);
gCatena.SafePrintf("cal_w2_0: %d\n",config_data.cal_w2_0);
gCatena.SafePrintf("cal_w1_factor: %d.%03d\n",(int)config_data.cal_w1_factor,(int)(config_data.cal_w1_factor*1000)%1000);
gCatena.SafePrintf("cal_w2_factor: %d.%03d\n",(int)config_data.cal_w2_factor,(int)(config_data.cal_w2_factor*1000)%1000);
gCatena.SafePrintf("Size of config_data: %d\n",sizeof(config_data));
// im Moment statisch...
//config_data.cal_w1_0 = 20000;
//config_data.cal_w2_0 = 20000;
//config_data.cal_w1_factor = 2.5;
//config_data.cal_w2_factor = 2.5;
// config_data speichern...
gCatena.SafePrintf("Writing Calibration Config to FRAM...\n");
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gLoRaWAN.SetReceiveBufferBufferCb(receiveMessage);
setTxCycleTime(CATCFG_T_CYCLE_INITIAL, CATCFG_INTERVAL_COUNT_INITIAL);
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
//LoadCell_1.begin(A3, A2);
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
gCatena.SafePrintf("Setup Scale 2...\n");
//LoadCell_2.begin(A1, A0);
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
lora_data.temperature = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight[i] = 0;
if (i < (MAX_VALUES_TO_SEND -1)) {
lora_data.temperature_change[i] = 0;
}
}
lora_data_first.version = LORA_DATA_VERSION_FIRST_PACKAGE;
lora_data_first.vbat = 0;
lora_data_first.humidity = 0;
lora_data_first.pressure = 0;
lora_data_first.weight1 = 0;
lora_data_first.weight2 = 0;
lora_data_first.weight = 0;
lora_data_first.temperature = 0;
my_position = 0;
}
void ShowLORAData(bool firstTime)
{
if (firstTime) {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data_first.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data_first.vbat);
gCatena.SafePrintf(" \"humidity\": \"%u\",\n",lora_data_first.humidity);
gCatena.SafePrintf(" \"pressure\": \"%u\",\n",lora_data_first.pressure);
gCatena.SafePrintf(" \"weight1\": \"%ld\",\n",lora_data_first.weight1);
gCatena.SafePrintf(" \"weight2\": \"%ld\",\n",lora_data_first.weight2);
gCatena.SafePrintf(" \"weight\": \"%u\",\n",lora_data_first.weight);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data_first.temperature);
gCatena.SafePrintf("}\n");
} else {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data.temperature);
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature_change\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND - 1; i++) {
gCatena.SafePrintf("%d",lora_data.temperature_change[i]);
if (i < (MAX_VALUES_TO_SEND - 2)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
}
void ReadSensors(bool firstTime)
{
// Power-Up HX711
LoadCell_1.power_up();
LoadCell_1.power_up();
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
int16_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
int16_t temp_last;
int16_t temp_change;
uint16_t weight_current;
uint16_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (int16_t)((m.Temperature) * 10);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
// Power-Down HX711
LoadCell_1.power_down();
LoadCell_1.power_down();
// Gewicht berechnen
weight_current = ((weight1_current - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((weight2_current - config_data.cal_w2_0) / config_data.cal_w2_factor);
// calculate last value
weight_last = 0;
temp_last = lora_data.temperature;
for (int i = 0; i < my_position; i++) {
temp_last = temp_last + lora_data.temperature_change[i];
}
if (my_position > 0) {
weight_last = lora_data.weight[my_position - 1];
}
if (firstTime) {
lora_data_first.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data_first.weight1 = weight1_current;
lora_data_first.weight2 = weight2_current;
lora_data_first.weight = weight_current;
lora_data_first.temperature = temp_current;
lora_data_first.humidity = humidity_current;
lora_data_first.pressure = pressure_current;
} else {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight[my_position] = weight_current;
if (my_position == 0) {
lora_data.temperature = temp_current;
} else {
temp_change = temp_current - temp_last;
if (temp_change > 127) {
temp_change = 127;
}
if (temp_change < -128) {
temp_change = -128;
}
lora_data.temperature_change[my_position - 1] = (uint8_t)temp_change;
}
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
}
if (my_position == 0) {
timer_pos0 = millis();
}
ShowLORAData(firstTime);
my_position++;
// Should we send the Data?
// we send data the first time the system is started, when the array is full
// or when the weight has fallen more than 100g or the first measurement is
// more than one hour old (which should not happen :-) )
if (firstTime || (my_position >= MAX_VALUES_TO_SEND) || ((weight_last - weight_current) > 20) || ((millis() - timer_pos0) > 3600000)) {
lora_data.offset_last_reading = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink(firstTime);
} else {
doLightSleep(&sensorJob);
}
}
void startSendingUplink(bool firstTime)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
if (firstTime) {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed);
} else {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}
static void receiveMessage(void *pContext, uint8_t port, const uint8_t *pMessage, size_t nMessage)
{
unsigned txCycle;
unsigned txCount;
gCatena.SafePrintf("receiveMessage was called!!!\n");
if (! (port == 1 && 2 <= nMessage && nMessage <= 3))
{
gCatena.SafePrintf("invalid message port(%02x)/length(%zx)\n",
port, nMessage
);
return;
}
txCycle = (pMessage[0] << 8) | pMessage[1];
if (txCycle < CATCFG_T_MIN || txCycle > CATCFG_T_MAX)
{
gCatena.SafePrintf("tx cycle time out of range: %u\n", txCycle);
return;
}
// byte [2], if present, is the repeat count.
// explicitly sending zero causes it to stick.
txCount = CATCFG_INTERVAL_COUNT;
if (nMessage >= 3)
{
txCount = pMessage[2];
}
// we print out the received message...
gCatena.SafePrintf("Received Data (Payload): \n");
for (byte i = 0; i < nMessage; i++) {
gCatena.SafePrintf("%c", pMessage[i]);
}
gCatena.SafePrintf("\n");
setTxCycleTime(txCycle, txCount);
}
void setTxCycleTime(unsigned txCycle, unsigned txCount)
{
if (txCount > 0)
gCatena.SafePrintf(
"message cycle time %u seconds for %u messages\n",
txCycle, txCount
);
else
gCatena.SafePrintf(
"message cycle time %u seconds indefinitely\n",
txCycle
);
gTxCycle = txCycle;
gTxCycleCount = txCount;
}

View File

@ -0,0 +1,718 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 8;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint8_t LORA_DATA_VERSION_FIRST_PACKAGE = 128;
static const uint32_t PRESSURE_OFFSET = 825;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
typedef struct {
long cal_w1_0;
long cal_w2_0;
float cal_w1_factor;
float cal_w2_factor;
} CONFIG_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
uint8_t temperature[MAX_VALUES_TO_SEND]; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
uint8_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
uint16_t weight[MAX_VALUES_TO_SEND]; // Waegezelle Gesamtgewicht, in 5g
uint8_t offset_last_reading;
} __attribute__((packed)) LORA_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Prozent
uint8_t temperature; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} __attribute__((packed)) LORA_data_first;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
LORA_data_first lora_data_first;
CONFIG_data config_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
// im Moment statisch...
config_data.cal_w1_0 = 20000;
config_data.cal_w2_0 = 20000;
config_data.cal_w1_factor = 2.5;
config_data.cal_w2_factor = 2.5;
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A3, A2);
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(A1, A0);
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight[i] = 0;
lora_data.temperature[i] = 0;
}
lora_data_first.version = LORA_DATA_VERSION_FIRST_PACKAGE;
lora_data_first.vbat = 0;
lora_data_first.humidity = 0;
lora_data_first.pressure = 0;
lora_data_first.weight1 = 0;
lora_data_first.weight2 = 0;
lora_data_first.weight = 0;
lora_data_first.temperature = 0;
my_position = 0;
}
void ShowLORAData(bool firstTime)
{
if (firstTime) {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data_first.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data_first.vbat);
gCatena.SafePrintf(" \"humidity\": \"%u\",\n",lora_data_first.humidity);
gCatena.SafePrintf(" \"pressure\": \"%u\",\n",lora_data_first.pressure);
gCatena.SafePrintf(" \"weight1\": \"%ld\",\n",lora_data_first.weight1);
gCatena.SafePrintf(" \"weight2\": \"%ld\",\n",lora_data_first.weight2);
gCatena.SafePrintf(" \"weight\": \"%u\",\n",lora_data_first.weight);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data_first.temperature);
gCatena.SafePrintf("}\n");
} else {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.temperature[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
}
void ReadSensors(bool firstTime)
{
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
uint8_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
uint8_t temp_last;
uint16_t weight_current;
uint16_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (uint8_t)((m.Temperature + 40) * 2);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
// Gewicht berechnen
weight_current = ((weight1_current - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((weight2_current - config_data.cal_w2_0) / config_data.cal_w2_factor);
// calculate last value
weight_last = 0;
if (my_position > 0) {
temp_last =lora_data.temperature[my_position -1];
weight_last = lora_data.weight[my_position - 1];
}
if (firstTime) {
lora_data_first.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data_first.weight1 = weight1_current;
lora_data_first.weight2 = weight2_current;
lora_data_first.weight = weight_current;
lora_data_first.temperature = temp_current;
lora_data_first.humidity = humidity_current;
lora_data_first.pressure = pressure_current;
} else {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight[my_position] = weight_current;
lora_data.temperature[my_position] = temp_current;
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
}
if (my_position == 0) {
timer_pos0 = millis();
}
ShowLORAData(firstTime);
my_position++;
// Should we send the Data?
// we send data the first time the system is started, when the array is full
// or when the weight has fallen more than 100g or the first measurement is
// more than one hour old (which should not happen :-) )
if (firstTime || (my_position >= MAX_VALUES_TO_SEND) || ((weight_last - weight_current) > 20) || ((millis() - timer_pos0) > 3600000)) {
lora_data.offset_last_reading = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink(firstTime);
} else {
doLightSleep(&sensorJob);
}
}
void startSendingUplink(bool firstTime)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
if (firstTime) {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed);
} else {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}

View File

@ -0,0 +1,649 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 4;
static const uint8_t LORA_DATA_VERSION = 1;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
typedef struct {
uint8_t version; // Versionierung des Paketformats
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Zehntels-Prozent
uint8_t pressure; // Luftdruck in XXXX
uint8_t reading_offset[MAX_VALUES_TO_SEND]; // Zeit der Messung in Sekunden, erster Wert ist 0
int16_t weight_raw1[MAX_VALUES_TO_SEND]; // Reading (raw) der ersten Waegzelle
int16_t weight_raw2[MAX_VALUES_TO_SEND]; // Reading (raw) der zweiten Waegzelle
int16_t temperature[MAX_VALUES_TO_SEND]; // Temperatur in 1/10 Grad Celsius
} LORA_data;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A3, A2);
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(A1, A0);
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
bool ShouldDataBeSent(void)
{
bool res = (my_position >= MAX_VALUES_TO_SEND) ||
((millis() - timer_pos0) > 3600000);
return res;
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
lora_data.humidity = 0;
lora_data.pressure = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.reading_offset[i] = 0;
lora_data.weight_raw1[i] = 0;
lora_data.weight_raw2[i] = 0;
lora_data.temperature[i] = 0;
}
}
void ShowLORAData(void)
{
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%i\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%i\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"humidity\": \"%i\",\n",lora_data.humidity);
gCatena.SafePrintf(" \"pressure\": \"%i\",\n",lora_data.pressure);
gCatena.SafePrintf(" \"reading_offset\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%i",lora_data.reading_offset[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight_raw1\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%i",lora_data.weight_raw1[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight_raw2\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%i",lora_data.weight_raw2[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%i",lora_data.temperature[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf("}\n");
}
void ReadSensors(bool firstTime)
{
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
int16_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int16_t w1_current;
int16_t w2_current;
int16_t temp_last;
uint8_t humidity_last;
uint8_t pressure_last;
int16_t w1_last;
int16_t w2_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = m.Temperature;
humidity_current = m.Humidity;
pressure_current = m.Pressure;
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
w1_current = w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
w2_current = w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
if (my_position > 0) {
temp_last = lora_data.temperature[my_position - 1];
w1_last = lora_data.weight_raw1[my_position - 1];
w2_last = lora_data.weight_raw2[my_position - 1];
}
// Wir registrieren die Werte nur, falls die Abweichung zur letzen Messung gross genug ist
if (my_position == 0 || abs(temp_current - temp_last) > 10 || abs(w1_current - w1_last) > 50 || abs(w2_current - w2_last) > 50) {
lora_data.vbat = (vBat * 1000 / 20);
if (my_position > 0) {
lora_data.reading_offset[my_position] = int((millis() - timer_pos0) / 1000);
} else {
timer_pos0 = millis();
}
lora_data.weight_raw1[my_position] = w1_current;
lora_data.weight_raw2[my_position] = w2_current;
lora_data.temperature[my_position] = temp_current;
lora_data.humidity = humidity_current;
lora_data.pressure = humidity_current;
ShowLORAData();
my_position++;
}
else {
gCatena.SafePrintf("Too little difference, measurements are not stored...\n");
}
// Should we send the Data?
if (firstTime || ShouldDataBeSent()) {
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink();
}
}
void startSendingUplink(void)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}

View File

@ -0,0 +1,656 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 6;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint32_t PRESSURE_OFFSET = 80000;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
typedef struct {
long cal_w1_0;
long cal_w2_0;
float cal_w1_factor;
float cal_w2_factor;
} FRAM_data;
typedef struct {
uint8_t version; // Versionierung des Paketformats
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t pressure; // Luftdruck in Pascal (0 entspricht 80000 Pa)
uint8_t reading_offset[MAX_VALUES_TO_SEND]; // Zeit der weiteren Messung in Sekunden seit Start
int16_t weight[MAX_VALUES_TO_SEND]; // Gewicht in 10-Gramm, Addition beider Waegzellen
uint8_t temperature[MAX_VALUES_TO_SEND]; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
} LORA_data;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
FRAM_data fram_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
// aktuell noch als Konstanten...
fram_data.cal_w1_0 = -10000;
fram_data.cal_w2_0 = -10000;
fram_data.cal_w1_factor = 1;
fram_data.cal_w2_factor = 1;
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A3, A2);
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(A1, A0);
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
bool ShouldDataBeSent(void)
{
bool res = (my_position > MAX_VALUES_TO_SEND) ||
((millis() - timer_pos0) > 3600000);
return res;
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
lora_data.humidity = 0;
lora_data.pressure = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.reading_offset[i] = 0;
lora_data.weight[i] = 0;
lora_data.temperature[i] = 0;
}
my_position = 0;
}
void ShowLORAData(void)
{
char str[10];
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"humidity\": \"%d\",\n",lora_data.humidity);
gCatena.SafePrintf(" \"pressure\": \"%d\",\n",lora_data.pressure);
gCatena.SafePrintf(" \"reading_offset\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%u",lora_data.reading_offset[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.temperature[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
void ReadSensors(bool firstTime)
{
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
int8_t temp_current;
uint8_t humidity_current;
int16_t pressure_current;
int32_t weight_current;
int32_t w1_current;
int32_t w2_current;
int8_t temp_last;
int32_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (int8_t)((m.Temperature + 40) * 2);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint16_t)(m.Pressure - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
w1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
w2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
// Calculate Weight
weight_current = (((w1_current - fram_data.cal_w1_0) / fram_data.cal_w1_factor) + ((w2_current - fram_data.cal_w2_0) / fram_data.cal_w2_factor));
// calculate last value
if (my_position > 0) {
temp_last =lora_data.temperature[my_position -1];
weight_last = lora_data.weight[my_position - 1];
}
// Wir registrieren die Werte nur, falls die Abweichung zur letzen Messung gross genug ist, oder es die erste Messung ist
if (my_position == 0 || abs(temp_current - temp_last) > 3 || abs(weight_current - weight_last) > 50) {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight[my_position] = weight_current;
lora_data.temperature[my_position] = temp_current;
if (my_position > 0) {
lora_data.reading_offset[my_position] = (uint8_t)((millis() - timer_pos0) / 1000);
} else {
timer_pos0 = millis();
lora_data.humidity = humidity_current;
lora_data.pressure = pressure_current;
}
ShowLORAData();
my_position++;
}
else {
gCatena.SafePrintf("Too little difference, measurements are not stored...\n");
}
// Should we send the Data?
if (firstTime || ShouldDataBeSent()) {
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink();
}
}
void startSendingUplink(void)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}

View File

@ -0,0 +1,668 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 3;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint32_t PRESSURE_OFFSET = 80000;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
typedef struct {
uint8_t version; // Versionierung des Paketformats
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
uint8_t temperature[MAX_VALUES_TO_SEND]; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
int16_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Pascal (0 entspricht 80000 Pa)
int32_t weight1[MAX_VALUES_TO_SEND]; // Waegezelle 1, Raw Value
int32_t weight2[MAX_VALUES_TO_SEND]; // Waegezelle 2, Raw Value
uint16_t reading_offset[MAX_VALUES_TO_SEND - 1]; // Zeit der weiteren Messungen in Sekunden seit Start
} __attribute__((packed)) LORA_data;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A3, A2);
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(A1, A0);
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
bool ShouldDataBeSent(void)
{
bool res = (my_position >= MAX_VALUES_TO_SEND) ||
((millis() - timer_pos0) > 3600000);
return res;
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
if (i < (MAX_VALUES_TO_SEND - 1)) {
lora_data.reading_offset[i] = 0;
}
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight1[i] = 0;
lora_data.weight2[i] = 0;
lora_data.temperature[i] = 0;
}
my_position = 0;
}
void ShowLORAData(void)
{
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"reading_offset\": [");
for (int i = 0; i < (MAX_VALUES_TO_SEND - 1); i++) {
gCatena.SafePrintf("%u",lora_data.reading_offset[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight1\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight1[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight2\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight2[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.temperature[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
void ReadSensors(bool firstTime)
{
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
int8_t temp_current;
uint8_t humidity_current;
int16_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
int8_t temp_last;
int32_t weight1_last;
int32_t weight2_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (int8_t)((m.Temperature + 40) * 2);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (int16_t)(m.Pressure - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
// calculate last value
if (my_position > 0) {
temp_last =lora_data.temperature[my_position -1];
weight1_last = lora_data.weight1[my_position - 1];
weight2_last = lora_data.weight2[my_position - 1];
}
// Wir registrieren die Werte nur, falls die Abweichung zur letzen Messung gross genug ist, oder es die erste Messung ist
if (my_position == 0 || abs(temp_current - temp_last) > 3 || abs(weight1_current - weight1_last) > 500 || abs(weight2_current - weight2_last) > 500) {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight1[my_position] = weight1_current;
lora_data.weight2[my_position] = weight2_current;
lora_data.temperature[my_position] = temp_current;
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
if (my_position > 0) {
lora_data.reading_offset[my_position - 1] = (uint16_t)((millis() - timer_pos0) / 1000);
} else {
timer_pos0 = millis();
}
ShowLORAData();
my_position++;
}
else {
gCatena.SafePrintf("Too little difference, measurements are not stored...\n");
}
// Should we send the Data?
if (firstTime || ShouldDataBeSent()) {
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink();
} else {
doLightSleep(&sensorJob);
}
}
void startSendingUplink(void)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,667 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 3;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint32_t PRESSURE_OFFSET = 80000;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
typedef struct {
uint8_t version; // Versionierung des Paketformats
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
uint8_t temperature[MAX_VALUES_TO_SEND]; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
int16_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Pascal (0 entspricht 80000 Pa)
int32_t weight1[MAX_VALUES_TO_SEND]; // Waegezelle 1, Raw Value
int32_t weight2[MAX_VALUES_TO_SEND]; // Waegezelle 2, Raw Value
uint8_t reading_offset[MAX_VALUES_TO_SEND]; // Zeit der weiteren Messungen in Minuten seit Start, letzter Wert: Offset beim Senden des Pakets
} __attribute__((packed)) LORA_data;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A3, A2);
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(A1, A0);
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
bool ShouldDataBeSent(void)
{
bool res = (my_position >= MAX_VALUES_TO_SEND) ||
((millis() - timer_pos0) > 3600000);
return res;
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.reading_offset[i] = 0;
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight1[i] = 0;
lora_data.weight2[i] = 0;
lora_data.temperature[i] = 0;
}
my_position = 0;
}
void ShowLORAData(void)
{
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"reading_offset\": [");
for (int i = 0; i < (MAX_VALUES_TO_SEND); i++) {
gCatena.SafePrintf("%d",lora_data.reading_offset[i]);
if (i < (MAX_VALUES_TO_SEND - 2)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight1\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight1[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight2\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight2[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.temperature[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
void ReadSensors(bool firstTime)
{
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
int8_t temp_current;
uint8_t humidity_current;
int16_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
int8_t temp_last;
int32_t weight1_last;
int32_t weight2_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (int8_t)((m.Temperature + 40) * 2);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (int16_t)(m.Pressure - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
// calculate last value
if (my_position > 0) {
temp_last =lora_data.temperature[my_position -1];
weight1_last = lora_data.weight1[my_position - 1];
weight2_last = lora_data.weight2[my_position - 1];
}
// Wir registrieren die Werte nur, falls die Abweichung zur letzen Messung gross genug ist, oder es die erste Messung ist
if (my_position == 0 || abs(temp_current - temp_last) > 3 || abs(weight1_current - weight1_last) > 500 || abs(weight2_current - weight2_last) > 500) {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight1[my_position] = weight1_current;
lora_data.weight2[my_position] = weight2_current;
lora_data.temperature[my_position] = temp_current;
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
if (my_position > 0) {
lora_data.reading_offset[my_position - 1] = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
} else {
timer_pos0 = millis();
}
ShowLORAData();
my_position++;
}
else {
gCatena.SafePrintf("Too little difference, measurements are not stored...\n");
}
// Should we send the Data?
if (firstTime || ShouldDataBeSent()) {
lora_data.reading_offset[my_position] = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink();
} else {
doLightSleep(&sensorJob);
}
}
void startSendingUplink(void)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,923 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
//CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE = 10, // every 10 seconds
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
CATCFG_T_CYCLE_INITIAL = 30, // every 30 seconds initially
CATCFG_INTERVAL_COUNT_INITIAL = 30, // repeat for 15 minutes
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
CATCFG_T_MIN = CATCFG_T_OVERHEAD,
CATCFG_T_MAX = CATCFG_T_CYCLE < 60 * 60 ? 60 * 60 : CATCFG_T_CYCLE, // normally one hour max.
CATCFG_INTERVAL_COUNT = 30,
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// the cycle time to use
unsigned gTxCycle;
// remaining before we reset to default
unsigned gTxCycleCount;
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
// Additional Commands
// forward reference to the command function
cCommandStream::CommandFn cmdHello;
cCommandStream::CommandFn cmdGetCalibrationSettings;
cCommandStream::CommandFn cmdGetSensorReadings;
cCommandStream::CommandFn cmdGetScale1;
cCommandStream::CommandFn cmdGetScale2;
cCommandStream::CommandFn cmdCalibrateZeroScale1;
cCommandStream::CommandFn cmdCalibrateZeroScale2;
cCommandStream::CommandFn cmdCalibrateScale1;
cCommandStream::CommandFn cmdCalibrateScale2;
// the individual commmands are put in this table
static const cCommandStream::cEntry sMyExtraCommmands[] =
{
{ "hello", cmdHello },
{ "get_calibration_settings", cmdGetCalibrationSettings },
{ "get_sensor_readings", cmdGetSensorReadings },
{ "calibrate_zero_scale1", cmdCalibrateZeroScale1 },
{ "calibrate_zero_scale2", cmdCalibrateZeroScale2 },
{ "calibrate_scale1", cmdCalibrateScale1 },
{ "calibrate_scale2", cmdCalibrateScale2 },
// other commands go here....
};
/* a top-level structure wraps the above and connects to the system table */
/* it optionally includes a "first word" so you can for sure avoid name clashes */
static cCommandStream::cDispatch
sMyExtraCommands_top(
sMyExtraCommmands, /* this is the pointer to the table */
sizeof(sMyExtraCommmands), /* this is the size of the table */
"application" /* this is the "first word" for all the commands in this table*/
);
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 8;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint8_t LORA_DATA_VERSION_FIRST_PACKAGE = 128;
static const uint32_t PRESSURE_OFFSET = 825;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
// must be 65 bytes long...
typedef struct {
long cal_w1_0; // 4 Bytes, Wert Waegezelle 1 ohne Gewicht
long cal_w2_0; // 4 Bytes, Wert Waegezelle 2 ohne Gewicht
float cal_w1_factor; // 4 Bytes,
float cal_w2_factor;
byte fill[49];
} __attribute__((packed)) CONFIG_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur (Startwert) in 1/10 Grad Celsius
int8_t temperature_change[MAX_VALUES_TO_SEND - 1]; // Unterschied Temperatur seit letztem Messwert in 1/10 Grad Celsius
uint8_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
uint16_t weight[MAX_VALUES_TO_SEND]; // Waegezelle Gesamtgewicht, in 5g
uint8_t offset_last_reading; // Zeitunterschied letzte zu erste Messung (in Minuten)
} __attribute__((packed)) LORA_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur in 1/10 Grad Celsius
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} __attribute__((packed)) LORA_data_first;
typedef struct {
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur in 1/10 Grad Celsius
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} SENSOR_data;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
LORA_data_first lora_data_first;
CONFIG_data config_data;
SENSOR_data last_sensor_reading;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
/* for 4451, we need wider tolerances, it seems */
#if defined(ARDUINO_ARCH_STM32)
LMIC_setClockError(10 * 65536 / 100);
#endif
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
// prepare external Power
pinMode(D10, OUTPUT);
digitalWrite(D10, HIGH);
/* add our application-specific commands */
gCatena.addCommands(
sMyExtraCommands_top,
nullptr
);
// read config_data from fram...
//gCatena.SafePrintf("Reading Calibration Config from FRAM...\n");
gCatena.getFram()->getField(cFramStorage::kBme680Cal, (uint8_t *)&config_data, sizeof(config_data));
//gCatena.SafePrintf("cal_w1_0: %d\n",config_data.cal_w1_0);
//gCatena.SafePrintf("cal_w2_0: %d\n",config_data.cal_w2_0);
//gCatena.SafePrintf("cal_w1_factor: %d.%03d\n",(int)config_data.cal_w1_factor,(int)(config_data.cal_w1_factor*1000)%1000);
//gCatena.SafePrintf("cal_w2_factor: %d.%03d\n",(int)config_data.cal_w2_factor,(int)(config_data.cal_w2_factor*1000)%1000);
//gCatena.SafePrintf("Size of config_data: %d\n",sizeof(config_data));
// im Moment statisch...
//config_data.cal_w1_0 = 20000;
//config_data.cal_w2_0 = 20000;
//config_data.cal_w1_factor = 2.5;
//config_data.cal_w2_factor = 2.5;
// config_data speichern...
//gCatena.SafePrintf("Writing Calibration Config to FRAM...\n");
//gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
//gCatena.SafePrintf("\n");
//gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
//gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
//gCatena.SafePrintf("Target network: %s / %s\n",
// gLoRaWAN.GetNetworkName(),
// gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
//gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
//gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
//gCatena.SafePrintf("USB enabled\n");
#else
//gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
//gCatena.SafePrintf(
// "CPU Unique ID: %s\n",
// gCatena.GetUniqueIDstring(&CpuIDstring));
//gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
//gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::On);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
//gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
// we only read vBus once, as there was a read error after deep sleep...
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
//gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A1, A0);
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(D12, A2);
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true,false);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
lora_data.temperature = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight[i] = 0;
if (i < (MAX_VALUES_TO_SEND -1)) {
lora_data.temperature_change[i] = 0;
}
}
lora_data_first.version = LORA_DATA_VERSION_FIRST_PACKAGE;
lora_data_first.vbat = 0;
lora_data_first.humidity = 0;
lora_data_first.pressure = 0;
lora_data_first.weight1 = 0;
lora_data_first.weight2 = 0;
lora_data_first.weight = 0;
lora_data_first.temperature = 0;
my_position = 0;
}
void ShowLORAData(bool firstTime)
{
if (firstTime) {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data_first.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data_first.vbat);
gCatena.SafePrintf(" \"humidity\": \"%u\",\n",lora_data_first.humidity);
gCatena.SafePrintf(" \"pressure\": \"%u\",\n",lora_data_first.pressure);
gCatena.SafePrintf(" \"weight1\": \"%ld\",\n",lora_data_first.weight1);
gCatena.SafePrintf(" \"weight2\": \"%ld\",\n",lora_data_first.weight2);
gCatena.SafePrintf(" \"weight\": \"%u\",\n",lora_data_first.weight);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data_first.temperature);
gCatena.SafePrintf("}\n");
} else {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data.temperature);
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature_change\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND - 1; i++) {
gCatena.SafePrintf("%d",lora_data.temperature_change[i]);
if (i < (MAX_VALUES_TO_SEND - 2)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
}
void ReadSensors(bool firstTime, bool readOnly)
{
gCatena.SafePrintf("start ReadSensors\n");
int16_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
int16_t temp_last;
int16_t temp_change;
int32_t weight_current32;
uint16_t weight_current;
uint16_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (int16_t)((m.Temperature) * 10);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
//float vBus = gCatena.ReadVbus();
//gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
//fUsbPower = (vBus > 3.0) ? true : false;
// Read Scales
gCatena.SafePrintf("Before Read Scales\n");
// Power-Up HX711
LoadCell_1.power_up();
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
// Power-Down HX711
LoadCell_1.power_down();
// Power-Up HX711
LoadCell_2.power_up();
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
// Power-Down HX711
LoadCell_2.power_down();
gCatena.SafePrintf("After Read Scales\n");
// Gewicht berechnen
weight_current32 = ((weight1_current - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((weight2_current - config_data.cal_w2_0) / config_data.cal_w2_factor);
if (weight_current32 < 0) {
weight_current32 = 0;
} else if (weight_current32 > UINT16_MAX) {
weight_current32 = UINT16_MAX;
}
weight_current = (uint16_t)weight_current32;
if (not(readOnly)) {
// calculate last value
weight_last = 0;
temp_last = lora_data.temperature;
for (int i = 0; i < my_position; i++) {
temp_last = temp_last + lora_data.temperature_change[i];
}
if (my_position > 0) {
weight_last = lora_data.weight[my_position - 1];
}
if (firstTime) {
lora_data_first.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data_first.weight1 = weight1_current;
lora_data_first.weight2 = weight2_current;
lora_data_first.weight = weight_current;
lora_data_first.temperature = temp_current;
lora_data_first.humidity = humidity_current;
lora_data_first.pressure = pressure_current;
} else {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight[my_position] = weight_current;
if (my_position == 0) {
lora_data.temperature = temp_current;
} else {
temp_change = temp_current - temp_last;
if (temp_change > 127) {
temp_change = 127;
}
if (temp_change < -128) {
temp_change = -128;
}
lora_data.temperature_change[my_position - 1] = (uint8_t)temp_change;
}
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
}
if (my_position == 0) {
timer_pos0 = millis();
}
ShowLORAData(firstTime);
my_position++;
// Should we send the Data?
// we send data the first time the system is started, when the array is full
// or when the weight has fallen more than 100g or the first measurement is
// more than one hour old (which should not happen :-) )
if (firstTime || (my_position >= MAX_VALUES_TO_SEND) || ((weight_last - weight_current) > 20) || ((millis() - timer_pos0) > 3600000)) {
lora_data.offset_last_reading = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink(firstTime);
} else {
Serial.flush();
LoadCell_1.power_down();
LoadCell_2.power_down();
gCatena.Sleep(CATCFG_T_INTERVAL);
}
}
//gCatena.SafePrintf("set last_sensor_reading fields...\n");
last_sensor_reading.vbat = (uint8_t)(vBat * 1000.0f / 20);
last_sensor_reading.weight1 = weight1_current;
last_sensor_reading.weight2 = weight2_current;
last_sensor_reading.weight = weight_current;
last_sensor_reading.temperature = temp_current;
last_sensor_reading.humidity = humidity_current;
last_sensor_reading.pressure = pressure_current;
gCatena.SafePrintf("completed ReadSensors\n");
}
void startSendingUplink(bool firstTime)
{
//gCatena.SafePrintf("start startSendingUplink\n");
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
if (firstTime) {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed);
} else {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
ClearLoraData();
//gCatena.SafePrintf("completed startSendingUplink\n");
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
//gCatena.SafePrintf("start sendBufferDoneCb\n");
osjobcb_t pFn;
pFn = settleDoneCb;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
//gCatena.SafePrintf("completed sendBufferDoneCb\n");
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
//gCatena.SafePrintf("start txFailedDoneCb\n");
//gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
//gCatena.SafePrintf("completed txFailedDoneCb\n");
}
static void settleDoneCb(
osjob_t* pSendJob)
{
//gCatena.SafePrintf("start settleDoneCb\n");
if (fUsbPower) {
//gCatena.SafePrintf("we are running on USB power, so we do not go to deep sleep...\n");
//gCatena.SafePrintf("calling os_setTimedCallback in settleDoneCb...Interval=%d\n",CATCFG_T_INTERVAL);
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
&sensorJob,
os_getTime()+sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb
);
gCatena.SafePrintf("returning from settleDoneCb\n");
return;
}
/* ok..., now it's time for a deep sleep */
Serial.flush();
LoadCell_1.power_down();
LoadCell_2.power_down();
gCatena.Sleep(CATCFG_T_INTERVAL);
sleepDoneCb(pSendJob);
gCatena.SafePrintf("completed settleDoneCb\n");
}
static void sleepDoneCb(osjob_t* pJob)
{
//gCatena.SafePrintf("start sleepDoneCb\n");
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
//gCatena.SafePrintf("completed sleepDoneCb\n");
}
static void warmupDoneCb(osjob_t* pJob)
{
//gCatena.SafePrintf("start warmupDoneCb\n");
gLed.Set(LedPattern::WarmingUp);
ReadSensors(false,false);
//gCatena.SafePrintf("completed warmupDoneCb\n");
}
/* process "application hello" -- args are ignored */
// argv[0] is "hello"
// argv[1..argc-1] are the (ignored) arguments
cCommandStream::CommandStatus cmdHello(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
pThis->printf("Hello, world!\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetCalibrationSettings(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
pThis->printf("{\n");
pThis->printf(" \"cal_w1_0\": \"%d\",\n",config_data.cal_w1_0);
pThis->printf(" \"cal_w2_0\": \"%d\",\n",config_data.cal_w2_0);
pThis->printf(" \"cal_w1_factor\": \"%d.%03d\n",(int)config_data.cal_w1_factor,(int)abs(config_data.cal_w1_factor*1000)%1000);
pThis->printf(" \"cal_w2_factor\": \"%d.%03d\n",(int)config_data.cal_w2_factor,(int)abs(config_data.cal_w2_factor*1000)%1000);
pThis->printf("}\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetSensorReadings(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
ReadSensors(false,true);
pThis->printf("{\n");
pThis->printf(" \"weight\": \"%d\",\n",last_sensor_reading.weight);
pThis->printf(" \"weight1_raw\": \"%d\",\n",last_sensor_reading.weight1);
pThis->printf(" \"weight2_raw\": \"%d\",\n",last_sensor_reading.weight2);
pThis->printf(" \"temperature\": \"%d\",\n",last_sensor_reading.temperature);
pThis->printf(" \"humidity\": \"%d\",\n",last_sensor_reading.humidity);
pThis->printf(" \"pressure\": \"%d\",\n",last_sensor_reading.pressure);
pThis->printf(" \"batt\": \"%d\",\n", last_sensor_reading.vbat);
pThis->printf("}\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetScale1(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
pThis->printf("getscale1\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdGetScale2(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
pThis->printf("getscale2\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateZeroScale1(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
ReadSensors(false,true);
config_data.cal_w1_0 = last_sensor_reading.weight1;
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_zero_scale1 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateZeroScale2(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
ReadSensors(false,true);
config_data.cal_w2_0 = last_sensor_reading.weight2;
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_zero_scale2 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateScale1(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
ReadSensors(false,true);
String w1_gramm(argv[1]);
config_data.cal_w1_factor = (((float)last_sensor_reading.weight1 - config_data.cal_w1_0)/ w1_gramm.toFloat());
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_scale1 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}
cCommandStream::CommandStatus cmdCalibrateScale2(cCommandStream *pThis,void *pContext,int argc,char **argv)
{
ReadSensors(false,true);
String w2_gramm(argv[1]);
config_data.cal_w2_factor = (((float)last_sensor_reading.weight2 - config_data.cal_w2_0)/ w2_gramm.toFloat());
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
pThis->printf("{ \"msg\": \"calibrate_scale2 was successful\" }\n");
return cCommandStream::CommandStatus::kSuccess;
}

View File

@ -0,0 +1,727 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 6;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint8_t LORA_DATA_VERSION_FIRST_PACKAGE = 128;
static const uint32_t PRESSURE_OFFSET = 825;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
typedef struct {
long cal_w1_0;
long cal_w2_0;
float cal_w1_factor;
float cal_w2_factor;
} CONFIG_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
uint8_t temperature[MAX_VALUES_TO_SEND]; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
uint8_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
uint16_t weight[MAX_VALUES_TO_SEND]; // Waegezelle Gesamtgewicht, in 5g
uint8_t reading_offset[MAX_VALUES_TO_SEND]; // Zeit der weiteren Messungen in Minuten seit Start, letzter Wert: Offset beim Senden des Pakets
} __attribute__((packed)) LORA_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Prozent
uint8_t temperature; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} __attribute__((packed)) LORA_data_first;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
LORA_data_first lora_data_first;
CONFIG_data config_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
// im Moment statisch...
config_data.cal_w1_0 = 20000;
config_data.cal_w2_0 = 20000;
config_data.cal_w1_factor = 2.5;
config_data.cal_w2_factor = 2.5;
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A3, A2);
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(A1, A0);
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
bool ShouldDataBeSent(void)
{
bool res = (my_position >= MAX_VALUES_TO_SEND) ||
((millis() - timer_pos0) > 3600000);
return res;
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.reading_offset[i] = 0;
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight[i] = 0;
lora_data.temperature[i] = 0;
}
lora_data_first.version = LORA_DATA_VERSION_FIRST_PACKAGE;
lora_data_first.vbat = 0;
lora_data_first.humidity = 0;
lora_data_first.pressure = 0;
lora_data_first.weight1 = 0;
lora_data_first.weight2 = 0;
lora_data_first.weight = 0;
lora_data_first.temperature = 0;
my_position = 0;
}
void ShowLORAData(bool firstTime)
{
if (firstTime) {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data_first.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data_first.vbat);
gCatena.SafePrintf(" \"humidity\": \"%u\",\n",lora_data_first.humidity);
gCatena.SafePrintf(" \"pressure\": \"%u\",\n",lora_data_first.pressure);
gCatena.SafePrintf(" \"weight1\": \"%ld\",\n",lora_data_first.weight1);
gCatena.SafePrintf(" \"weight2\": \"%ld\",\n",lora_data_first.weight2);
gCatena.SafePrintf(" \"weight\": \"%u\",\n",lora_data_first.weight);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data_first.temperature);
gCatena.SafePrintf("}\n");
} else {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"reading_offset\": [");
for (int i = 0; i < (MAX_VALUES_TO_SEND); i++) {
gCatena.SafePrintf("%d",lora_data.reading_offset[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.temperature[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
}
void ReadSensors(bool firstTime)
{
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
uint8_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
uint8_t temp_last;
uint16_t weight_current;
uint16_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (uint8_t)((m.Temperature + 40) * 2);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
// Gewicht berechnen
weight_current = ((weight1_current - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((weight2_current - config_data.cal_w2_0) / config_data.cal_w2_factor);
// calculate last value
if (my_position > 0) {
temp_last =lora_data.temperature[my_position -1];
weight_last = lora_data.weight[my_position - 1];
}
// Wir registrieren die Werte nur, falls die Abweichung zur letzen Messung gross genug ist, oder es die erste Messung ist
if (my_position == 0 || abs(temp_current - temp_last) > 3 || abs(weight_current - weight_last) > 10) {
if (firstTime) {
lora_data_first.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data_first.weight1 = weight1_current;
lora_data_first.weight2 = weight2_current;
lora_data_first.weight = weight_current;
lora_data_first.temperature = temp_current;
lora_data_first.humidity = humidity_current;
lora_data_first.pressure = pressure_current;
} else {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight[my_position] = weight_current;
lora_data.temperature[my_position] = temp_current;
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
}
if (my_position > 0) {
lora_data.reading_offset[my_position - 1] = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
} else {
timer_pos0 = millis();
}
ShowLORAData(firstTime);
my_position++;
}
else {
gCatena.SafePrintf("Too little difference, measurements are not stored...\n");
}
// Should we send the Data?
if (firstTime || ShouldDataBeSent()) {
lora_data.reading_offset[my_position] = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink(firstTime);
} else {
doLightSleep(&sensorJob);
}
}
void startSendingUplink(bool firstTime)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
if (firstTime) {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed);
} else {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}

View File

@ -0,0 +1,727 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 6;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint8_t LORA_DATA_VERSION_FIRST_PACKAGE = 128;
static const uint32_t PRESSURE_OFFSET = 825;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
typedef struct {
long cal_w1_0;
long cal_w2_0;
float cal_w1_factor;
float cal_w2_factor;
} CONFIG_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
uint8_t temperature[MAX_VALUES_TO_SEND]; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
uint8_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
uint16_t weight[MAX_VALUES_TO_SEND]; // Waegezelle Gesamtgewicht, in 5g
uint8_t reading_offset[MAX_VALUES_TO_SEND]; // Zeit der weiteren Messungen in Minuten seit Start, letzter Wert: Offset beim Senden des Pakets
} __attribute__((packed)) LORA_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Prozent
uint8_t temperature; // Temperatur in 1/2 Grad Celsius (0 => -40 C, 255 => 87.5 C)
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} __attribute__((packed)) LORA_data_first;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
LORA_data_first lora_data_first;
CONFIG_data config_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
// im Moment statisch...
config_data.cal_w1_0 = 20000;
config_data.cal_w2_0 = 20000;
config_data.cal_w1_factor = 2.5;
config_data.cal_w2_factor = 2.5;
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
LoadCell_1.begin(A3, A2);
gCatena.SafePrintf("Setup Scale 2...\n");
LoadCell_2.begin(A1, A0);
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
bool ShouldDataBeSent(void)
{
bool res = (my_position >= MAX_VALUES_TO_SEND) ||
((millis() - timer_pos0) > 3600000);
return res;
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.reading_offset[i] = 0;
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight[i] = 0;
lora_data.temperature[i] = 0;
}
lora_data_first.version = LORA_DATA_VERSION_FIRST_PACKAGE;
lora_data_first.vbat = 0;
lora_data_first.humidity = 0;
lora_data_first.pressure = 0;
lora_data_first.weight1 = 0;
lora_data_first.weight2 = 0;
lora_data_first.weight = 0;
lora_data_first.temperature = 0;
my_position = 0;
}
void ShowLORAData(bool firstTime)
{
if (firstTime) {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data_first.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data_first.vbat);
gCatena.SafePrintf(" \"humidity\": \"%u\",\n",lora_data_first.humidity);
gCatena.SafePrintf(" \"pressure\": \"%u\",\n",lora_data_first.pressure);
gCatena.SafePrintf(" \"weight1\": \"%ld\",\n",lora_data_first.weight1);
gCatena.SafePrintf(" \"weight2\": \"%ld\",\n",lora_data_first.weight2);
gCatena.SafePrintf(" \"weight\": \"%u\",\n",lora_data_first.weight);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data_first.temperature);
gCatena.SafePrintf("}\n");
} else {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"reading_offset\": [");
for (int i = 0; i < (MAX_VALUES_TO_SEND); i++) {
gCatena.SafePrintf("%d",lora_data.reading_offset[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.temperature[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
}
void ReadSensors(bool firstTime)
{
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
uint8_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
uint8_t temp_last;
uint16_t weight_current;
uint16_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (uint8_t)((m.Temperature + 40) * 2);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
// Gewicht berechnen
weight_current = ((weight1_current - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((weight2_current - config_data.cal_w2_0) / config_data.cal_w2_factor);
// calculate last value
if (my_position > 0) {
temp_last =lora_data.temperature[my_position -1];
weight_last = lora_data.weight[my_position - 1];
}
// Wir registrieren die Werte nur, falls die Abweichung zur letzen Messung gross genug ist, oder es die erste Messung ist
if (my_position == 0 || abs(temp_current - temp_last) > 3 || abs(weight_current - weight_last) > 10) {
if (firstTime) {
lora_data_first.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data_first.weight1 = weight1_current;
lora_data_first.weight2 = weight2_current;
lora_data_first.weight = weight_current;
lora_data_first.temperature = temp_current;
lora_data_first.humidity = humidity_current;
lora_data_first.pressure = pressure_current;
} else {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight[my_position] = weight_current;
lora_data.temperature[my_position] = temp_current;
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
}
if (my_position > 0) {
lora_data.reading_offset[my_position - 1] = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
} else {
timer_pos0 = millis();
}
ShowLORAData(firstTime);
my_position++;
}
else {
gCatena.SafePrintf("Too little difference, measurements are not stored...\n");
}
// Should we send the Data?
if (firstTime || ShouldDataBeSent()) {
lora_data.reading_offset[MAX_VALUES_TO_SEND - 1] = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink(firstTime);
} else {
doLightSleep(&sensorJob);
}
}
void startSendingUplink(bool firstTime)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
if (firstTime) {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed);
} else {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}

View File

@ -0,0 +1,768 @@
/*
beescale_lora_mcci.ino
BeieliScale, see https://mini-beieli.ch
Joerg Lehmann, nbit Informatik GmbH
*/
#include <Catena.h>
#include <Catena_Led.h>
#include <Catena_CommandStream.h>
#include <Catena_Mx25v8035f.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Arduino_LoRaWAN.h>
#include <lmic.h>
#include <hal/hal.h>
#include <mcciadk_baselib.h>
#include <cmath>
#include <type_traits>
#include <HX711.h>
using namespace McciCatena;
/****************************************************************************\
|
| MANIFEST CONSTANTS & TYPEDEFS
|
\****************************************************************************/
/* how long do we wait between transmissions? (in seconds) */
enum {
// set this to interval between transmissions, in seconds
// Actual time will be a little longer because have to
// add measurement and broadcast time, but we attempt
// to compensate for the gross effects below.
//CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE = 6 * 60, // every 6 minutes
CATCFG_T_CYCLE_TEST = 30, // every 10 seconds
};
/* additional timing parameters; ususually you don't change these. */
enum {
CATCFG_T_WARMUP = 1,
CATCFG_T_SETTLE = 5,
CATCFG_T_OVERHEAD = (CATCFG_T_WARMUP + CATCFG_T_SETTLE),
};
constexpr uint32_t CATCFG_GetInterval(uint32_t tCycle)
{
return (tCycle < CATCFG_T_OVERHEAD)
? CATCFG_T_OVERHEAD
: tCycle - CATCFG_T_OVERHEAD;
}
enum {
CATCFG_T_INTERVAL = CATCFG_GetInterval(CATCFG_T_CYCLE),
};
enum {
PIN_ONE_WIRE = A2, // XSDA1 == A2
PIN_SHT10_CLK = 8, // XSCL0 == D8
PIN_SHT10_DATA = 12, // XSDA0 == D12
};
// forwards
static void settleDoneCb(osjob_t* pSendJob);
static void warmupDoneCb(osjob_t* pSendJob);
static void txFailedDoneCb(osjob_t* pSendJob);
static void sleepDoneCb(osjob_t* pSendJob);
static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb;
/****************************************************************************\
|
| READ-ONLY DATA
|
\****************************************************************************/
static const char sVersion[] = "0.1";
static const byte MAX_VALUES_TO_SEND = 8;
static const uint8_t LORA_DATA_VERSION = 1;
static const uint8_t LORA_DATA_VERSION_FIRST_PACKAGE = 128;
static const uint32_t PRESSURE_OFFSET = 825;
/****************************************************************************\
|
| VARIABLES
|
\****************************************************************************/
// must be 65 bytes long...
typedef struct {
long cal_w1_0; // 4 Bytes, Wert Waegezelle 1 ohne Gewicht
long cal_w2_0; // 4 Bytes, Wert Waegezelle 1 ohne Gewicht
float cal_w1_factor; // 4 Bytes,
float cal_w2_factor;
byte fill[49];
} __attribute__((packed)) CONFIG_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity[MAX_VALUES_TO_SEND]; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur (Startwert) in 1/10 Grad Celsius
int8_t temperature_change[MAX_VALUES_TO_SEND - 1]; // Unterschied Temperatur seit letztem Messwert in 1/10 Grad Celsius
uint8_t pressure[MAX_VALUES_TO_SEND]; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
uint16_t weight[MAX_VALUES_TO_SEND]; // Waegezelle Gesamtgewicht, in 5g
uint8_t offset_last_reading; // Zeitunterschied letzte zu erste Messung (in Minuten)
} __attribute__((packed)) LORA_data;
typedef struct {
uint8_t version; // Version
uint8_t vbat; // Batteriespannung (1 Einheit => 20 mV)
uint8_t humidity; // Luftfeuchtigkeit in Prozent
int16_t temperature; // Temperatur in 1/10 Grad Celsius
uint8_t pressure; // Luftdruck in Hekto-Pascal (0 entspricht 825 hPa)
int32_t weight1; // Waegezelle 1, Raw Value
int32_t weight2; // Waegezelle 2, Raw Value
uint16_t weight; // Waegezelle Gesamtgewicht, in 5g
} __attribute__((packed)) LORA_data_first;
byte my_position = 0; // what is our actual measurement, starts with 0
long timer_pos0;
// Global Variables
LORA_data lora_data;
LORA_data_first lora_data_first;
CONFIG_data config_data;
// generic timer
long t_cur;
// the primary object
Catena gCatena;
//
// the LoRaWAN backhaul. Note that we use the
// Catena version so it can provide hardware-specific
// information to the base class.
//
Catena::LoRaWAN gLoRaWAN;
//
// the LED
//
StatusLed gLed(Catena::PIN_STATUS_LED);
// The temperature/humidity sensor
Adafruit_BME280 gBME280; // The default initalizer creates an I2C connection
bool fBme;
SPIClass gSPI2(
Catena::PIN_SPI2_MOSI,
Catena::PIN_SPI2_MISO,
Catena::PIN_SPI2_SCK);
// The flash
Catena_Mx25v8035f gFlash;
bool fFlash;
// Scales
HX711 LoadCell_1;
HX711 LoadCell_2;
// USB power
bool fUsbPower;
// have we printed the sleep info?
bool g_fPrintedSleeping = false;
// the job that's used to synchronize us with the LMIC code
static osjob_t sensorJob;
void sensorJob_cb(osjob_t* pJob);
void setup(void)
{
gCatena.begin();
ClearLoraData();
setup_platform();
setup_bme280();
setup_scales();
setup_flash();
setup_uplink();
}
void setup_platform(void)
{
// read config_data from fram...
gCatena.SafePrintf("Reading Calibration Config from FRAM...\n");
gCatena.getFram()->getField(cFramStorage::kBme680Cal, (uint8_t *)&config_data, sizeof(config_data));
gCatena.SafePrintf("cal_w1_0: %d\n",config_data.cal_w1_0);
gCatena.SafePrintf("cal_w2_0: %d\n",config_data.cal_w2_0);
gCatena.SafePrintf("cal_w1_factor: %d.%03d\n",(int)config_data.cal_w1_factor,(int)(config_data.cal_w1_factor*1000)%1000);
gCatena.SafePrintf("cal_w2_factor: %d.%03d\n",(int)config_data.cal_w2_factor,(int)(config_data.cal_w2_factor*1000)%1000);
gCatena.SafePrintf("Size of config_data: %d\n",sizeof(config_data));
// im Moment statisch...
//config_data.cal_w1_0 = 20000;
//config_data.cal_w2_0 = 20000;
//config_data.cal_w1_factor = 2.5;
//config_data.cal_w2_factor = 2.5;
// config_data speichern...
gCatena.SafePrintf("Writing Calibration Config to FRAM...\n");
gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data));
#ifdef USBCON
// if running unattended, don't wait for USB connect.
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended))) {
while (!Serial)
/* wait for USB attach */
yield();
}
#endif
gCatena.SafePrintf("\n");
gCatena.SafePrintf("-------------------------------------------------------------------------------\n");
gCatena.SafePrintf("BeieliScale Version %s.\n", sVersion);
{
char sRegion[16];
gCatena.SafePrintf("Target network: %s / %s\n",
gLoRaWAN.GetNetworkName(),
gLoRaWAN.GetRegionString(sRegion, sizeof(sRegion)));
}
gCatena.SafePrintf("Enter 'help' for a list of commands.\n");
#ifdef CATENA_CFG_SYSCLK
gCatena.SafePrintf("SYSCLK: %d MHz\n", CATENA_CFG_SYSCLK);
#endif
#ifdef USBCON
gCatena.SafePrintf("USB enabled\n");
#else
gCatena.SafePrintf("USB disabled\n");
#endif
Catena::UniqueID_string_t CpuIDstring;
gCatena.SafePrintf(
"CPU Unique ID: %s\n",
gCatena.GetUniqueIDstring(&CpuIDstring));
gCatena.SafePrintf("--------------------------------------------------------------------------------\n");
gCatena.SafePrintf("\n");
// set up the LED
gLed.begin();
gCatena.registerObject(&gLed);
gLed.Set(LedPattern::FastFlash);
// set up LoRaWAN
gCatena.SafePrintf("LoRaWAN init: ");
if (!gLoRaWAN.begin(&gCatena)) {
gCatena.SafePrintf("failed\n");
}
else {
gCatena.SafePrintf("succeeded\n");
}
gCatena.registerObject(&gLoRaWAN);
/* find the platform */
const Catena::EUI64_buffer_t* pSysEUI = gCatena.GetSysEUI();
uint32_t flags;
const CATENA_PLATFORM* const pPlatform = gCatena.GetPlatform();
if (pPlatform) {
gCatena.SafePrintf("EUI64: ");
for (unsigned i = 0; i < sizeof(pSysEUI->b); ++i) {
gCatena.SafePrintf("%s%02x", i == 0 ? "" : "-", pSysEUI->b[i]);
}
gCatena.SafePrintf("\n");
flags = gCatena.GetPlatformFlags();
gCatena.SafePrintf(
"Platform Flags: %#010x\n",
flags);
gCatena.SafePrintf(
"Operating Flags: %#010x\n",
gCatena.GetOperatingFlags());
}
else {
gCatena.SafePrintf("**** no platform, check provisioning ****\n");
flags = 0;
}
}
void setup_bme280(void)
{
if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) {
fBme = true;
}
else {
fBme = false;
gCatena.SafePrintf("No BME280 found: check wiring\n");
}
}
void setup_scales(void)
{
gCatena.SafePrintf("Setup Scales...\n");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
gCatena.SafePrintf("Setup Scale 1...\n");
//LoadCell_1.begin(A3, A2);
if (!(LoadCell_1.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 1 not ready.\n");
}
gCatena.SafePrintf("Setup Scale 2...\n");
//LoadCell_2.begin(A1, A0);
if (!(LoadCell_2.wait_ready_timeout(1000))) {
gCatena.SafePrintf("Scale 2 not ready.\n");
}
gCatena.SafePrintf("Setup Scales is complete\n");
}
void setup_flash(void)
{
if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) {
fFlash = true;
gFlash.powerDown();
gCatena.SafePrintf("FLASH found, put power down\n");
}
else {
fFlash = false;
gFlash.end();
gSPI2.end();
gCatena.SafePrintf("No FLASH found: check hardware\n");
}
}
void setup_uplink(void)
{
/* trigger a join by sending the first packet */
if (!(gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fManufacturingTest))) {
if (!gLoRaWAN.IsProvisioned())
gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n");
else {
gLed.Set(LedPattern::Joining);
/* warm up the BME280 by discarding a measurement */
if (fBme)
(void)gBME280.readTemperature();
/* trigger a join by sending the first packet */
ReadSensors(true);
}
}
}
// The Arduino loop routine -- in our case, we just drive the other loops.
// If we try to do too much, we can break the LMIC radio. So the work is
// done by outcalls scheduled from the LMIC os loop.
void loop()
{
gCatena.poll();
}
void ClearLoraData(void)
{
lora_data.version = LORA_DATA_VERSION;
lora_data.vbat = 0;
lora_data.temperature = 0;
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
lora_data.humidity[i] = 0;
lora_data.pressure[i] = 0;
lora_data.weight[i] = 0;
if (i < (MAX_VALUES_TO_SEND -1)) {
lora_data.temperature_change[i] = 0;
}
}
lora_data_first.version = LORA_DATA_VERSION_FIRST_PACKAGE;
lora_data_first.vbat = 0;
lora_data_first.humidity = 0;
lora_data_first.pressure = 0;
lora_data_first.weight1 = 0;
lora_data_first.weight2 = 0;
lora_data_first.weight = 0;
lora_data_first.temperature = 0;
my_position = 0;
}
void ShowLORAData(bool firstTime)
{
if (firstTime) {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data_first.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data_first.vbat);
gCatena.SafePrintf(" \"humidity\": \"%u\",\n",lora_data_first.humidity);
gCatena.SafePrintf(" \"pressure\": \"%u\",\n",lora_data_first.pressure);
gCatena.SafePrintf(" \"weight1\": \"%ld\",\n",lora_data_first.weight1);
gCatena.SafePrintf(" \"weight2\": \"%ld\",\n",lora_data_first.weight2);
gCatena.SafePrintf(" \"weight\": \"%u\",\n",lora_data_first.weight);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data_first.temperature);
gCatena.SafePrintf("}\n");
} else {
gCatena.SafePrintf("{\n");
gCatena.SafePrintf(" \"version\": \"%u\",\n",lora_data.version);
gCatena.SafePrintf(" \"vbat\": \"%u\",\n",lora_data.vbat);
gCatena.SafePrintf(" \"temperature\": \"%u\",\n",lora_data.temperature);
gCatena.SafePrintf(" \"humidity\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.humidity[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"pressure\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%d",lora_data.pressure[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"weight\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
gCatena.SafePrintf("%ld",lora_data.weight[i]);
if (i < (MAX_VALUES_TO_SEND - 1)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("],\n");
gCatena.SafePrintf(" \"temperature_change\": [");
for (int i = 0; i < MAX_VALUES_TO_SEND - 1; i++) {
gCatena.SafePrintf("%d",lora_data.temperature_change[i]);
if (i < (MAX_VALUES_TO_SEND - 2)) {
gCatena.SafePrintf(",");
}
}
gCatena.SafePrintf("]\n");
gCatena.SafePrintf("}\n");
}
}
void ReadSensors(bool firstTime)
{
// Power-Up HX711
LoadCell_1.power_up();
LoadCell_1.power_up();
// vBat
float vBat = gCatena.ReadVbat();
gCatena.SafePrintf("vBat: %d mV\n", (int)(vBat * 1000.0f));
// vBus
float vBus = gCatena.ReadVbus();
gCatena.SafePrintf("vBus: %d mV\n", (int)(vBus * 1000.0f));
fUsbPower = (vBus > 3.0) ? true : false;
int16_t temp_current;
uint8_t humidity_current;
uint8_t pressure_current;
int32_t weight1_current;
int32_t weight2_current;
int16_t temp_last;
int16_t temp_change;
uint16_t weight_current;
uint16_t weight_last;
if (fBme) {
Adafruit_BME280::Measurements m = gBME280.readTemperaturePressureHumidity();
// temperature is 2 bytes from -0x80.00 to +0x7F.FF degrees C
// pressure is 2 bytes, hPa * 10.
// humidity is one byte, where 0 == 0/256 and 0xFF == 255/256.
gCatena.SafePrintf(
"BME280: T: %d P: %d RH: %d\n",
(int)m.Temperature,
(int)m.Pressure,
(int)m.Humidity);
temp_current = (int16_t)((m.Temperature) * 10);
humidity_current = (uint8_t)m.Humidity;
pressure_current = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET);
gCatena.SafePrintf("pressure_current: %d\n",pressure_current);
}
gCatena.SafePrintf("Before Read Scales\n");
if (LoadCell_1.is_ready()) {
Serial.println("HX711 LoadCell_1 is ready.");
long w1 = LoadCell_1.read_average(5);
weight1_current = (int32_t)w1;
gCatena.SafePrintf("Load_cell 1 output val: %ld\n", w1);
}
else {
Serial.println("HX711 LoadCell_1 not ready.");
}
if (LoadCell_2.is_ready()) {
Serial.println("HX711 LoadCell_2 is ready.");
long w2 = LoadCell_2.read_average(5);
weight2_current = (int32_t)w2;
gCatena.SafePrintf("Load_cell 2 output val: %ld\n", w2);
}
else {
Serial.println("HX711 LoadCell_2 not ready.");
}
gCatena.SafePrintf("After Read Scales\n");
// Power-Down HX711
LoadCell_1.power_down();
LoadCell_1.power_down();
// Gewicht berechnen
weight_current = ((weight1_current - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((weight2_current - config_data.cal_w2_0) / config_data.cal_w2_factor);
// calculate last value
weight_last = 0;
temp_last = lora_data.temperature;
for (int i = 0; i < my_position; i++) {
temp_last = temp_last + lora_data.temperature_change[i];
}
if (my_position > 0) {
weight_last = lora_data.weight[my_position - 1];
}
if (firstTime) {
lora_data_first.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data_first.weight1 = weight1_current;
lora_data_first.weight2 = weight2_current;
lora_data_first.weight = weight_current;
lora_data_first.temperature = temp_current;
lora_data_first.humidity = humidity_current;
lora_data_first.pressure = pressure_current;
} else {
lora_data.vbat = (uint8_t)(vBat * 1000.0f / 20);
lora_data.weight[my_position] = weight_current;
if (my_position == 0) {
lora_data.temperature = temp_current;
} else {
temp_change = temp_current - temp_last;
if (temp_change > 127) {
temp_change = 127;
}
if (temp_change < -128) {
temp_change = -128;
}
lora_data.temperature_change[my_position - 1] = (uint8_t)temp_change;
}
lora_data.humidity[my_position] = humidity_current;
lora_data.pressure[my_position] = pressure_current;
}
if (my_position == 0) {
timer_pos0 = millis();
}
ShowLORAData(firstTime);
my_position++;
// Should we send the Data?
// we send data the first time the system is started, when the array is full
// or when the weight has fallen more than 100g or the first measurement is
// more than one hour old (which should not happen :-) )
if (firstTime || (my_position >= MAX_VALUES_TO_SEND) || ((weight_last - weight_current) > 20) || ((millis() - timer_pos0) > 3600000)) {
lora_data.offset_last_reading = (uint8_t)((millis() - timer_pos0) / 1000 / 60);
gCatena.SafePrintf("startSendingUplink()\n");
startSendingUplink(firstTime);
} else {
doLightSleep(&sensorJob);
}
}
void startSendingUplink(bool firstTime)
{
LedPattern savedLed = gLed.Set(LedPattern::Measuring);
if (savedLed != LedPattern::Joining)
gLed.Set(LedPattern::Sending);
else
gLed.Set(LedPattern::Joining);
bool fConfirmed = false;
if (gCatena.GetOperatingFlags() & (1 << 16)) {
gCatena.SafePrintf("requesting confirmed tx\n");
fConfirmed = true;
}
if (firstTime) {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed);
} else {
gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed);
}
ClearLoraData();
}
static void sendBufferDoneCb(
void* pContext,
bool fStatus)
{
osjobcb_t pFn;
gLed.Set(LedPattern::Settling);
if (!fStatus) {
gCatena.SafePrintf("send buffer failed\n");
pFn = txFailedDoneCb;
}
else {
pFn = settleDoneCb;
}
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_SETTLE),
pFn);
}
static void txFailedDoneCb(
osjob_t* pSendJob)
{
gCatena.SafePrintf("not provisioned, idling\n");
gLoRaWAN.Shutdown();
gLed.Set(LedPattern::NotProvisioned);
}
static void settleDoneCb(
osjob_t* pSendJob)
{
const bool fDeepSleep = checkDeepSleep();
if (!g_fPrintedSleeping)
doSleepAlert(fDeepSleep);
if (fDeepSleep)
doDeepSleep(pSendJob);
else
doLightSleep(pSendJob);
}
bool checkDeepSleep(void)
{
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
bool fDeepSleep;
if (fDeepSleepTest) {
fDeepSleep = true;
}
#ifdef USBCON
else if (Serial.dtr()) {
fDeepSleep = false;
}
#endif
else if (gCatena.GetOperatingFlags() & (1 << 17)) {
fDeepSleep = false;
}
else if ((gCatena.GetOperatingFlags() & static_cast<uint32_t>(gCatena.OPERATING_FLAGS::fUnattended)) != 0) {
fDeepSleep = true;
}
else {
fDeepSleep = false;
}
return fDeepSleep;
}
void doSleepAlert(const bool fDeepSleep)
{
g_fPrintedSleeping = true;
if (fDeepSleep) {
bool const fDeepSleepTest = gCatena.GetOperatingFlags() & (1 << 19);
const uint32_t deepSleepDelay = fDeepSleepTest ? 10 : 30;
gCatena.SafePrintf("using deep sleep in %u secs"
#ifdef USBCON
" (USB will disconnect while asleep)"
#endif
": ",
deepSleepDelay);
// sleep and print
gLed.Set(LedPattern::TwoShort);
for (auto n = deepSleepDelay; n > 0; --n) {
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 1000) {
gCatena.poll();
yield();
}
gCatena.SafePrintf(".");
}
gCatena.SafePrintf("\nStarting deep sleep.\n");
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100) {
gCatena.poll();
yield();
}
}
else
gCatena.SafePrintf("using light sleep\n");
}
void doDeepSleep(osjob_t* pJob)
{
/* ok... now it's time for a deep sleep */
gLed.Set(LedPattern::Off);
Serial.end();
Wire.end();
SPI.end();
if (fFlash)
gSPI2.end();
gCatena.Sleep(CATCFG_T_INTERVAL);
/* and now... we're awake again. trigger another measurement */
Serial.begin();
Wire.begin();
SPI.begin();
if (fFlash)
gSPI2.begin();
sleepDoneCb(pJob);
}
void doLightSleep(osjob_t* pJob)
{
gLed.Set(LedPattern::Sleeping);
os_setTimedCallback(
pJob,
os_getTime() + sec2osticks(CATCFG_T_INTERVAL),
sleepDoneCb);
}
static void sleepDoneCb(osjob_t* pJob)
{
gLed.Set(LedPattern::WarmingUp);
os_setTimedCallback(
&sensorJob,
os_getTime() + sec2osticks(CATCFG_T_WARMUP),
warmupDoneCb);
}
static void warmupDoneCb(osjob_t* pJob)
{
ReadSensors(false);
}

Binary file not shown.

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
viewBox="0 0 33.866666 33.866668"
version="1.1"
id="svg96"
inkscape:version="0.92.4 (unknown)"
sodipodi:docname="bee-logo-128-cutout.svg">
<defs
id="defs90" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="6.4296875"
inkscape:cx="64"
inkscape:cy="63.780818"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1369"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="1" />
<metadata
id="metadata93">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-263.13332)">
<path
style="opacity:1;fill:#483737;fill-opacity:1;stroke:#483737;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
d="M 64.605469,3.0644531 C 61.008105,3.0003949 57.29972,4.8041766 50.607422,8.6679688 L 36.943359,16.558594 23.277344,24.447266 C 10.780426,31.662384 9.6132812,32.661981 9.6132812,48.117188 v 15.779296 15.777344 c 1e-7,14.430161 0.2794604,15.942338 13.6640628,23.669922 l 13.666015,7.88867 13.664063,7.89063 c 12.49694,7.21511 13.945505,7.72762 27.330078,0 l 13.666016,-7.89063 13.664064,-7.88867 c 12.49694,-7.21508 13.66601,-8.214716 13.66601,-23.669922 V 63.896484 48.117188 c 0,-14.430237 -0.27948,-15.942338 -13.66406,-23.669922 L 91.603516,16.558594 77.9375,8.6679688 C 71.689043,5.0604097 68.202833,3.1285114 64.605469,3.0644531 Z M 63.529297,35.904297 c 11.421218,0 22.336405,2.321093 32.257812,6.517578 a 7.5378873,7.5378873 0 1 1 -5.86914,13.876953 C 81.811305,52.86989 72.913834,50.976562 63.529297,50.976562 c -9.384397,0 -18.283969,1.893328 -26.390625,5.322266 A 7.5378873,7.5378873 0 1 1 31.261719,42.421875 c 9.921402,-4.196485 20.846473,-6.517578 32.267578,-6.517578 z m 0,20.912109 c 8.53426,0 16.703271,1.734716 24.123047,4.873047 a 7.5378874,7.5378874 0 1 1 -5.875,13.876953 c -5.605111,-2.370784 -11.750487,-3.675781 -18.248047,-3.675781 -6.497443,0 -12.650715,1.304997 -18.255859,3.675781 A 7.5378874,7.5378874 0 1 1 39.404297,61.689453 c 7.419787,-3.138331 15.590827,-4.873047 24.125,-4.873047 z m 0,20.935547 c 5.643583,0 11.059704,1.145794 15.974609,3.224609 a 7.5395547,7.5395547 0 1 1 -5.876953,13.886719 c -3.100354,-1.311345 -6.490823,-2.037109 -10.097656,-2.037109 -3.606679,0 -7.006909,0.725727 -10.107422,2.037109 A 7.5378875,7.5378875 0 1 1 47.554688,80.976562 c 4.914946,-2.078777 10.331124,-3.224609 15.974609,-3.224609 z"
transform="matrix(0.26458333,0,0,0.26458333,0,263.13332)"
id="path815"
inkscape:connector-curvature="0" />
<path
style="fill:#fc0ff0;fill-opacity:0;stroke:#483737;stroke-width:2.90305877;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10.674714,268.54246 8.8603022,264.91363"
id="path899"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#483737;stroke-width:2.90305877;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 23.375595,268.54246 1.814411,-3.62883"
id="path899-8"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="83.081947mm"
height="52.319721mm"
viewBox="0 0 83.081947 52.319721"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (unknown)"
sodipodi:docname="mini-beieli-praegung-text-as-path.svg"
inkscape:export-filename="/home/joerg/bee-logo-box.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0"
refX="0"
id="DotL"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path978"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,5.92,0.8)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.1106813"
inkscape:cx="3.5729304"
inkscape:cy="42.625215"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="3840"
inkscape:window-height="2089"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="1"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10">
<inkscape:grid
type="xygrid"
id="grid895"
spacingx="1"
spacingy="1"
originx="15.558254"
originy="-258.4087" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(15.558254,13.728425)">
<path
style="opacity:1;fill:#483737;fill-opacity:1;stroke:#483737;stroke-width:0.42512852;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
d="m 8.7393227,11.644791 -1.0589324,0.611374 -1.0589359,0.611374 c -1.0371974,0.598824 -1.1494606,0.559109 -2.1178708,0 L 3.4446478,12.256165 2.3857128,11.644791 C 1.3485151,11.045971 1.3267775,10.928889 1.3267775,9.8106715 V 8.5879237 7.3651789 c 0,-1.1976465 0.090525,-1.2750137 1.0589353,-1.8341225 L 3.4446478,4.9196842 4.5035836,4.3083089 c 1.0371978,-0.59882 1.1494596,-0.559106 2.1178708,0 l 1.0589359,0.6113753 1.0589324,0.6113722 c 1.0371922,0.5988236 1.058929,0.7159034 1.058929,1.8341225 v 1.2227448 1.2227478 c 0,1.1976435 -0.090524,1.2750145 -1.058929,1.8341195 z"
id="path815"
inkscape:connector-curvature="0"
sodipodi:nodetypes="scsscsscsscsscsscss"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 3.231964,7.4616379 c 0.6985148,-0.295449 1.4664939,-0.4588256 2.272625,-0.4588256 0.8061387,0 1.5741179,0.1633766 2.2726329,0.4588256"
id="path846-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 3.8626628,8.9546319 c 0.5046637,-0.2134551 1.0595109,-0.331491 1.6419262,-0.331491 0.5824224,0 1.1372702,0.1180359 1.6419342,0.331491"
id="path846-1-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 4.494172,10.44954 c 0.3105661,-0.131357 0.6520092,-0.203993 1.010417,-0.203993 0.3584154,0 0.699858,0.07264 1.0104178,0.203993"
id="path846-8-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="fill:#fc0ff0;fill-opacity:0;stroke:#483737;stroke-width:0.85025704;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 3.7083008,5.2207965 3.1768908,4.1579827"
id="path899"
inkscape:connector-curvature="0"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="fill:none;stroke:#483737;stroke-width:0.85025704;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 7.4281735,5.2207965 7.9595827,4.1579827"
id="path899-8"
inkscape:connector-curvature="0"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<g
aria-label="mini-beieli.ch"
style="font-style:normal;font-weight:normal;font-size:4.44661283px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.11116531"
id="text28">
<path
d="M 14.177507,10.866552 14.6736,6.7572427 h 0.533301 q 0.165365,0 0.260449,0.082682 0.09509,0.078548 0.09095,0.2563151 l -0.02067,0.434082 q 0.297657,-0.4216797 0.644922,-0.6242513 0.347266,-0.2067057 0.731738,-0.2067057 0.409278,0 0.644922,0.2563151 0.235645,0.2521809 0.272852,0.7317382 0.305924,-0.5084961 0.682129,-0.7482747 0.376204,-0.2397786 0.822688,-0.2397786 0.28112,0 0.49196,0.1033528 0.21084,0.1033529 0.343132,0.3017904 0.136425,0.1984375 0.181901,0.4878255 0.04961,0.289388 0.0041,0.6614583 l -0.305925,2.6127599 h -1.033528 l 0.305924,-2.6127599 q 0.04961,-0.3803385 -0.03307,-0.5374349 -0.07855,-0.1612305 -0.326595,-0.1612305 -0.16123,0 -0.322461,0.082682 -0.157096,0.078548 -0.297656,0.2315104 -0.14056,0.1529623 -0.260449,0.3762044 -0.119889,0.2191081 -0.206706,0.4960938 v 0.00413 l -0.243913,2.1208003 h -1.037662 l 0.305924,-2.6127599 q 0.04961,-0.3762044 -0.02481,-0.5374349 -0.07441,-0.1612305 -0.32246,-0.1612305 -0.173633,0 -0.338998,0.086816 -0.16123,0.086816 -0.30179,0.252181 -0.14056,0.1612305 -0.260449,0.3927409 -0.11989,0.2273763 -0.206706,0.5126302 l -0.239779,2.0670568 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path873" />
<path
d="m 22.565625,6.7613768 -0.49196,4.1051752 h -1.033528 l 0.491959,-4.1051752 z m 0.248047,-1.1782226 q 0,0.1322917 -0.05788,0.252181 -0.05374,0.1157552 -0.148828,0.2067057 -0.09095,0.086816 -0.21084,0.1405599 -0.115755,0.049609 -0.239778,0.049609 -0.11989,0 -0.235645,-0.049609 -0.111621,-0.053744 -0.198437,-0.1405599 -0.08682,-0.09095 -0.14056,-0.2067057 -0.05374,-0.1198893 -0.05374,-0.252181 0,-0.1322917 0.05374,-0.252181 0.05374,-0.1198893 0.14056,-0.2067057 0.09095,-0.09095 0.202571,-0.144694 0.115756,-0.053743 0.239779,-0.053743 0.124023,0 0.239779,0.053743 0.119889,0.049609 0.210839,0.1405599 0.09095,0.09095 0.144694,0.2108398 0.05374,0.1157552 0.05374,0.252181 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path875" />
<path
d="m 23.05345,10.866552 0.49196,-4.1093093 h 0.533301 q 0.169498,0 0.260449,0.082682 0.09508,0.078548 0.08682,0.2563151 l -0.02067,0.4919596 q 0.322461,-0.4506185 0.711067,-0.6697266 0.388607,-0.219108 0.802019,-0.219108 0.264583,0 0.475423,0.1033528 0.214974,0.1033529 0.351399,0.3017904 0.14056,0.1984375 0.194304,0.4878255 0.05788,0.289388 0.0124,0.6614583 l -0.305924,2.6127599 h -1.050065 l 0.310058,-2.6127599 q 0.04547,-0.3803385 -0.04961,-0.5374349 -0.09095,-0.1612305 -0.338998,-0.1612305 -0.169498,0 -0.347265,0.095085 -0.177767,0.095085 -0.343132,0.2687174 -0.16123,0.1736328 -0.297656,0.4216797 -0.132292,0.2439127 -0.21084,0.5457031 l -0.214974,1.9802404 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path877" />
<path
d="m 29.114062,6.7613768 -0.491959,4.1051752 h -1.033529 l 0.49196,-4.1051752 z m 0.248047,-1.1782226 q 0,0.1322917 -0.05788,0.252181 -0.05374,0.1157552 -0.148828,0.2067057 -0.09095,0.086816 -0.21084,0.1405599 -0.115755,0.049609 -0.239778,0.049609 -0.119889,0 -0.235645,-0.049609 -0.111621,-0.053744 -0.198437,-0.1405599 -0.08682,-0.09095 -0.14056,-0.2067057 -0.05374,-0.1198893 -0.05374,-0.252181 0,-0.1322917 0.05374,-0.252181 0.05374,-0.1198893 0.14056,-0.2067057 0.09095,-0.09095 0.202571,-0.144694 0.115756,-0.053743 0.239779,-0.053743 0.124023,0 0.239779,0.053743 0.119889,0.049609 0.21084,0.1405599 0.09095,0.09095 0.144694,0.2108398 0.05374,0.1157552 0.05374,0.252181 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path879" />
<path
d="m 29.833398,8.034684 h 1.934765 l -0.107487,0.8433593 h -1.930631 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path881" />
<path
d="M 32.235318,10.866552 32.95052,4.92583 h 1.029394 l -0.32246,2.6251626 q 0.297656,-0.3927408 0.657324,-0.6201171 0.363802,-0.2315104 0.773079,-0.2315104 0.268718,0 0.483692,0.095085 0.219108,0.09095 0.37207,0.2811198 0.157096,0.1901692 0.239778,0.4836914 0.08682,0.289388 0.08682,0.6862629 0,0.5250326 -0.165365,1.0128581 -0.16123,0.4836913 -0.44235,0.8557621 -0.28112,0.37207 -0.657324,0.595312 -0.37207,0.219108 -0.79375,0.219108 -0.326595,0 -0.58291,-0.124023 -0.252181,-0.124024 -0.425814,-0.334864 l -0.03307,0.111621 q -0.04548,0.144694 -0.119889,0.214974 -0.07441,0.07028 -0.248047,0.07028 z m 2.447396,-3.3486323 q -0.165365,0 -0.338997,0.1116211 -0.169499,0.1116211 -0.33073,0.3059245 -0.157096,0.1901692 -0.293522,0.4506184 -0.136425,0.2604492 -0.23151,0.5581055 L 33.3722,9.8909014 q 0.14056,0.1322916 0.318326,0.1901696 0.177767,0.05374 0.3514,0.05374 0.264584,0 0.483692,-0.1570962 0.219108,-0.1570964 0.37207,-0.4092774 0.157096,-0.256315 0.239779,-0.5746419 0.08682,-0.3183268 0.08682,-0.6449218 0,-0.4258138 -0.144694,-0.6283854 -0.14056,-0.2025716 -0.396875,-0.2025716 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path883" />
<path
d="m 40.441535,7.7494301 q 0,0.2728516 -0.124023,0.5002279 -0.119889,0.2232421 -0.429948,0.4051432 -0.310058,0.1777669 -0.847493,0.3059244 -0.533301,0.1281576 -1.35599,0.2025716 0,0.4878255 0.227376,0.72347 0.231511,0.2315108 0.7028,0.2315108 0.297656,0 0.487825,-0.06615 0.194304,-0.066146 0.33073,-0.1446942 0.136425,-0.082682 0.239778,-0.1488281 0.107487,-0.066146 0.235645,-0.066146 0.111621,0 0.190169,0.09095 l 0.272852,0.3224606 q -0.231511,0.214974 -0.450619,0.372071 -0.219108,0.152962 -0.454753,0.256315 -0.23151,0.09922 -0.491959,0.144694 -0.256315,0.04961 -0.558106,0.04961 -0.396875,0 -0.72347,-0.124023 -0.32246,-0.124024 -0.553971,-0.3514 -0.227376,-0.23151 -0.355534,-0.5498373 -0.124023,-0.3224609 -0.124023,-0.7193359 0,-0.4836914 0.165364,-0.9301757 0.169499,-0.4506185 0.475424,-0.79375 0.310058,-0.3431315 0.74414,-0.5498372 0.438216,-0.2067057 0.979785,-0.2067057 0.355534,0 0.620117,0.095085 0.268718,0.09095 0.442351,0.2439128 0.177767,0.1488281 0.264583,0.3348632 0.09095,0.1860352 0.09095,0.3720703 z m -1.46761,-0.3183268 q -0.223242,0 -0.417546,0.078548 -0.190169,0.074414 -0.343131,0.2149739 -0.152962,0.1364258 -0.264583,0.3348633 -0.111622,0.1943034 -0.177767,0.4299479 0.553971,-0.066146 0.892968,-0.1405599 0.338998,-0.078548 0.525033,-0.1653646 0.186035,-0.09095 0.248047,-0.1901692 0.06201,-0.1033529 0.06201,-0.2191081 0,-0.062012 -0.02894,-0.1198893 -0.02894,-0.062012 -0.09508,-0.1116211 -0.06201,-0.049609 -0.16123,-0.078548 -0.09922,-0.033073 -0.239779,-0.033073 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path885" />
<path
d="m 42.42591,6.7613768 -0.49196,4.1051752 h -1.033528 l 0.491959,-4.1051752 z m 0.248047,-1.1782226 q 0,0.1322917 -0.05788,0.252181 -0.05374,0.1157552 -0.148828,0.2067057 -0.09095,0.086816 -0.21084,0.1405599 -0.115755,0.049609 -0.239778,0.049609 -0.11989,0 -0.235645,-0.049609 -0.111621,-0.053744 -0.198437,-0.1405599 -0.08682,-0.09095 -0.14056,-0.2067057 -0.05374,-0.1198893 -0.05374,-0.252181 0,-0.1322917 0.05374,-0.252181 0.05374,-0.1198893 0.14056,-0.2067057 0.09095,-0.09095 0.202571,-0.144694 0.115755,-0.053743 0.239779,-0.053743 0.124023,0 0.239778,0.053743 0.11989,0.049609 0.21084,0.1405599 0.09095,0.09095 0.144694,0.2108398 0.05374,0.1157552 0.05374,0.252181 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path887" />
<path
d="m 46.692317,7.7494301 q 0,0.2728516 -0.124023,0.5002279 -0.119889,0.2232421 -0.429948,0.4051432 -0.310058,0.1777669 -0.847493,0.3059244 -0.533301,0.1281576 -1.35599,0.2025716 0,0.4878255 0.227377,0.72347 0.23151,0.2315108 0.702799,0.2315108 0.297656,0 0.487825,-0.06615 0.194304,-0.066146 0.33073,-0.1446942 0.136425,-0.082682 0.239778,-0.1488281 0.107487,-0.066146 0.235645,-0.066146 0.111621,0 0.190169,0.09095 l 0.272852,0.3224606 q -0.231511,0.214974 -0.450619,0.372071 -0.219108,0.152962 -0.454752,0.256315 -0.231511,0.09922 -0.49196,0.144694 -0.256315,0.04961 -0.558106,0.04961 -0.396875,0 -0.72347,-0.124023 -0.32246,-0.124024 -0.553971,-0.3514 -0.227376,-0.23151 -0.355534,-0.5498373 -0.124023,-0.3224609 -0.124023,-0.7193359 0,-0.4836914 0.165364,-0.9301757 0.169499,-0.4506185 0.475424,-0.79375 0.310058,-0.3431315 0.74414,-0.5498372 0.438216,-0.2067057 0.979785,-0.2067057 0.355534,0 0.620117,0.095085 0.268718,0.09095 0.442351,0.2439128 0.177767,0.1488281 0.264583,0.3348632 0.09095,0.1860352 0.09095,0.3720703 z m -1.46761,-0.3183268 q -0.223242,0 -0.417546,0.078548 -0.190169,0.074414 -0.343131,0.2149739 -0.152962,0.1364258 -0.264583,0.3348633 -0.111622,0.1943034 -0.177767,0.4299479 0.553971,-0.066146 0.892968,-0.1405599 0.338998,-0.078548 0.525033,-0.1653646 0.186035,-0.09095 0.248047,-0.1901692 0.06201,-0.1033529 0.06201,-0.2191081 0,-0.062012 -0.02894,-0.1198893 -0.02894,-0.062012 -0.09509,-0.1116211 -0.06201,-0.049609 -0.16123,-0.078548 -0.09922,-0.033073 -0.239779,-0.033073 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path889" />
<path
d="M 47.134667,10.866552 47.849869,4.92583 h 1.037663 l -0.719336,5.940722 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path891" />
<path
d="m 50.760287,6.7613768 -0.491959,4.1051752 h -1.033529 l 0.49196,-4.1051752 z m 0.248047,-1.1782226 q 0,0.1322917 -0.05788,0.252181 -0.05374,0.1157552 -0.148828,0.2067057 -0.09095,0.086816 -0.21084,0.1405599 -0.115755,0.049609 -0.239778,0.049609 -0.11989,0 -0.235645,-0.049609 -0.111621,-0.053744 -0.198437,-0.1405599 -0.08682,-0.09095 -0.14056,-0.2067057 -0.05374,-0.1198893 -0.05374,-0.252181 0,-0.1322917 0.05374,-0.252181 0.05374,-0.1198893 0.14056,-0.2067057 0.09095,-0.09095 0.202571,-0.144694 0.115756,-0.053743 0.239779,-0.053743 0.124023,0 0.239779,0.053743 0.119889,0.049609 0.210839,0.1405599 0.09095,0.09095 0.144695,0.2108398 0.05374,0.1157552 0.05374,0.252181 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path893" />
<path
d="m 51.38867,10.300179 q 0,-0.128158 0.04548,-0.243913 0.04961,-0.1157553 0.132291,-0.1984376 0.08682,-0.082682 0.202572,-0.1322916 0.115755,-0.049609 0.248047,-0.049609 0.132291,0 0.248047,0.049609 0.115755,0.049609 0.198437,0.1322916 0.08682,0.082682 0.136426,0.1984376 0.04961,0.115755 0.04961,0.243913 0,0.132291 -0.04961,0.248047 -0.04961,0.115755 -0.136426,0.198437 -0.08268,0.08268 -0.198437,0.128158 -0.115756,0.04961 -0.248047,0.04961 -0.132292,0 -0.248047,-0.04961 -0.115755,-0.04548 -0.202572,-0.128158 -0.08268,-0.08268 -0.132291,-0.198437 -0.04548,-0.115756 -0.04548,-0.248047 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path895" />
<path
d="m 56.672069,10.138948 q -0.202571,0.223242 -0.388607,0.376205 -0.181901,0.148828 -0.376204,0.243912 -0.194303,0.09095 -0.409277,0.128158 -0.21084,0.04134 -0.471289,0.04134 -0.359668,0 -0.644922,-0.119889 -0.28112,-0.124024 -0.479558,-0.347266 -0.194303,-0.227376 -0.30179,-0.5415688 -0.103353,-0.3183268 -0.103353,-0.7110677 0,-0.3348633 0.07028,-0.649056 0.07441,-0.3183268 0.21084,-0.5953124 0.136426,-0.2769857 0.330729,-0.5084961 0.194304,-0.2356445 0.434082,-0.4010091 0.243913,-0.1653646 0.525033,-0.2563151 0.285254,-0.095085 0.599446,-0.095085 0.413412,0 0.7028,0.144694 0.289388,0.1405599 0.51263,0.434082 l -0.338997,0.3927409 q -0.03307,0.045475 -0.08268,0.074414 -0.04961,0.028939 -0.107487,0.028939 -0.07028,0 -0.128157,-0.037207 -0.05374,-0.041341 -0.124024,-0.086816 -0.07028,-0.045475 -0.177767,-0.082682 -0.103352,-0.041341 -0.272851,-0.041341 -0.219108,0 -0.417546,0.1198893 -0.198437,0.1198893 -0.351399,0.3389974 -0.152963,0.219108 -0.243913,0.5291666 -0.09095,0.3100586 -0.09095,0.6945312 0,0.4382161 0.19017,0.6738606 0.194303,0.2315108 0.516764,0.2315108 0.223242,0 0.363802,-0.06201 0.14056,-0.062012 0.239779,-0.1364258 0.09922,-0.074414 0.177767,-0.1364258 0.08268,-0.062012 0.181901,-0.062012 0.05374,0 0.09922,0.024805 0.04961,0.024805 0.08682,0.07028 l 0.268717,0.3224607 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path897" />
<path
d="M 57.035872,10.866552 57.751073,4.92583 h 1.050065 l -0.305924,2.5052733 q 0.30179,-0.3720703 0.65319,-0.5498372 0.355534,-0.181901 0.727604,-0.181901 0.264584,0 0.475423,0.1033528 0.214974,0.1033529 0.3514,0.3017904 0.14056,0.1984375 0.194303,0.4878255 0.05788,0.289388 0.0124,0.6614583 l -0.310059,2.6127599 h -1.045931 l 0.305925,-2.6127599 q 0.04961,-0.3803385 -0.04134,-0.541569 -0.09095,-0.1653646 -0.338998,-0.1653646 -0.152962,0 -0.318327,0.086816 -0.16123,0.082682 -0.314192,0.2397787 -0.152963,0.1529622 -0.285254,0.3720702 -0.132292,0.214974 -0.223242,0.4836914 l -0.256315,2.1373368 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path900" />
</g>
<flowRoot
xml:space="preserve"
id="flowRoot844"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><flowRegion
id="flowRegion846"><rect
id="rect848"
width="718.11023"
height="548.03149"
x="37.795277"
y="404.40945" /></flowRegion><flowPara
id="flowPara850" /></flowRoot> </g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="83.081947mm"
height="52.319721mm"
viewBox="0 0 83.081947 52.319721"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (unknown)"
sodipodi:docname="mini-beieli-praegung-text.as-path.svg"
inkscape:export-filename="/home/joerg/bee-logo-box.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0"
refX="0"
id="DotL"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path978"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,5.92,0.8)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.1106813"
inkscape:cx="3.5729304"
inkscape:cy="42.625215"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="3840"
inkscape:window-height="2089"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="1"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10">
<inkscape:grid
type="xygrid"
id="grid895"
spacingx="1"
spacingy="1"
originx="15.558254"
originy="-258.4087" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(15.558254,13.728425)">
<path
style="opacity:1;fill:#483737;fill-opacity:1;stroke:#483737;stroke-width:0.42512852;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
d="m 8.7393227,11.644791 -1.0589324,0.611374 -1.0589359,0.611374 c -1.0371974,0.598824 -1.1494606,0.559109 -2.1178708,0 L 3.4446478,12.256165 2.3857128,11.644791 C 1.3485151,11.045971 1.3267775,10.928889 1.3267775,9.8106715 V 8.5879237 7.3651789 c 0,-1.1976465 0.090525,-1.2750137 1.0589353,-1.8341225 L 3.4446478,4.9196842 4.5035836,4.3083089 c 1.0371978,-0.59882 1.1494596,-0.559106 2.1178708,0 l 1.0589359,0.6113753 1.0589324,0.6113722 c 1.0371922,0.5988236 1.058929,0.7159034 1.058929,1.8341225 v 1.2227448 1.2227478 c 0,1.1976435 -0.090524,1.2750145 -1.058929,1.8341195 z"
id="path815"
inkscape:connector-curvature="0"
sodipodi:nodetypes="scsscsscsscsscsscss"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 3.231964,7.4616379 c 0.6985148,-0.295449 1.4664939,-0.4588256 2.272625,-0.4588256 0.8061387,0 1.5741179,0.1633766 2.2726329,0.4588256"
id="path846-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 3.8626628,8.9546319 c 0.5046637,-0.2134551 1.0595109,-0.331491 1.6419262,-0.331491 0.5824224,0 1.1372702,0.1180359 1.6419342,0.331491"
id="path846-1-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 4.494172,10.44954 c 0.3105661,-0.131357 0.6520092,-0.203993 1.010417,-0.203993 0.3584154,0 0.699858,0.07264 1.0104178,0.203993"
id="path846-8-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="fill:#fc0ff0;fill-opacity:0;stroke:#483737;stroke-width:0.85025704;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 3.7083008,5.2207965 3.1768908,4.1579827"
id="path899"
inkscape:connector-curvature="0"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="fill:none;stroke:#483737;stroke-width:0.85025704;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 7.4281735,5.2207965 7.9595827,4.1579827"
id="path899-8"
inkscape:connector-curvature="0"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<g
aria-label="mini-beieli.ch"
style="font-style:normal;font-weight:normal;font-size:4.44661283px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.11116531"
id="text28">
<path
d="M 14.177507,10.866552 14.6736,6.7572427 h 0.533301 q 0.165365,0 0.260449,0.082682 0.09509,0.078548 0.09095,0.2563151 l -0.02067,0.434082 q 0.297657,-0.4216797 0.644922,-0.6242513 0.347266,-0.2067057 0.731738,-0.2067057 0.409278,0 0.644922,0.2563151 0.235645,0.2521809 0.272852,0.7317382 0.305924,-0.5084961 0.682129,-0.7482747 0.376204,-0.2397786 0.822688,-0.2397786 0.28112,0 0.49196,0.1033528 0.21084,0.1033529 0.343132,0.3017904 0.136425,0.1984375 0.181901,0.4878255 0.04961,0.289388 0.0041,0.6614583 l -0.305925,2.6127599 h -1.033528 l 0.305924,-2.6127599 q 0.04961,-0.3803385 -0.03307,-0.5374349 -0.07855,-0.1612305 -0.326595,-0.1612305 -0.16123,0 -0.322461,0.082682 -0.157096,0.078548 -0.297656,0.2315104 -0.14056,0.1529623 -0.260449,0.3762044 -0.119889,0.2191081 -0.206706,0.4960938 v 0.00413 l -0.243913,2.1208003 h -1.037662 l 0.305924,-2.6127599 q 0.04961,-0.3762044 -0.02481,-0.5374349 -0.07441,-0.1612305 -0.32246,-0.1612305 -0.173633,0 -0.338998,0.086816 -0.16123,0.086816 -0.30179,0.252181 -0.14056,0.1612305 -0.260449,0.3927409 -0.11989,0.2273763 -0.206706,0.5126302 l -0.239779,2.0670568 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path873" />
<path
d="m 22.565625,6.7613768 -0.49196,4.1051752 h -1.033528 l 0.491959,-4.1051752 z m 0.248047,-1.1782226 q 0,0.1322917 -0.05788,0.252181 -0.05374,0.1157552 -0.148828,0.2067057 -0.09095,0.086816 -0.21084,0.1405599 -0.115755,0.049609 -0.239778,0.049609 -0.11989,0 -0.235645,-0.049609 -0.111621,-0.053744 -0.198437,-0.1405599 -0.08682,-0.09095 -0.14056,-0.2067057 -0.05374,-0.1198893 -0.05374,-0.252181 0,-0.1322917 0.05374,-0.252181 0.05374,-0.1198893 0.14056,-0.2067057 0.09095,-0.09095 0.202571,-0.144694 0.115756,-0.053743 0.239779,-0.053743 0.124023,0 0.239779,0.053743 0.119889,0.049609 0.210839,0.1405599 0.09095,0.09095 0.144694,0.2108398 0.05374,0.1157552 0.05374,0.252181 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path875" />
<path
d="m 23.05345,10.866552 0.49196,-4.1093093 h 0.533301 q 0.169498,0 0.260449,0.082682 0.09508,0.078548 0.08682,0.2563151 l -0.02067,0.4919596 q 0.322461,-0.4506185 0.711067,-0.6697266 0.388607,-0.219108 0.802019,-0.219108 0.264583,0 0.475423,0.1033528 0.214974,0.1033529 0.351399,0.3017904 0.14056,0.1984375 0.194304,0.4878255 0.05788,0.289388 0.0124,0.6614583 l -0.305924,2.6127599 h -1.050065 l 0.310058,-2.6127599 q 0.04547,-0.3803385 -0.04961,-0.5374349 -0.09095,-0.1612305 -0.338998,-0.1612305 -0.169498,0 -0.347265,0.095085 -0.177767,0.095085 -0.343132,0.2687174 -0.16123,0.1736328 -0.297656,0.4216797 -0.132292,0.2439127 -0.21084,0.5457031 l -0.214974,1.9802404 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path877" />
<path
d="m 29.114062,6.7613768 -0.491959,4.1051752 h -1.033529 l 0.49196,-4.1051752 z m 0.248047,-1.1782226 q 0,0.1322917 -0.05788,0.252181 -0.05374,0.1157552 -0.148828,0.2067057 -0.09095,0.086816 -0.21084,0.1405599 -0.115755,0.049609 -0.239778,0.049609 -0.119889,0 -0.235645,-0.049609 -0.111621,-0.053744 -0.198437,-0.1405599 -0.08682,-0.09095 -0.14056,-0.2067057 -0.05374,-0.1198893 -0.05374,-0.252181 0,-0.1322917 0.05374,-0.252181 0.05374,-0.1198893 0.14056,-0.2067057 0.09095,-0.09095 0.202571,-0.144694 0.115756,-0.053743 0.239779,-0.053743 0.124023,0 0.239779,0.053743 0.119889,0.049609 0.21084,0.1405599 0.09095,0.09095 0.144694,0.2108398 0.05374,0.1157552 0.05374,0.252181 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path879" />
<path
d="m 29.833398,8.034684 h 1.934765 l -0.107487,0.8433593 h -1.930631 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path881" />
<path
d="M 32.235318,10.866552 32.95052,4.92583 h 1.029394 l -0.32246,2.6251626 q 0.297656,-0.3927408 0.657324,-0.6201171 0.363802,-0.2315104 0.773079,-0.2315104 0.268718,0 0.483692,0.095085 0.219108,0.09095 0.37207,0.2811198 0.157096,0.1901692 0.239778,0.4836914 0.08682,0.289388 0.08682,0.6862629 0,0.5250326 -0.165365,1.0128581 -0.16123,0.4836913 -0.44235,0.8557621 -0.28112,0.37207 -0.657324,0.595312 -0.37207,0.219108 -0.79375,0.219108 -0.326595,0 -0.58291,-0.124023 -0.252181,-0.124024 -0.425814,-0.334864 l -0.03307,0.111621 q -0.04548,0.144694 -0.119889,0.214974 -0.07441,0.07028 -0.248047,0.07028 z m 2.447396,-3.3486323 q -0.165365,0 -0.338997,0.1116211 -0.169499,0.1116211 -0.33073,0.3059245 -0.157096,0.1901692 -0.293522,0.4506184 -0.136425,0.2604492 -0.23151,0.5581055 L 33.3722,9.8909014 q 0.14056,0.1322916 0.318326,0.1901696 0.177767,0.05374 0.3514,0.05374 0.264584,0 0.483692,-0.1570962 0.219108,-0.1570964 0.37207,-0.4092774 0.157096,-0.256315 0.239779,-0.5746419 0.08682,-0.3183268 0.08682,-0.6449218 0,-0.4258138 -0.144694,-0.6283854 -0.14056,-0.2025716 -0.396875,-0.2025716 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path883" />
<path
d="m 40.441535,7.7494301 q 0,0.2728516 -0.124023,0.5002279 -0.119889,0.2232421 -0.429948,0.4051432 -0.310058,0.1777669 -0.847493,0.3059244 -0.533301,0.1281576 -1.35599,0.2025716 0,0.4878255 0.227376,0.72347 0.231511,0.2315108 0.7028,0.2315108 0.297656,0 0.487825,-0.06615 0.194304,-0.066146 0.33073,-0.1446942 0.136425,-0.082682 0.239778,-0.1488281 0.107487,-0.066146 0.235645,-0.066146 0.111621,0 0.190169,0.09095 l 0.272852,0.3224606 q -0.231511,0.214974 -0.450619,0.372071 -0.219108,0.152962 -0.454753,0.256315 -0.23151,0.09922 -0.491959,0.144694 -0.256315,0.04961 -0.558106,0.04961 -0.396875,0 -0.72347,-0.124023 -0.32246,-0.124024 -0.553971,-0.3514 -0.227376,-0.23151 -0.355534,-0.5498373 -0.124023,-0.3224609 -0.124023,-0.7193359 0,-0.4836914 0.165364,-0.9301757 0.169499,-0.4506185 0.475424,-0.79375 0.310058,-0.3431315 0.74414,-0.5498372 0.438216,-0.2067057 0.979785,-0.2067057 0.355534,0 0.620117,0.095085 0.268718,0.09095 0.442351,0.2439128 0.177767,0.1488281 0.264583,0.3348632 0.09095,0.1860352 0.09095,0.3720703 z m -1.46761,-0.3183268 q -0.223242,0 -0.417546,0.078548 -0.190169,0.074414 -0.343131,0.2149739 -0.152962,0.1364258 -0.264583,0.3348633 -0.111622,0.1943034 -0.177767,0.4299479 0.553971,-0.066146 0.892968,-0.1405599 0.338998,-0.078548 0.525033,-0.1653646 0.186035,-0.09095 0.248047,-0.1901692 0.06201,-0.1033529 0.06201,-0.2191081 0,-0.062012 -0.02894,-0.1198893 -0.02894,-0.062012 -0.09508,-0.1116211 -0.06201,-0.049609 -0.16123,-0.078548 -0.09922,-0.033073 -0.239779,-0.033073 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path885" />
<path
d="m 42.42591,6.7613768 -0.49196,4.1051752 h -1.033528 l 0.491959,-4.1051752 z m 0.248047,-1.1782226 q 0,0.1322917 -0.05788,0.252181 -0.05374,0.1157552 -0.148828,0.2067057 -0.09095,0.086816 -0.21084,0.1405599 -0.115755,0.049609 -0.239778,0.049609 -0.11989,0 -0.235645,-0.049609 -0.111621,-0.053744 -0.198437,-0.1405599 -0.08682,-0.09095 -0.14056,-0.2067057 -0.05374,-0.1198893 -0.05374,-0.252181 0,-0.1322917 0.05374,-0.252181 0.05374,-0.1198893 0.14056,-0.2067057 0.09095,-0.09095 0.202571,-0.144694 0.115755,-0.053743 0.239779,-0.053743 0.124023,0 0.239778,0.053743 0.11989,0.049609 0.21084,0.1405599 0.09095,0.09095 0.144694,0.2108398 0.05374,0.1157552 0.05374,0.252181 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path887" />
<path
d="m 46.692317,7.7494301 q 0,0.2728516 -0.124023,0.5002279 -0.119889,0.2232421 -0.429948,0.4051432 -0.310058,0.1777669 -0.847493,0.3059244 -0.533301,0.1281576 -1.35599,0.2025716 0,0.4878255 0.227377,0.72347 0.23151,0.2315108 0.702799,0.2315108 0.297656,0 0.487825,-0.06615 0.194304,-0.066146 0.33073,-0.1446942 0.136425,-0.082682 0.239778,-0.1488281 0.107487,-0.066146 0.235645,-0.066146 0.111621,0 0.190169,0.09095 l 0.272852,0.3224606 q -0.231511,0.214974 -0.450619,0.372071 -0.219108,0.152962 -0.454752,0.256315 -0.231511,0.09922 -0.49196,0.144694 -0.256315,0.04961 -0.558106,0.04961 -0.396875,0 -0.72347,-0.124023 -0.32246,-0.124024 -0.553971,-0.3514 -0.227376,-0.23151 -0.355534,-0.5498373 -0.124023,-0.3224609 -0.124023,-0.7193359 0,-0.4836914 0.165364,-0.9301757 0.169499,-0.4506185 0.475424,-0.79375 0.310058,-0.3431315 0.74414,-0.5498372 0.438216,-0.2067057 0.979785,-0.2067057 0.355534,0 0.620117,0.095085 0.268718,0.09095 0.442351,0.2439128 0.177767,0.1488281 0.264583,0.3348632 0.09095,0.1860352 0.09095,0.3720703 z m -1.46761,-0.3183268 q -0.223242,0 -0.417546,0.078548 -0.190169,0.074414 -0.343131,0.2149739 -0.152962,0.1364258 -0.264583,0.3348633 -0.111622,0.1943034 -0.177767,0.4299479 0.553971,-0.066146 0.892968,-0.1405599 0.338998,-0.078548 0.525033,-0.1653646 0.186035,-0.09095 0.248047,-0.1901692 0.06201,-0.1033529 0.06201,-0.2191081 0,-0.062012 -0.02894,-0.1198893 -0.02894,-0.062012 -0.09509,-0.1116211 -0.06201,-0.049609 -0.16123,-0.078548 -0.09922,-0.033073 -0.239779,-0.033073 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path889" />
<path
d="M 47.134667,10.866552 47.849869,4.92583 h 1.037663 l -0.719336,5.940722 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path891" />
<path
d="m 50.760287,6.7613768 -0.491959,4.1051752 h -1.033529 l 0.49196,-4.1051752 z m 0.248047,-1.1782226 q 0,0.1322917 -0.05788,0.252181 -0.05374,0.1157552 -0.148828,0.2067057 -0.09095,0.086816 -0.21084,0.1405599 -0.115755,0.049609 -0.239778,0.049609 -0.11989,0 -0.235645,-0.049609 -0.111621,-0.053744 -0.198437,-0.1405599 -0.08682,-0.09095 -0.14056,-0.2067057 -0.05374,-0.1198893 -0.05374,-0.252181 0,-0.1322917 0.05374,-0.252181 0.05374,-0.1198893 0.14056,-0.2067057 0.09095,-0.09095 0.202571,-0.144694 0.115756,-0.053743 0.239779,-0.053743 0.124023,0 0.239779,0.053743 0.119889,0.049609 0.210839,0.1405599 0.09095,0.09095 0.144695,0.2108398 0.05374,0.1157552 0.05374,0.252181 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path893" />
<path
d="m 51.38867,10.300179 q 0,-0.128158 0.04548,-0.243913 0.04961,-0.1157553 0.132291,-0.1984376 0.08682,-0.082682 0.202572,-0.1322916 0.115755,-0.049609 0.248047,-0.049609 0.132291,0 0.248047,0.049609 0.115755,0.049609 0.198437,0.1322916 0.08682,0.082682 0.136426,0.1984376 0.04961,0.115755 0.04961,0.243913 0,0.132291 -0.04961,0.248047 -0.04961,0.115755 -0.136426,0.198437 -0.08268,0.08268 -0.198437,0.128158 -0.115756,0.04961 -0.248047,0.04961 -0.132292,0 -0.248047,-0.04961 -0.115755,-0.04548 -0.202572,-0.128158 -0.08268,-0.08268 -0.132291,-0.198437 -0.04548,-0.115756 -0.04548,-0.248047 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path895" />
<path
d="m 56.672069,10.138948 q -0.202571,0.223242 -0.388607,0.376205 -0.181901,0.148828 -0.376204,0.243912 -0.194303,0.09095 -0.409277,0.128158 -0.21084,0.04134 -0.471289,0.04134 -0.359668,0 -0.644922,-0.119889 -0.28112,-0.124024 -0.479558,-0.347266 -0.194303,-0.227376 -0.30179,-0.5415688 -0.103353,-0.3183268 -0.103353,-0.7110677 0,-0.3348633 0.07028,-0.649056 0.07441,-0.3183268 0.21084,-0.5953124 0.136426,-0.2769857 0.330729,-0.5084961 0.194304,-0.2356445 0.434082,-0.4010091 0.243913,-0.1653646 0.525033,-0.2563151 0.285254,-0.095085 0.599446,-0.095085 0.413412,0 0.7028,0.144694 0.289388,0.1405599 0.51263,0.434082 l -0.338997,0.3927409 q -0.03307,0.045475 -0.08268,0.074414 -0.04961,0.028939 -0.107487,0.028939 -0.07028,0 -0.128157,-0.037207 -0.05374,-0.041341 -0.124024,-0.086816 -0.07028,-0.045475 -0.177767,-0.082682 -0.103352,-0.041341 -0.272851,-0.041341 -0.219108,0 -0.417546,0.1198893 -0.198437,0.1198893 -0.351399,0.3389974 -0.152963,0.219108 -0.243913,0.5291666 -0.09095,0.3100586 -0.09095,0.6945312 0,0.4382161 0.19017,0.6738606 0.194303,0.2315108 0.516764,0.2315108 0.223242,0 0.363802,-0.06201 0.14056,-0.062012 0.239779,-0.1364258 0.09922,-0.074414 0.177767,-0.1364258 0.08268,-0.062012 0.181901,-0.062012 0.05374,0 0.09922,0.024805 0.04961,0.024805 0.08682,0.07028 l 0.268717,0.3224607 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path897" />
<path
d="M 57.035872,10.866552 57.751073,4.92583 h 1.050065 l -0.305924,2.5052733 q 0.30179,-0.3720703 0.65319,-0.5498372 0.355534,-0.181901 0.727604,-0.181901 0.264584,0 0.475423,0.1033528 0.214974,0.1033529 0.3514,0.3017904 0.14056,0.1984375 0.194303,0.4878255 0.05788,0.289388 0.0124,0.6614583 l -0.310059,2.6127599 h -1.045931 l 0.305925,-2.6127599 q 0.04961,-0.3803385 -0.04134,-0.541569 -0.09095,-0.1653646 -0.338998,-0.1653646 -0.152962,0 -0.318327,0.086816 -0.16123,0.082682 -0.314192,0.2397787 -0.152963,0.1529622 -0.285254,0.3720702 -0.132292,0.214974 -0.223242,0.4836914 l -0.256315,2.1373368 z"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531"
id="path900" />
</g>
<flowRoot
xml:space="preserve"
id="flowRoot844"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><flowRegion
id="flowRegion846"><rect
id="rect848"
width="718.11023"
height="548.03149"
x="37.795277"
y="404.40945" /></flowRegion><flowPara
id="flowPara850" /></flowRoot> </g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,294 @@
%!PS-Adobe-3.0 EPSF-3.0
%%Creator: cairo 1.16.0 (https://cairographics.org)
%%CreationDate: Wed Feb 20 19:23:49 2019
%%Pages: 1
%%DocumentData: Clean7Bit
%%LanguageLevel: 2
%%BoundingBox: 47 71 217 100
%%EndComments
%%BeginProlog
50 dict begin
/q { gsave } bind def
/Q { grestore } bind def
/cm { 6 array astore concat } bind def
/w { setlinewidth } bind def
/J { setlinecap } bind def
/j { setlinejoin } bind def
/M { setmiterlimit } bind def
/d { setdash } bind def
/m { moveto } bind def
/l { lineto } bind def
/c { curveto } bind def
/h { closepath } bind def
/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
0 exch rlineto 0 rlineto closepath } bind def
/S { stroke } bind def
/f { fill } bind def
/f* { eofill } bind def
/n { newpath } bind def
/W { clip } bind def
/W* { eoclip } bind def
/BT { } bind def
/ET { } bind def
/BDC { mark 3 1 roll /BDC pdfmark } bind def
/EMC { mark /EMC pdfmark } bind def
/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
/Tj { show currentpoint cairo_store_point } bind def
/TJ {
{
dup
type /stringtype eq
{ show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
} forall
currentpoint cairo_store_point
} bind def
/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
/Tf { pop /cairo_font exch def /cairo_font_matrix where
{ pop cairo_selectfont } if } bind def
/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
/cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
/cairo_font where { pop cairo_selectfont } if } bind def
/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
/g { setgray } bind def
/rg { setrgbcolor } bind def
/d1 { setcachedevice } bind def
/cairo_data_source {
CairoDataIndex CairoData length lt
{ CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }
{ () } ifelse
} def
/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def
/cairo_image { image cairo_flush_ascii85_file } def
/cairo_imagemask { imagemask cairo_flush_ascii85_file } def
%%EndProlog
%%BeginSetup
%%BeginResource: font Carlito-BoldItalic
11 dict begin
/FontType 42 def
/FontName /Carlito-BoldItalic def
/PaintType 0 def
/FontMatrix [ 1 0 0 1 0 0 ] def
/FontBBox [ 0 0 0 0 ] def
/Encoding 256 array def
0 1 255 { Encoding exch /.notdef put } for
Encoding 45 /hyphen put
Encoding 46 /period put
Encoding 98 /b put
Encoding 99 /c put
Encoding 101 /e put
Encoding 104 /h put
Encoding 105 /i put
Encoding 108 /l put
Encoding 109 /m put
Encoding 110 /n put
/CharStrings 11 dict dup begin
/.notdef 0 def
/m 1 def
/i 2 def
/n 3 def
/hyphen 4 def
/b 5 def
/e 6 def
/l 7 def
/period 8 def
/c 9 def
/h 10 def
end readonly def
/sfnts [
<0001000000090080000300106376742026d7027600000828000000386670676ddbb62e8c0000
086000000a75676c79667c62e8020000009c0000078c686561647d5a70d0000012d800000036
686865610e6808980000131000000024686d747826e2028c000013340000002c6c6f63610000
319400001360000000306d61787001bf0bc90000139000000020707265709a783a38000013b0
0000008100040028000003ff057600230037003b003f0088b50001020001424bb01a50584034
000302010203016800010402010466000000020300025b000400050804055b00090906510006
060e4300080807510007070d07441b4032000302010203016800010402010466000600090006
0959000000020300025b000400050804055b00080807510007070d074459400d3f3e11111428
26232a1c220a182b133e0133321e0215140e040f012327263e0435342623220e022322271334
3e0233321e0215140e0223222e020121112137211121e5388e61466e4c281a29302b220516ae
12061527312b1d292a222d211a0f2410481525321e1c3225151525321c1e322515feb803d7fc
2941034cfcb404552d3c25445f3a354c39292322155b6d24342a23272f211f260c0f0c1efd4a
1c3225151525321c1d3125151525310460fa8a4504ec0000000100370000061903f0002c005b
400c06010400201f0c03030402424bb0245058401606010404005302010200000f4308070503
03030d03441b401a0000000f430601040401530201010117430807050303030d034459400f00
00002c002c2316231524252109162b33133332160f013e01333216173e0133321e0207032313
362623220e020715032313362623220e020703377881282e010548a85d6372094ab66c446641
170b4afa4a0c273c274d443a153bfb4a0c243c2a4f443a153a03e2272b6966637b747b743260
8c5afd8802785c4d274a6b4301fdff02785b4e2a4f6f45fe0c00000000020043000001f0059d
000300170026402300020203530003030e43040101010f430000000d0044000014120a080003
00031105102b0103231301140e0223222e0235343e0233321e0201b477fa7701361b2d391e1d
372a1a1a2b371e1e392c1a03e1fc1f03e1011d20392b19192b3920203a2b1a192c3900010032
000003ec03f0001a004bb50601020301424bb024505840130003030053010100000f43050402
02020d02441b40170000000f430003030153000101174305040202020d024459400c0000001a
001a2315252106132b33133332160f013e0133321e0207032313362623220e02070332778129
2d02054ebc644067431b0b4afe4b0b2d3c29564f41133403e2272b776d6a32608c5afd880278
5c4d2e547749fe210001005101e1023e02ad00030017401400000101004d0000000151000100
0145111002112b132107216a01d41afe2d02adcc00000002003bfff1040b059d0017002800a6
4bb0225058400b1e030205041301020502421b400b1e03020504130103050242594bb00f5058
401d0000000e430701040401530001011743000505025406030202021502441b4bb022505840
1d0000000e430701040401530001011743000505025406030202021802441b40210000000e43
0701040401530001011743060103030d43000505025400020218024459594013191800002220
182819280017001628231108122b331333033e0133321e0215140e0223222627070e01230122
0e020f011e0133323e023534263badf94e48af6341694b294f88b5664f7b2a080b242a01c728
534d42171c22562a406a4b2945059dfd855f6f2d5c8d607febb46b3c331b2322032a365d7e48
e5201b4c7b9a4f67620000020031fff103c403ef002300320063400b29050201041001000102
424bb00f5058401e000104000401006805010404035300030317430000000253000202150244
1b401e00010400040100680501040403530003031743000000025300020218024459400c2524
243225322826232706132b01140e010407141633323e0233321f010e0323222e0235343e0233
321e0225220e02073e0335342e0203c43b96fefdc76f72485d42331f1b1342386a717d49609d
6f3d5195d3835681552bfe9d365d4a361086a45a1e0e1f3002f2426d573e127671202720164e
344b31173c6f9b6075d9a6642d495a2025435f3910252b311c0f1d180f000001003f000001e7
059d0003001840150000000e43020101010d01440000000300031103102b331333033fadfbae
059dfa63000000010054fff20185012000130012400f0000000153000101150144282402112b
37343e0233321e0215140e0223222e025417293820203829181829382020382917891f382818
1828381f203828171728380000000001002efff1036103ef002e0069400a1401030100010004
02424bb00f505840240002030503020568000504030504660003030153000101174300040400
530000001500441b402400020305030205680005040305046600030301530001011743000404
005300000018004459b7232623252a2406152b250e0323222e0235343e0433321617070e0123
222e0223220e0215141633323e023332161f01032e31595e673f57895f3323425e75894c648c
365208180e111b22332935604a2c5d4e36443027180d170941b036492d133b6d995f51998671
502d45475f0b0e1316133a6a965d6a711e241e0c0b4e0001003a000003ee059d0017002c4029
0301030101420000000e430003030153000101174305040202020d0244000000170017231523
1106132b331333033e0133321e0207032313362623220e0207033aadfe4a49ab5a4067431b0b
4bfd4a0c2c3c254f4a40163e059dfda25a5732608c5afd8802785c4f294b6941fdfb00000000
000000000000000000000000000000fe00c200fe00c205300000059d03e10000febe079efdda
053ffff2059d03effff1febe079efddab0002cb02060662db0012c206420b0c050b004265ab0
04455b582123211b8a5820b050505821b040591b20b038505821b038595920b00b456164b028
505821b00b4520b030505821b030591b20b0c050582066208a8a6120b00a5058601b20b02050
5821b00a601b20b036505821b036601b605959591bb0002b595923b00050586559592db0022c
204520b00425616420b005435058b0052342b00623421b212159b001602db0032c2321232120
64b105624220b0062342b20b01022a2120b00643208a208ab0002bb13005258a515860501b61
52595823592120b0405358b0002b1b21b0405923b000505865592db0042cb007432bb2000200
4360422db0052cb00723422320b000234261b0026266b00163b00160b0042a2db0062c202045
20b0024563b804006220b0005058b040605966b001636044b001602db0072c20204520b0002b
23b10804256020458a2361206420b020505821b0001bb0305058b0201bb040595923b0005058
6559b0032523614444b001602db0082cb1050545b00161442db0092cb001602020b009434ab0
00505820b009234259b00a434ab000525820b00a2342592db00a2c20b0106266b0016320b804
00638a2361b00b4360208a6020b00b2342232db00b2c4b5458b10701445924b00d6523782db0
0c2c4b51584b5358b1070144591b215924b0136523782db00d2cb1000c435558b10c0c43b001
6142b00a2b59b00043b0022542b109022542b10a022542b001162320b003255058b101004360
b00425428a8a208a2361b0092a2123b00161208a2361b0092a211bb101004360b0022542b002
2561b0092a2159b0094347b00a434760b0026220b0005058b040605966b0016320b0024563b8
04006220b0005058b040605966b0016360b10000132344b00143b0003eb20101014360422db0
0e2cb1000545545800b00c23422060b00161b50d0d01000b0042428a60b10d052bb06d2b1b22
592db00f2cb1000e2b2db0102cb1010e2b2db0112cb1020e2b2db0122cb1030e2b2db0132cb1
040e2b2db0142cb1050e2b2db0152cb1060e2b2db0162cb1070e2b2db0172cb1080e2b2db018
2cb1090e2b2db0192cb0082bb1000545545800b00c23422060b00161b50d0d01000b0042428a
60b10d052bb06d2b1b22592db01a2cb100192b2db01b2cb101192b2db01c2cb102192b2db01d
2cb103192b2db01e2cb104192b2db01f2cb105192b2db0202cb106192b2db0212cb107192b2d
b0222cb108192b2db0232cb109192b2db0242c203cb001602db0252c2060b00d60204323b001
6043b0022561b00160b0242a212db0262cb0252bb0252a2db0272c2020472020b0024563b804
006220b0005058b040605966b001636023613823208a555820472020b0024563b804006220b0
005058b040605966b00163602361381b21592db0282cb1000545545800b00116b0272ab00115
301b22592db0292cb0082bb1000545545800b00116b0272ab00115301b22592db02a2c2035b0
01602db02b2c00b0034563b804006220b0005058b040605966b00163b0002bb0024563b80400
6220b0005058b040605966b00163b0002bb00016b40000000000443e2338b12a01152a2db02c
2c203c204720b0024563b804006220b0005058b040605966b0016360b0004361382db02d2c2e
173c2db02e2c203c204720b0024563b804006220b0005058b040605966b0016360b0004361b0
014363382db02f2cb102001625202e2047b0002342b00225498a8a47234723612058621b2159
b0012342b22e010115142a2db0302cb00016b00425b004254723472361b006452b658a2e2320
203c8a382db0312cb00016b00425b00425202e472347236120b0042342b006452b20b0605058
20b0405158b3022003201bb30226031a5942422320b00843208a234723472361234660b00443
b0026220b0005058b040605966b001636020b0002b208a8a6120b00243606423b00343616450
58b00243611bb003436059b00325b0026220b0005058b040605966b0016361232020b0042623
4661381b23b0084346b00225b0084347234723616020b00443b0026220b0005058b040605966
b00163602320b0002b23b0044360b0002bb0052561b00525b0026220b0005058b040605966b0
0163b004266120b00425606423b0032560645058211b232159232020b0042623466138592db0
322cb00016202020b00526202e4723472361233c382db0332cb0001620b00823422020204623
47b0002b2361382db0342cb00016b00325b002254723472361b00054582e203c23211bb00225
b00225472347236120b00525b004254723472361b00625b0052549b0022561b0014563232058
621b215963b804006220b0005058b040605966b0016360232e2320203c8a382321592db0352c
b0001620b00843202e47234723612060b0206066b0026220b0005058b040605966b001632320
203c8a382db0362c23202e46b00225465258203c592eb12601142b2db0372c23202e46b00225
465058203c592eb12601142b2db0382c23202e46b00225465258203c5923202e46b002254650
58203c592eb12601142b2db0392cb0302b23202e46b00225465258203c592eb12601142b2db0
3a2cb0312b8a20203cb00423428a3823202e46b00225465258203c592eb12601142bb004432e
b0262b2db03b2cb00016b00425b00426202e4723472361b006452b23203c202e2338b1260114
2b2db03c2cb108042542b00016b00425b00425202e472347236120b0042342b006452b20b060
505820b0405158b3022003201bb30226031a594242232047b00443b0026220b0005058b04060
5966b001636020b0002b208a8a6120b00243606423b0034361645058b00243611bb003436059
b00325b0026220b0005058b040605966b0016361b0022546613823203c23381b212020462347
b0002b2361382159b12601142b2db03d2cb0302b2eb12601142b2db03e2cb0312b212320203c
b00423422338b12601142bb004432eb0262b2db03f2cb000152047b0002342b2000101151413
2eb02c2a2db0402cb000152047b0002342b20001011514132eb02c2a2db0412cb100011413b0
2d2a2db0422cb02f2a2db0432cb000164523202e20468a236138b12601142b2db0442cb00823
42b0432b2db0452cb200003c2b2db0462cb200013c2b2db0472cb201003c2b2db0482cb20101
3c2b2db0492cb200003d2b2db04a2cb200013d2b2db04b2cb201003d2b2db04c2cb201013d2b
2db04d2cb20000392b2db04e2cb20001392b2db04f2cb20100392b2db0502cb20101392b2db0
512cb200003b2b2db0522cb200013b2b2db0532cb201003b2b2db0542cb201013b2b2db0552c
b200003e2b2db0562cb200013e2b2db0572cb201003e2b2db0582cb201013e2b2db0592cb200
003a2b2db05a2cb200013a2b2db05b2cb201003a2b2db05c2cb201013a2b2db05d2cb0322b2e
b12601142b2db05e2cb0322bb0362b2db05f2cb0322bb0372b2db0602cb00016b0322bb0382b
2db0612cb0332b2eb12601142b2db0622cb0332bb0362b2db0632cb0332bb0372b2db0642cb0
332bb0382b2db0652cb0342b2eb12601142b2db0662cb0342bb0362b2db0672cb0342bb0372b
2db0682cb0342bb0382b2db0692cb0352b2eb12601142b2db06a2cb0352bb0362b2db06b2cb0
352bb0372b2db06c2cb0352bb0382b2db06d2c2bb00865b003245078b00115302d0000000001
000000011a5e61392c2f5f0f3cf5001b0800000000004a53db2600000000ce2e3052fc8dfdd6
09190828000300060002000100000000000100000600fe0001c40aa0fc8df7ed0919080000fb
000000000000000000000000000b040e0028066e003701f7004304380032027300510439003b
03ee003101f7003f02230054034b002e0438003a00000000000001440000022c000002a40000
03480000037c000004a000000598000005cc000006200000070c0000078c00010000000b00f5
00100000000000020050005d006e000000e50a75000000004bb800c85258b101018e59b90800
08006320b0012344b0032370b017452020b0286066208a5558b0022561b00145632362b00223
44b20b01062ab20c06062ab21406062a59b2042809455244b20c08072ab1060144b124018851
58b0408858b1060344b12601885158b804008858b106014459595959b801ff85b0048db10500
4400000000>
] def
/f-0-0 currentdict end definefont pop
%%EndResource
%%EndSetup
%%Page: 1 1
%%BeginPageSetup
%%PageBoundingBox: 47 71 217 100
%%EndPageSetup
q 47 71 170 29 rectclip
1 0 0 -1 0 149 cm q
0.282353 0.215686 0.215686 rg
68.875 71.926 m 65.875 73.656 l 62.871 75.391 l 59.93 77.086 59.613 76.977
56.867 75.391 c 53.867 73.656 l 50.863 71.926 l 47.926 70.227 47.863 69.895
47.863 66.727 c 47.863 59.793 l 47.863 56.398 48.121 56.18 50.863 54.594
c 53.867 52.859 l 56.867 51.129 l 59.809 49.43 60.125 49.543 62.871 51.129
c 65.875 52.859 l 68.875 54.594 l 71.816 56.293 71.875 56.625 71.875 59.793
c 71.875 66.727 l 71.875 70.121 71.621 70.34 68.875 71.926 c h
68.875 71.926 m f
1.205089 w
1 J
1 j
[] 0.0 d
4 M q 1 0 0 1 0 0 cm
68.875 71.926 m 65.875 73.656 l 62.871 75.391 l 59.93 77.086 59.613 76.977
56.867 75.391 c 53.867 73.656 l 50.863 71.926 l 47.926 70.227 47.863 69.895
47.863 66.727 c 47.863 59.793 l 47.863 56.398 48.121 56.18 50.863 54.594
c 53.867 52.859 l 56.867 51.129 l 59.809 49.43 60.125 49.543 62.871 51.129
c 65.875 52.859 l 68.875 54.594 l 71.816 56.293 71.875 56.625 71.875 59.793
c 71.875 66.727 l 71.875 70.121 71.621 70.34 68.875 71.926 c h
68.875 71.926 m S Q
1 0.8 0 rg
3.31125 w
q 1 0 0 1 0 0 cm
53.262 60.066 m 55.242 59.23 57.422 58.766 59.707 58.766 c 61.992 58.766
64.168 59.23 66.148 60.066 c S Q
q 1 0 0 1 0 0 cm
55.051 64.297 m 56.48 63.695 58.055 63.359 59.707 63.359 c 61.355 63.359
62.93 63.695 64.359 64.297 c S Q
q 1 0 0 1 0 0 cm
56.84 68.535 m 57.723 68.164 58.691 67.957 59.707 67.957 c 60.723 67.957
61.691 68.164 62.57 68.535 c S Q
0.282353 0.215686 0.215686 rg
2.410177 w
0 j
q 1 0 0 1 0 0 cm
54.613 53.715 m 53.109 50.703 l S Q
q 1 0 0 1 0 0 cm
65.16 53.715 m 66.664 50.703 l S Q
BT
23.999999 0 0 -23.999999 83.645814 69.718046 Tm
/f-0-0 1 Tf
[(mini-be)-3(ieli.)-3(ch)]TJ
ET
Q Q
showpage
%%Trailer
end
%%EOF

View File

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="83.081947mm"
height="52.319721mm"
viewBox="0 0 83.081947 52.319721"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (unknown)"
sodipodi:docname="mini-beieli-praegung.svg"
inkscape:export-filename="/home/joerg/bee-logo-box.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0"
refX="0"
id="DotL"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path978"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,5.92,0.8)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.1106813"
inkscape:cx="10.645336"
inkscape:cy="42.625215"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="3840"
inkscape:window-height="2089"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="1"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10">
<inkscape:grid
type="xygrid"
id="grid895"
spacingx="1"
spacingy="1"
originx="15.558254"
originy="-258.4087" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(15.558254,13.728425)">
<path
style="opacity:1;fill:#483737;fill-opacity:1;stroke:#483737;stroke-width:0.42512852;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
d="m 8.7393227,11.644791 -1.0589324,0.611374 -1.0589359,0.611374 c -1.0371974,0.598824 -1.1494606,0.559109 -2.1178708,0 L 3.4446478,12.256165 2.3857128,11.644791 C 1.3485151,11.045971 1.3267775,10.928889 1.3267775,9.8106715 V 8.5879237 7.3651789 c 0,-1.1976465 0.090525,-1.2750137 1.0589353,-1.8341225 L 3.4446478,4.9196842 4.5035836,4.3083089 c 1.0371978,-0.59882 1.1494596,-0.559106 2.1178708,0 l 1.0589359,0.6113753 1.0589324,0.6113722 c 1.0371922,0.5988236 1.058929,0.7159034 1.058929,1.8341225 v 1.2227448 1.2227478 c 0,1.1976435 -0.090524,1.2750145 -1.058929,1.8341195 z"
id="path815"
inkscape:connector-curvature="0"
sodipodi:nodetypes="scsscsscsscsscsscss"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 3.231964,7.4616379 c 0.6985148,-0.295449 1.4664939,-0.4588256 2.272625,-0.4588256 0.8061387,0 1.5741179,0.1633766 2.2726329,0.4588256"
id="path846-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 3.8626628,8.9546319 c 0.5046637,-0.2134551 1.0595109,-0.331491 1.6419262,-0.331491 0.5824224,0 1.1372702,0.1180359 1.6419342,0.331491"
id="path846-1-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#ffcc00;stroke-width:1.16813552;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
d="m 4.494172,10.44954 c 0.3105661,-0.131357 0.6520092,-0.203993 1.010417,-0.203993 0.3584154,0 0.699858,0.07264 1.0104178,0.203993"
id="path846-8-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="fill:#fc0ff0;fill-opacity:0;stroke:#483737;stroke-width:0.85025704;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 3.7083008,5.2207965 3.1768908,4.1579827"
id="path899"
inkscape:connector-curvature="0"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="fill:none;stroke:#483737;stroke-width:0.85025704;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 7.4281735,5.2207965 7.9595827,4.1579827"
id="path899-8"
inkscape:connector-curvature="0"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.44661283px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.11116531"
x="13.95013"
y="10.866552"
id="text28"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"><tspan
sodipodi:role="line"
id="tspan26"
x="13.95013"
y="10.866552"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46666622px;font-family:Carlito;-inkscape-font-specification:'Carlito Bold Italic';fill:#483737;fill-opacity:1;stroke-width:0.11116531">mini-beieli.ch</tspan></text>
<flowRoot
xml:space="preserve"
id="flowRoot844"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><flowRegion
id="flowRegion846"><rect
id="rect848"
width="718.11023"
height="548.03149"
x="37.795277"
y="404.40945" /></flowRegion><flowPara
id="flowPara850" /></flowRoot> </g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
Case/1455C1202.pdf Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,30 @@
$fn = 100;
// Head
head_length=57.13;
head_width=26.13;
head_thickness=2.4;
head_radius_corners=(44.65 - 35.6) /2.0;
offset_m12_holes = (10-2-1.6-0.1)-8.5;
module loch() {
cylinder(r=(0.15*25.4/2), h=10, center=true);
cylinder(r1=(0.23*25.4/2), r2=(0.15*25.4/2), h=((0.23-0.15)*25.4/2), center=true);
}
module m12() {
cylinder(d=12.2, h=10, center=true);
}
color("red")
difference() {
minkowski() {
cube([head_length - (2 * head_radius_corners),head_width - (2 * head_radius_corners),head_thickness], center=true);
cylinder(r=head_radius_corners, h=0.1, center=true);
};
translate([-48.15/2,0,0-((0.23-0.15)*25.4/2)]) loch();
translate([48.15/2,0,0-((0.23-0.15)*25.4/2)]) loch();
translate([-20.95/2,offset_m12_holes,0]) m12();
translate([20.95/2,offset_m12_holes,0]) m12();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,38 @@
$fn = 100;
// Head
head_length=57.13;
head_width=26.13;
head_thickness=2.4;
head_radius_corners=(44.65 - 35.6) /2.0;
module loch() {
cylinder(r=(0.15*25.4/2), h=10, center=true);
cylinder(r1=(0.23*25.4/2), r2=(0.15*25.4/2), h=((0.23-0.15)*25.4/2), center=true);
}
module switch() {
cube([9.2,13.8,5],center = true);
}
module usb() {
cube([8,3.2,5],center = true);
}
module antenne() {
cylinder(d=6.5, h=10, center=true);
}
color("red")
difference() {
minkowski() {
cube([head_length - (2 * head_radius_corners),head_width - (2 * head_radius_corners),head_thickness], center=true);
cylinder(r=head_radius_corners, h=0.1, center=true);
};
translate([-48.15/2,0,0-((0.23-0.15)*25.4/2)]) loch();
translate([48.15/2,0,0-((0.23-0.15)*25.4/2)]) loch();
translate([-11.4-0.3-(8.3/2),-2.5]) switch();
translate([0,1,0]) usb();
translate([16,0,0]) antenne();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

58
Case/endcap-front.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.4 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
PCB/BeieliScale-v3.0.fzz Normal file

Binary file not shown.

BIN
PCB/BeieliScale-v3.1.fzz Normal file

Binary file not shown.

BIN
PCB/BeieliScale-v4.0.fzz Normal file

Binary file not shown.

4
PCB/README-1.3.txt Normal file
View File

@ -0,0 +1,4 @@
Uploaded
23 Apr 22:40

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,210 @@
EESchema-LIBRARY Version 2.4
#encoding utf-8
#
# Connector_Conn_01x02_Female
#
DEF Connector_Conn_01x02_Female J 0 40 Y N 1 F N
F0 "J" 0 100 50 H V C CNN
F1 "Connector_Conn_01x02_Female" 0 -200 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
A 0 -100 20 901 -901 1 1 6 N 0 -80 0 -120
A 0 0 20 901 -901 1 1 6 N 0 20 0 -20
P 2 1 1 6 -50 -100 -20 -100 N
P 2 1 1 6 -50 0 -20 0 N
X Pin_1 1 -200 0 150 R 50 50 1 1 P
X Pin_2 2 -200 -100 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x03_Male
#
DEF Connector_Conn_01x03_Male J 0 40 Y N 1 F N
F0 "J" 0 200 50 H V C CNN
F1 "Connector_Conn_01x03_Male" 0 -200 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
S 34 -95 0 -105 1 1 6 F
S 34 5 0 -5 1 1 6 F
S 34 105 0 95 1 1 6 F
P 2 1 1 6 50 -100 34 -100 N
P 2 1 1 6 50 0 34 0 N
P 2 1 1 6 50 100 34 100 N
X Pin_1 1 200 100 150 L 50 50 1 1 P
X Pin_2 2 200 0 150 L 50 50 1 1 P
X Pin_3 3 200 -100 150 L 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Conn_01x05_Female
#
DEF Connector_Conn_01x05_Female J 0 40 Y N 1 F N
F0 "J" 0 300 50 H V C CNN
F1 "Connector_Conn_01x05_Female" 0 -300 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*_1x??_*
$ENDFPLIST
DRAW
A 0 -200 20 901 -901 1 1 6 N 0 -180 0 -220
A 0 -100 20 901 -901 1 1 6 N 0 -80 0 -120
A 0 0 20 901 -901 1 1 6 N 0 20 0 -20
A 0 100 20 901 -901 1 1 6 N 0 120 0 80
A 0 200 20 901 -901 1 1 6 N 0 220 0 180
P 2 1 1 6 -50 -200 -20 -200 N
P 2 1 1 6 -50 -100 -20 -100 N
P 2 1 1 6 -50 0 -20 0 N
P 2 1 1 6 -50 100 -20 100 N
P 2 1 1 6 -50 200 -20 200 N
X Pin_1 1 -200 200 150 R 50 50 1 1 P
X Pin_2 2 -200 100 150 R 50 50 1 1 P
X Pin_3 3 -200 0 150 R 50 50 1 1 P
X Pin_4 4 -200 -100 150 R 50 50 1 1 P
X Pin_5 5 -200 -200 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# Connector_Generic_Conn_01x01
#
DEF Connector_Generic_Conn_01x01 J 0 40 Y N 1 F N
F0 "J" 0 100 50 H V C CNN
F1 "Connector_Generic_Conn_01x01" 0 -100 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
$FPLIST
Connector*:*
$ENDFPLIST
DRAW
S -50 5 0 -5 1 1 6 N
S -50 50 50 -50 1 1 10 f
X Pin_1 1 -200 0 150 R 50 50 1 1 P
ENDDRAW
ENDDEF
#
# MiscellaneousDevices_ADAFRUIT_FEATHER
#
DEF MiscellaneousDevices_ADAFRUIT_FEATHER U 0 40 Y Y 2 F N
F0 "U" 200 -550 60 V V C CNN
F1 "MiscellaneousDevices_ADAFRUIT_FEATHER" 200 250 60 V V C CNN
F2 "" 150 -200 60 H V C CNN
F3 "" 150 -200 60 H V C CNN
DRAW
S -150 650 150 -650 1 1 0 N
S -150 800 150 -800 2 1 0 N
X VBAT 17 -250 550 100 R 40 40 1 1 I
X EN 18 -250 450 100 R 40 40 1 1 I
X VBUS 19 -250 350 100 R 40 40 1 1 I
X 13 20 -250 250 100 R 40 40 1 1 I
X 12 21 -250 150 100 R 40 40 1 1 I
X 11 22 -250 50 100 R 40 40 1 1 I
X 10 23 -250 -50 100 R 40 40 1 1 I
X 9 24 -250 -150 100 R 40 40 1 1 I
X 6 25 -250 -250 100 R 40 40 1 1 I
X 5 26 -250 -350 100 R 40 40 1 1 I
X 3 27 -250 -450 100 R 40 40 1 1 I
X 2 28 -250 -550 100 R 40 40 1 1 I
X DIO1 1 -250 -750 100 R 40 40 2 1 I
X A2 10 -250 150 100 R 40 40 2 1 I
X A1 11 -250 250 100 R 40 40 2 1 I
X A0 12 -250 350 100 R 40 40 2 1 I
X GND 13 -250 450 100 R 40 40 2 1 I
X AREF 14 -250 550 100 R 40 40 2 1 I
X +3V3 15 -250 650 100 R 40 40 2 1 I
X RST 16 -250 750 100 R 40 40 2 1 I
X 1 2 -250 -650 100 R 40 40 2 1 I
X 0 3 -250 -550 100 R 40 40 2 1 I
X MISO 4 -250 -450 100 R 40 40 2 1 I
X MOSI 5 -250 -350 100 R 40 40 2 1 I
X SCK 6 -250 -250 100 R 40 40 2 1 I
X A5 7 -250 -150 100 R 40 40 2 1 I
X A4 8 -250 -50 100 R 40 40 2 1 I
X A3 9 -250 50 100 R 40 40 2 1 I
ENDDRAW
ENDDEF
#
# SEN-13879_SEN-13879
#
DEF SEN-13879_SEN-13879 U 0 40 Y Y 1 L N
F0 "U" -400 626 50 H V L BNN
F1 "SEN-13879_SEN-13879" -400 -700 50 H V L BNN
F2 "SPARKFUN_SEN-13879" 0 0 50 H I L BNN
F3 "Load Cell Amp Hx711" 0 0 50 H I L BNN
F4 "SparkFun" 0 0 50 H I L BNN
F5 "Unavailable" 0 0 50 H I L BNN
F6 "SEN-13879" 0 0 50 H I L BNN
F7 "None" 0 0 50 H I L BNN
F8 "None" 0 0 50 H I L BNN
DRAW
P 2 0 0 10 -400 -600 -400 600 N
P 2 0 0 10 -400 600 400 600 N
P 2 0 0 10 400 -600 -400 -600 N
P 2 0 0 10 400 600 400 -600 N
X RED 1 -500 300 100 R 40 40 0 0 B
X VDD 10 500 500 100 L 40 40 0 0 W
X B+ 11 -500 -300 100 R 40 40 0 0 I
X B- 12 -500 -400 100 R 40 40 0 0 I
X BLK 2 -500 200 100 R 40 40 0 0 B
X WHT 3 -500 100 100 R 40 40 0 0 I
X GRN 4 -500 0 100 R 40 40 0 0 I
X YLW 5 -500 -100 100 R 40 40 0 0 B
X GND 6 500 -500 100 L 40 40 0 0 W
X CLK 7 500 -200 100 L 40 40 0 0 I C
X DAT 8 500 -100 100 L 40 40 0 0 O
X VCC 9 500 400 100 L 40 40 0 0 W
ENDDRAW
ENDDEF
#
# T4145015051-001_T4145015051-001
#
DEF T4145015051-001_T4145015051-001 J 0 40 N Y 1 L N
F0 "J" -300 500 50 H V L BNN
F1 "T4145015051-001_T4145015051-001" -300 -500 50 H V L BNN
F2 "T4145015051-001" 0 0 50 H I L BNN
F3 "Circular Metric Connectors; M12 R/A F PNLREAR STAMPED PIN A-CODE 5P" 0 0 50 H I L BNN
F4 "https://www.te.com/usa-en/product-T4145015051-001.html?te_bu=Cor&te_type=disp&te_campaign=seda_glo_cor-seda-global-disp-prtnr-fy19-seda-model-bom-cta_sma-317_1&elqCampaignId=32493" 0 0 50 H I L BNN
F5 "TE Connectivity" 0 0 50 H I L BNN
F6 "Unavailable" 0 0 50 H I L BNN
F7 "None" 0 0 50 H I L BNN
F8 "T4145015051-001" 0 0 50 H I L BNN
F9 "T4145015051-001" 0 0 50 H I L BNN
F10 "None" 0 0 50 H I L BNN
DRAW
P 2 0 0 10 -300 -300 300 -300 N
P 2 0 0 10 -300 400 -300 -300 N
P 2 0 0 10 -300 400 -100 400 N
P 2 0 0 10 -100 300 100 300 N
P 2 0 0 10 -100 400 -100 300 N
P 2 0 0 10 100 300 100 400 N
P 2 0 0 10 100 400 300 400 N
P 2 0 0 10 300 -300 300 400 N
X 1 P$1 -200 400 200 D 40 40 0 0 B I
X 2 P$2 200 400 200 D 40 40 0 0 B I
X 3 P$3 200 -300 200 U 40 40 0 0 B I
X 4 P$4 -200 -300 200 U 40 40 0 0 B I
X 5 P$5 0 300 300 D 40 40 0 0 B I
ENDDRAW
ENDDEF
#
# power_PWR_FLAG
#
DEF power_PWR_FLAG #FLG 0 0 N N 1 F P
F0 "#FLG" 0 75 50 H I C CNN
F1 "power_PWR_FLAG" 0 150 50 H V C CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
DRAW
P 6 0 1 0 0 0 0 50 -40 75 0 100 40 75 0 50 N
X pwr 1 0 0 0 U 50 50 0 0 w
ENDDRAW
ENDDEF
#
#End Library

View File

@ -0,0 +1,3 @@
EESchema-DOCLIB Version 2.0
#
#End Doc Library

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,406 @@
EESchema Schematic File Version 4
LIBS:beieliscale-kicad-cache
EELAYER 29 0
EELAYER END
$Descr A4 11693 8268
encoding utf-8
Sheet 1 1
Title ""
Date ""
Rev ""
Comp ""
Comment1 ""
Comment2 ""
Comment3 ""
Comment4 ""
$EndDescr
$Comp
L SEN-13879:SEN-13879 HX711-1
U 1 1 5C8AE7C5
P 4550 3350
F 0 "HX711-1" H 4550 4117 50 0000 C CNN
F 1 "SEN-13879" H 4550 4026 50 0000 C CNN
F 2 "SPARKFUN_SEN-13879" H 4550 3350 50 0001 L BNN
F 3 "Load Cell Amp Hx711" H 4550 3350 50 0001 L BNN
F 4 "SparkFun" H 4550 3350 50 0001 L BNN "Field4"
F 5 "Unavailable" H 4550 3350 50 0001 L BNN "Field5"
F 6 "SEN-13879" H 4550 3350 50 0001 L BNN "Field6"
F 7 "None" H 4550 3350 50 0001 L BNN "Field7"
F 8 "None" H 4550 3350 50 0001 L BNN "Field8"
1 4550 3350
1 0 0 -1
$EndComp
Wire Wire Line
6700 3550 6700 3150
Wire Wire Line
6700 3150 6850 3150
Wire Wire Line
5050 3550 6700 3550
Wire Wire Line
5050 3450 6750 3450
Wire Wire Line
6750 3450 6750 3250
Wire Wire Line
6750 3250 6850 3250
NoConn ~ 6850 2950
NoConn ~ 4050 3650
NoConn ~ 4050 3750
Wire Wire Line
5050 2950 5600 2950
Wire Wire Line
5600 2950 5600 2850
$Comp
L power:PWR_FLAG #FLG0101
U 1 1 5C8B848F
P 6000 2850
F 0 "#FLG0101" H 6000 2925 50 0001 C CNN
F 1 "PWR_FLAG" H 6000 3024 50 0000 C CNN
F 2 "" H 6000 2850 50 0001 C CNN
F 3 "~" H 6000 2850 50 0001 C CNN
1 6000 2850
1 0 0 -1
$EndComp
Wire Wire Line
5050 2850 5600 2850
Connection ~ 5600 2850
Wire Wire Line
5600 2850 5700 2850
Wire Wire Line
5500 3850 5500 3050
NoConn ~ 8700 3050
NoConn ~ 8700 3250
NoConn ~ 8700 3650
NoConn ~ 8700 3750
NoConn ~ 8700 3850
$Comp
L power:PWR_FLAG #FLG0102
U 1 1 5C8C2453
P 5250 3850
F 0 "#FLG0102" H 5250 3925 50 0001 C CNN
F 1 "PWR_FLAG" H 5250 4024 50 0000 C CNN
F 2 "" H 5250 3850 50 0001 C CNN
F 3 "~" H 5250 3850 50 0001 C CNN
1 5250 3850
1 0 0 -1
$EndComp
Connection ~ 5250 3850
$Comp
L SEN-13879:SEN-13879 HX711-2
U 1 1 5C8CD302
P 4550 5100
F 0 "HX711-2" H 4550 5867 50 0000 C CNN
F 1 "SEN-13879" H 4550 5776 50 0000 C CNN
F 2 "SPARKFUN_SEN-13879" H 4550 5100 50 0001 L BNN
F 3 "Load Cell Amp Hx711" H 4550 5100 50 0001 L BNN
F 4 "SparkFun" H 4550 5100 50 0001 L BNN "Field4"
F 5 "Unavailable" H 4550 5100 50 0001 L BNN "Field5"
F 6 "SEN-13879" H 4550 5100 50 0001 L BNN "Field6"
F 7 "None" H 4550 5100 50 0001 L BNN "Field7"
F 8 "None" H 4550 5100 50 0001 L BNN "Field8"
1 4550 5100
1 0 0 -1
$EndComp
NoConn ~ 4050 5400
NoConn ~ 4050 5500
Wire Wire Line
5050 4600 5600 4600
Wire Wire Line
5600 4600 5600 2950
Connection ~ 5600 2950
Wire Wire Line
5050 4700 5600 4700
Wire Wire Line
5600 4700 5600 4600
Connection ~ 5600 4600
Wire Wire Line
5050 5600 5250 5600
Wire Wire Line
5250 5600 5250 3850
Wire Wire Line
6650 5300 6650 3350
Wire Wire Line
6650 3350 6850 3350
Wire Wire Line
5050 5300 6650 5300
$Comp
L MiscellaneousDevices:ADAFRUIT_FEATHER U1
U 1 1 5C8BFC62
P 8950 3300
F 0 "U1" H 9128 3353 60 0000 L CNN
F 1 "MCCI 4610" H 9128 3247 60 0000 L CNN
F 2 "" H 9100 3100 60 0000 C CNN
F 3 "" H 9100 3100 60 0000 C CNN
1 8950 3300
1 0 0 -1
$EndComp
NoConn ~ 8700 2850
$Comp
L Connector:Conn_01x02_Female BAT1
U 1 1 5C92F7C2
P 4350 1900
F 0 "BAT1" H 4244 1575 50 0000 C CNN
F 1 "Conn_01x02_Female" H 4244 1666 50 0000 C CNN
F 2 "Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical" H 4350 1900 50 0001 C CNN
F 3 "~" H 4350 1900 50 0001 C CNN
1 4350 1900
-1 0 0 1
$EndComp
Wire Wire Line
8150 1900 8150 2750
Wire Wire Line
8150 2750 8700 2750
Wire Wire Line
5250 3850 5500 3850
$Comp
L T4145015051-001:T4145015051-001 J2
U 1 1 5CA65D1D
P 2950 5000
F 0 "J2" V 2483 5050 50 0000 C CNN
F 1 "T4145015051-001" V 2574 5050 50 0000 C CNN
F 2 "T4145015051-001" H 2950 5000 50 0001 L BNN
F 3 "Circular Metric Connectors; M12 R/A F PNLREAR STAMPED PIN A-CODE 5P" H 2950 5000 50 0001 L BNN
F 4 "https://www.te.com/usa-en/product-T4145015051-001.html?te_bu=Cor&te_type=disp&te_campaign=seda_glo_cor-seda-global-disp-prtnr-fy19-seda-model-bom-cta_sma-317_1&elqCampaignId=32493" H 2950 5000 50 0001 L BNN "Field4"
F 5 "TE Connectivity" H 2950 5000 50 0001 L BNN "Field5"
F 6 "Unavailable" H 2950 5000 50 0001 L BNN "Field6"
F 7 "None" H 2950 5000 50 0001 L BNN "Field7"
F 8 "T4145015051-001" H 2950 5000 50 0001 L BNN "Field8"
F 9 "T4145015051-001" H 2950 5000 50 0001 L BNN "Field9"
F 10 "None" H 2950 5000 50 0001 L BNN "Field10"
1 2950 5000
0 1 1 0
$EndComp
$Comp
L T4145015051-001:T4145015051-001 J1
U 1 1 5CA66C31
P 2950 3250
F 0 "J1" V 2483 3300 50 0000 C CNN
F 1 "T4145015051-001" V 2574 3300 50 0000 C CNN
F 2 "T4145015051-001" H 2950 3250 50 0001 L BNN
F 3 "Circular Metric Connectors; M12 R/A F PNLREAR STAMPED PIN A-CODE 5P" H 2950 3250 50 0001 L BNN
F 4 "https://www.te.com/usa-en/product-T4145015051-001.html?te_bu=Cor&te_type=disp&te_campaign=seda_glo_cor-seda-global-disp-prtnr-fy19-seda-model-bom-cta_sma-317_1&elqCampaignId=32493" H 2950 3250 50 0001 L BNN "Field4"
F 5 "TE Connectivity" H 2950 3250 50 0001 L BNN "Field5"
F 6 "Unavailable" H 2950 3250 50 0001 L BNN "Field6"
F 7 "None" H 2950 3250 50 0001 L BNN "Field7"
F 8 "T4145015051-001" H 2950 3250 50 0001 L BNN "Field8"
F 9 "T4145015051-001" H 2950 3250 50 0001 L BNN "Field9"
F 10 "None" H 2950 3250 50 0001 L BNN "Field10"
1 2950 3250
0 1 1 0
$EndComp
Wire Wire Line
4050 3050 3350 3050
Wire Wire Line
4050 3150 3450 3150
Wire Wire Line
3450 3150 3450 3450
Wire Wire Line
3450 3450 3350 3450
Wire Wire Line
4050 3250 3650 3250
Wire Wire Line
3650 3250 3650 3650
Wire Wire Line
3650 3650 2650 3650
Wire Wire Line
2650 3650 2650 3450
Wire Wire Line
4050 3350 3750 3350
Wire Wire Line
3750 3350 3750 3800
Wire Wire Line
3750 3800 2450 3800
Wire Wire Line
2450 3800 2450 3050
Wire Wire Line
2450 3050 2650 3050
Wire Wire Line
4050 3450 3850 3450
Wire Wire Line
3850 3450 3850 3900
Wire Wire Line
3850 3900 3250 3900
Wire Wire Line
3250 3900 3250 3250
Wire Wire Line
4050 4800 3350 4800
Wire Wire Line
4050 4900 3500 4900
Wire Wire Line
3500 4900 3500 5200
Wire Wire Line
3500 5200 3350 5200
Wire Wire Line
4050 5000 3600 5000
Wire Wire Line
3600 5000 3600 5400
Wire Wire Line
3600 5400 2500 5400
Wire Wire Line
2500 5400 2500 5200
Wire Wire Line
2500 5200 2650 5200
Wire Wire Line
4050 5100 3700 5100
Wire Wire Line
3700 5100 3700 5550
Wire Wire Line
3700 5550 2400 5550
Wire Wire Line
2400 5550 2400 4800
Wire Wire Line
2400 4800 2650 4800
Wire Wire Line
3750 5200 3750 5600
Wire Wire Line
3750 5600 3250 5600
Wire Wire Line
3250 5600 3250 5000
Wire Wire Line
3750 5200 4050 5200
Wire Wire Line
4550 1800 5150 1800
Wire Wire Line
5050 3850 5150 3850
Wire Wire Line
5150 3850 5250 3850
Connection ~ 5150 3850
Wire Wire Line
5150 1800 5150 3850
$Comp
L Connector:Conn_01x02_Female SWITCH1
U 1 1 5CAD5AEF
P 6300 1700
F 0 "SWITCH1" H 6194 1375 50 0000 C CNN
F 1 "Conn_01x02_Female" H 6194 1466 50 0000 C CNN
F 2 "Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical" H 6300 1700 50 0001 C CNN
F 3 "~" H 6300 1700 50 0001 C CNN
1 6300 1700
0 -1 -1 0
$EndComp
Wire Wire Line
6400 1900 8150 1900
Wire Wire Line
4550 1900 6300 1900
NoConn ~ 8700 2950
$Comp
L Connector:Conn_01x05_Female J3
U 1 1 5CBF85B5
P 6000 3850
F 0 "J3" H 6150 4050 50 0000 C CNN
F 1 "Conn_01x05_Female" H 5950 4150 50 0000 C CNN
F 2 "Connector_JST:JST_PH_B5B-PH-K_1x05_P2.00mm_Vertical" H 6000 3850 50 0001 C CNN
F 3 "~" H 6000 3850 50 0001 C CNN
1 6000 3850
-1 0 0 1
$EndComp
Wire Wire Line
6600 4050 6600 2850
Wire Wire Line
6600 2850 6850 2850
Wire Wire Line
6200 4050 6600 4050
Wire Wire Line
6200 3950 6500 3950
Wire Wire Line
6500 3950 6500 3050
Wire Wire Line
5500 3050 6500 3050
Connection ~ 6500 3050
Wire Wire Line
6500 3050 6850 3050
Wire Wire Line
6200 3650 6400 3650
Wire Wire Line
6400 3650 6400 2750
Wire Wire Line
6400 2750 6850 2750
Wire Wire Line
6200 3850 6400 3850
Wire Wire Line
6400 3850 6400 4450
Wire Wire Line
8500 4450 8500 3550
Wire Wire Line
8500 3550 8700 3550
Wire Wire Line
6200 3750 6550 3750
Wire Wire Line
6550 3750 6550 4350
Wire Wire Line
8400 4350 8400 3450
Wire Wire Line
8400 3450 8700 3450
Wire Wire Line
6550 4350 8400 4350
Wire Wire Line
6400 4450 8500 4450
NoConn ~ 6850 3550
$Comp
L MiscellaneousDevices:ADAFRUIT_FEATHER U1
U 2 1 5C8C025E
P 7100 3500
F 0 "U1" H 7278 3606 60 0000 L CNN
F 1 "MCCI 4610" H 7278 3500 60 0000 L CNN
F 2 "Modules:ADAFRUIT_FEATHER" H 7278 3394 60 0000 L CNN
F 3 "" H 7250 3300 60 0000 C CNN
2 7100 3500
1 0 0 -1
$EndComp
NoConn ~ 6850 4250
NoConn ~ 6850 3950
NoConn ~ 6850 3850
NoConn ~ 6850 3750
NoConn ~ 6850 3650
NoConn ~ 6850 3450
Wire Wire Line
8600 5200 5050 5200
Wire Wire Line
8600 3150 8600 5200
Wire Wire Line
8600 3150 8700 3150
$Comp
L Connector:Conn_01x03_Male J4
U 1 1 5CFBD036
P 6000 4900
F 0 "J4" H 6108 5181 50 0000 C CNN
F 1 "Conn_01x03_Male" H 6108 5090 50 0000 C CNN
F 2 "Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical" H 6000 4900 50 0001 C CNN
F 3 "~" H 6000 4900 50 0001 C CNN
1 6000 4900
1 0 0 -1
$EndComp
Wire Wire Line
6200 5600 6200 5000
Wire Wire Line
6200 4800 6750 4800
Wire Wire Line
6750 4800 6750 4050
Wire Wire Line
6750 4050 6850 4050
Wire Wire Line
6200 4900 6800 4900
Wire Wire Line
6800 4900 6800 4150
Wire Wire Line
6800 4150 6850 4150
NoConn ~ 8700 3350
Wire Wire Line
5250 5600 6200 5600
Connection ~ 5250 5600
$Comp
L Connector:Conn_01x01_Male J5
U 1 1 5CFE89F8
P 5500 2350
F 0 "J5" H 5608 2531 50 0000 C CNN
F 1 "VOUT1" H 5608 2440 50 0000 C CNN
F 2 "" H 5500 2350 50 0001 C CNN
F 3 "~" H 5500 2350 50 0001 C CNN
1 5500 2350
1 0 0 -1
$EndComp
Wire Wire Line
5700 2350 5700 2850
Connection ~ 5700 2850
Wire Wire Line
5700 2850 6000 2850
$EndSCHEMATC

View File

@ -0,0 +1,7 @@
"Id";"Designator";"Package";"Quantity";"Designation";"Supplier and ref";
1;"U2";"ADAFRUIT_FEATHER";1;"ADAFRUIT_FEATHER";;;
2;"HX711-2,HX711-1";"SPARKFUN_SEN-13879";2;"SEN-13879";;;
3;"J1,J2";"CUI_MD2-60ST";2;"Conn_01x05_Male";;;
4;"J3";"AMPHENOL_MUSB-K152-30";1;"MUSB-K152-30";;;
5;"J4";"JST_XH_B02B-XH-AM_1x02_P2.50mm_Vertical";1;"Conn_01x02_Female";;;
6;"SW1";"SW_E-Switch_EG1224_SPDT_Angled";1;"SWITCH_SPST";;;
Can't render this file because it has a wrong number of fields in line 2.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,369 @@
(export (version D)
(design
(source /home/joerg/git-repos/beieliscale/PCB/beieliscale-kicad/beieliscale-kicad.sch)
(date "Fr 07 Jun 2019 16:19:25 CEST")
(tool "Eeschema 5.1.2-1.fc30")
(sheet (number 1) (name /) (tstamps /)
(title_block
(title)
(company)
(rev)
(date)
(source beieliscale-kicad.sch)
(comment (number 1) (value ""))
(comment (number 2) (value ""))
(comment (number 3) (value ""))
(comment (number 4) (value "")))))
(components
(comp (ref HX711-1)
(value SEN-13879)
(footprint SPARKFUN_SEN-13879)
(datasheet "Load Cell Amp Hx711")
(fields
(field (name Field4) SparkFun)
(field (name Field5) Unavailable)
(field (name Field6) SEN-13879)
(field (name Field7) None)
(field (name Field8) None))
(libsource (lib SEN-13879) (part SEN-13879) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5C8AE7C5))
(comp (ref HX711-2)
(value SEN-13879)
(footprint SPARKFUN_SEN-13879)
(datasheet "Load Cell Amp Hx711")
(fields
(field (name Field4) SparkFun)
(field (name Field5) Unavailable)
(field (name Field6) SEN-13879)
(field (name Field7) None)
(field (name Field8) None))
(libsource (lib SEN-13879) (part SEN-13879) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5C8CD302))
(comp (ref U1)
(value "MCCI 4610")
(footprint Modules:ADAFRUIT_FEATHER)
(libsource (lib MiscellaneousDevices) (part ADAFRUIT_FEATHER) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5C8BFC62))
(comp (ref BAT1)
(value Conn_01x02_Female)
(footprint Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical)
(datasheet ~)
(libsource (lib Connector) (part Conn_01x02_Female) (description "Generic connector, single row, 01x02, script generated (kicad-library-utils/schlib/autogen/connector/)"))
(sheetpath (names /) (tstamps /))
(tstamp 5C92F7C2))
(comp (ref J2)
(value T4145015051-001)
(footprint T4145015051-001)
(datasheet "Circular Metric Connectors; M12 R/A F PNLREAR STAMPED PIN A-CODE 5P")
(fields
(field (name Field10) None)
(field (name Field4) https://www.te.com/usa-en/product-T4145015051-001.html?te_bu=Cor&te_type=disp&te_campaign=seda_glo_cor-seda-global-disp-prtnr-fy19-seda-model-bom-cta_sma-317_1&elqCampaignId=32493)
(field (name Field5) "TE Connectivity")
(field (name Field6) Unavailable)
(field (name Field7) None)
(field (name Field8) T4145015051-001)
(field (name Field9) T4145015051-001))
(libsource (lib T4145015051-001) (part T4145015051-001) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5CA65D1D))
(comp (ref J1)
(value T4145015051-001)
(footprint T4145015051-001)
(datasheet "Circular Metric Connectors; M12 R/A F PNLREAR STAMPED PIN A-CODE 5P")
(fields
(field (name Field10) None)
(field (name Field4) https://www.te.com/usa-en/product-T4145015051-001.html?te_bu=Cor&te_type=disp&te_campaign=seda_glo_cor-seda-global-disp-prtnr-fy19-seda-model-bom-cta_sma-317_1&elqCampaignId=32493)
(field (name Field5) "TE Connectivity")
(field (name Field6) Unavailable)
(field (name Field7) None)
(field (name Field8) T4145015051-001)
(field (name Field9) T4145015051-001))
(libsource (lib T4145015051-001) (part T4145015051-001) (description ""))
(sheetpath (names /) (tstamps /))
(tstamp 5CA66C31))
(comp (ref SWITCH1)
(value Conn_01x02_Female)
(footprint Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical)
(datasheet ~)
(libsource (lib Connector) (part Conn_01x02_Female) (description "Generic connector, single row, 01x02, script generated (kicad-library-utils/schlib/autogen/connector/)"))
(sheetpath (names /) (tstamps /))
(tstamp 5CAD5AEF))
(comp (ref J3)
(value Conn_01x05_Female)
(footprint Connector_JST:JST_PH_B5B-PH-K_1x05_P2.00mm_Vertical)
(datasheet ~)
(libsource (lib Connector) (part Conn_01x05_Female) (description "Generic connector, single row, 01x05, script generated (kicad-library-utils/schlib/autogen/connector/)"))
(sheetpath (names /) (tstamps /))
(tstamp 5CBF85B5))
(comp (ref J4)
(value Conn_01x03_Male)
(footprint Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical)
(datasheet ~)
(libsource (lib Connector) (part Conn_01x03_Male) (description "Generic connector, single row, 01x03, script generated (kicad-library-utils/schlib/autogen/connector/)"))
(sheetpath (names /) (tstamps /))
(tstamp 5CFBD036))
(comp (ref VOUT1)
(value Conn_01x01)
(footprint Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical)
(datasheet ~)
(libsource (lib Connector_Generic) (part Conn_01x01) (description "Generic connector, single row, 01x01, script generated (kicad-library-utils/schlib/autogen/connector/)"))
(sheetpath (names /) (tstamps /))
(tstamp 5CFF362B)))
(libparts
(libpart (lib Connector) (part Conn_01x02_Female)
(description "Generic connector, single row, 01x02, script generated (kicad-library-utils/schlib/autogen/connector/)")
(docs ~)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x02_Female))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))))
(libpart (lib Connector) (part Conn_01x03_Male)
(description "Generic connector, single row, 01x03, script generated (kicad-library-utils/schlib/autogen/connector/)")
(docs ~)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x03_Male))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))
(pin (num 3) (name Pin_3) (type passive))))
(libpart (lib Connector) (part Conn_01x05_Female)
(description "Generic connector, single row, 01x05, script generated (kicad-library-utils/schlib/autogen/connector/)")
(docs ~)
(footprints
(fp Connector*:*_1x??_*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x05_Female))
(pins
(pin (num 1) (name Pin_1) (type passive))
(pin (num 2) (name Pin_2) (type passive))
(pin (num 3) (name Pin_3) (type passive))
(pin (num 4) (name Pin_4) (type passive))
(pin (num 5) (name Pin_5) (type passive))))
(libpart (lib Connector_Generic) (part Conn_01x01)
(description "Generic connector, single row, 01x01, script generated (kicad-library-utils/schlib/autogen/connector/)")
(docs ~)
(footprints
(fp Connector*:*))
(fields
(field (name Reference) J)
(field (name Value) Conn_01x01))
(pins
(pin (num 1) (name Pin_1) (type passive))))
(libpart (lib MiscellaneousDevices) (part ADAFRUIT_FEATHER)
(fields
(field (name Reference) U)
(field (name Value) ADAFRUIT_FEATHER))
(pins
(pin (num 1) (name DIO1) (type input))
(pin (num 2) (name 1) (type input))
(pin (num 3) (name 0) (type input))
(pin (num 4) (name MISO) (type input))
(pin (num 5) (name MOSI) (type input))
(pin (num 6) (name SCK) (type input))
(pin (num 7) (name A5) (type input))
(pin (num 8) (name A4) (type input))
(pin (num 9) (name A3) (type input))
(pin (num 10) (name A2) (type input))
(pin (num 11) (name A1) (type input))
(pin (num 12) (name A0) (type input))
(pin (num 13) (name GND) (type input))
(pin (num 14) (name AREF) (type input))
(pin (num 15) (name +3V3) (type input))
(pin (num 16) (name RST) (type input))
(pin (num 17) (name VBAT) (type input))
(pin (num 18) (name EN) (type input))
(pin (num 19) (name VBUS) (type input))
(pin (num 20) (name 13) (type input))
(pin (num 21) (name 12) (type input))
(pin (num 22) (name 11) (type input))
(pin (num 23) (name 10) (type input))
(pin (num 24) (name 9) (type input))
(pin (num 25) (name 6) (type input))
(pin (num 26) (name 5) (type input))
(pin (num 27) (name 3) (type input))
(pin (num 28) (name 2) (type input))))
(libpart (lib SEN-13879) (part SEN-13879)
(fields
(field (name Reference) U)
(field (name Value) SEN-13879)
(field (name Footprint) SPARKFUN_SEN-13879)
(field (name Datasheet) "Load Cell Amp Hx711")
(field (name Field4) SparkFun)
(field (name Field5) Unavailable)
(field (name Field6) SEN-13879)
(field (name Field7) None)
(field (name Field8) None))
(pins
(pin (num 1) (name RED) (type BiDi))
(pin (num 2) (name BLK) (type BiDi))
(pin (num 3) (name WHT) (type input))
(pin (num 4) (name GRN) (type input))
(pin (num 5) (name YLW) (type BiDi))
(pin (num 6) (name GND) (type power_in))
(pin (num 7) (name CLK) (type input))
(pin (num 8) (name DAT) (type output))
(pin (num 9) (name VCC) (type power_in))
(pin (num 10) (name VDD) (type power_in))
(pin (num 11) (name B+) (type input))
(pin (num 12) (name B-) (type input))))
(libpart (lib T4145015051-001) (part T4145015051-001)
(fields
(field (name Reference) J)
(field (name Value) T4145015051-001)
(field (name Footprint) T4145015051-001)
(field (name Datasheet) "Circular Metric Connectors; M12 R/A F PNLREAR STAMPED PIN A-CODE 5P")
(field (name Field4) https://www.te.com/usa-en/product-T4145015051-001.html?te_bu=Cor&te_type=disp&te_campaign=seda_glo_cor-seda-global-disp-prtnr-fy19-seda-model-bom-cta_sma-317_1&elqCampaignId=32493)
(field (name Field5) "TE Connectivity")
(field (name Field6) Unavailable)
(field (name Field7) None)
(field (name Field8) T4145015051-001)
(field (name Field9) T4145015051-001)
(field (name Field10) None))
(pins
(pin (num P$1) (name 1) (type BiDi))
(pin (num P$2) (name 2) (type BiDi))
(pin (num P$3) (name 3) (type BiDi))
(pin (num P$4) (name 4) (type BiDi))
(pin (num P$5) (name 5) (type BiDi)))))
(libraries
(library (logical Connector)
(uri /usr/share/kicad/library/Connector.lib))
(library (logical Connector_Generic)
(uri /usr/share/kicad/library/Connector_Generic.lib))
(library (logical MiscellaneousDevices)
(uri /home/joerg/git-repos/KiCad-Libraries/symbols/MiscellaneousDevices.lib))
(library (logical SEN-13879)
(uri /home/joerg/Downloads/SEN-13879.lib))
(library (logical T4145015051-001)
(uri /home/joerg/Downloads/T4145015051-001.lib)))
(nets
(net (code 1) (name "Net-(J3-Pad1)")
(node (ref J3) (pin 1))
(node (ref U1) (pin 15)))
(net (code 2) (name "Net-(BAT1-Pad1)")
(node (ref SWITCH1) (pin 1))
(node (ref BAT1) (pin 1)))
(net (code 3) (name "Net-(SWITCH1-Pad2)")
(node (ref U1) (pin 17))
(node (ref SWITCH1) (pin 2)))
(net (code 4) (name "Net-(U1-Pad19)")
(node (ref U1) (pin 19)))
(net (code 5) (name "Net-(J3-Pad5)")
(node (ref J3) (pin 5))
(node (ref U1) (pin 16)))
(net (code 6) (name "Net-(J3-Pad3)")
(node (ref J3) (pin 3))
(node (ref U1) (pin 25)))
(net (code 7) (name "Net-(HX711-2-Pad4)")
(node (ref HX711-2) (pin 4))
(node (ref J2) (pin P$4)))
(net (code 8) (name "Net-(HX711-2-Pad1)")
(node (ref J2) (pin P$1))
(node (ref HX711-2) (pin 1)))
(net (code 9) (name "Net-(HX711-2-Pad2)")
(node (ref J2) (pin P$2))
(node (ref HX711-2) (pin 2)))
(net (code 10) (name "Net-(HX711-2-Pad3)")
(node (ref HX711-2) (pin 3))
(node (ref J2) (pin P$3)))
(net (code 11) (name "Net-(HX711-2-Pad5)")
(node (ref HX711-2) (pin 5))
(node (ref J2) (pin P$5)))
(net (code 12) (name "Net-(U1-Pad6)")
(node (ref U1) (pin 6)))
(net (code 13) (name "Net-(U1-Pad7)")
(node (ref U1) (pin 7)))
(net (code 14) (name "Net-(U1-Pad9)")
(node (ref U1) (pin 9)))
(net (code 15) (name "Net-(HX711-2-Pad8)")
(node (ref HX711-2) (pin 8))
(node (ref U1) (pin 21)))
(net (code 16) (name "Net-(J4-Pad1)")
(node (ref J4) (pin 1))
(node (ref U1) (pin 3)))
(net (code 17) (name "Net-(J4-Pad2)")
(node (ref J4) (pin 2))
(node (ref U1) (pin 2)))
(net (code 18) (name "Net-(U1-Pad5)")
(node (ref U1) (pin 5)))
(net (code 19) (name "Net-(U1-Pad23)")
(node (ref U1) (pin 23)))
(net (code 20) (name "Net-(J3-Pad4)")
(node (ref U1) (pin 24))
(node (ref J3) (pin 4)))
(net (code 21) (name "Net-(U1-Pad8)")
(node (ref U1) (pin 8)))
(net (code 22) (name "Net-(U1-Pad1)")
(node (ref U1) (pin 1)))
(net (code 23) (name "Net-(U1-Pad4)")
(node (ref U1) (pin 4)))
(net (code 24) (name "Net-(HX711-1-Pad7)")
(node (ref HX711-1) (pin 7))
(node (ref U1) (pin 12)))
(net (code 25) (name "Net-(U1-Pad14)")
(node (ref U1) (pin 14)))
(net (code 26) (name "Net-(U1-Pad20)")
(node (ref U1) (pin 20)))
(net (code 27) (name "Net-(U1-Pad22)")
(node (ref U1) (pin 22)))
(net (code 28) (name "Net-(U1-Pad26)")
(node (ref U1) (pin 26)))
(net (code 29) (name "Net-(U1-Pad27)")
(node (ref U1) (pin 27)))
(net (code 30) (name "Net-(U1-Pad28)")
(node (ref U1) (pin 28)))
(net (code 31) (name "Net-(BAT1-Pad2)")
(node (ref HX711-1) (pin 6))
(node (ref BAT1) (pin 2))
(node (ref J3) (pin 2))
(node (ref HX711-2) (pin 6))
(node (ref U1) (pin 13))
(node (ref J4) (pin 3)))
(net (code 32) (name "Net-(HX711-1-Pad10)")
(node (ref HX711-2) (pin 9))
(node (ref HX711-2) (pin 10))
(node (ref HX711-1) (pin 9))
(node (ref HX711-1) (pin 10))
(node (ref VOUT1) (pin 1)))
(net (code 33) (name "Net-(HX711-2-Pad11)")
(node (ref HX711-2) (pin 11)))
(net (code 34) (name "Net-(HX711-2-Pad12)")
(node (ref HX711-2) (pin 12)))
(net (code 35) (name "Net-(HX711-1-Pad3)")
(node (ref J1) (pin P$3))
(node (ref HX711-1) (pin 3)))
(net (code 36) (name "Net-(HX711-1-Pad4)")
(node (ref J1) (pin P$4))
(node (ref HX711-1) (pin 4)))
(net (code 37) (name "Net-(HX711-1-Pad5)")
(node (ref HX711-1) (pin 5))
(node (ref J1) (pin P$5)))
(net (code 38) (name "Net-(HX711-1-Pad8)")
(node (ref U1) (pin 11))
(node (ref HX711-1) (pin 8)))
(net (code 39) (name "Net-(HX711-1-Pad11)")
(node (ref HX711-1) (pin 11)))
(net (code 40) (name "Net-(HX711-1-Pad12)")
(node (ref HX711-1) (pin 12)))
(net (code 41) (name "Net-(HX711-1-Pad2)")
(node (ref HX711-1) (pin 2))
(node (ref J1) (pin P$2)))
(net (code 42) (name "Net-(HX711-1-Pad1)")
(node (ref HX711-1) (pin 1))
(node (ref J1) (pin P$1)))
(net (code 43) (name "Net-(HX711-2-Pad7)")
(node (ref HX711-2) (pin 7))
(node (ref U1) (pin 10)))
(net (code 44) (name "Net-(U1-Pad18)")
(node (ref U1) (pin 18)))))

View File

@ -0,0 +1,248 @@
update=Mo 27 Mai 2019 17:30:58 CEST
version=1
last_client=kicad
[general]
version=1
RootSch=
BoardNm=
[cvpcb]
version=1
NetIExt=net
[eeschema]
version=1
LibDir=
[eeschema/libraries]
[schematic_editor]
version=1
PageLayoutDescrFile=empty.kicad_wks
PlotDirectoryName=
SubpartIdSeparator=0
SubpartFirstId=65
NetFmtName=Pcbnew
SpiceAjustPassiveValues=0
LabSize=50
ERC_TestSimilarLabels=1
[pcbnew]
version=1
PageLayoutDescrFile=
LastNetListRead=beieliscale-kicad.net
CopperLayerCount=2
BoardThickness=1.6
AllowMicroVias=0
AllowBlindVias=0
RequireCourtyardDefinitions=0
ProhibitOverlappingCourtyards=1
MinTrackWidth=0.2
MinViaDiameter=0.4
MinViaDrill=0.3
MinMicroViaDiameter=0.2
MinMicroViaDrill=0.09999999999999999
MinHoleToHole=0.25
TrackWidth1=0.25
ViaDiameter1=0.8
ViaDrill1=0.4
dPairWidth1=0.2
dPairGap1=0.25
dPairViaGap1=0.25
SilkLineWidth=0.15
SilkTextSizeV=1
SilkTextSizeH=1
SilkTextSizeThickness=0.15
SilkTextItalic=0
SilkTextUpright=1
CopperLineWidth=0.2
CopperTextSizeV=1.5
CopperTextSizeH=1.5
CopperTextThickness=0.3
CopperTextItalic=0
CopperTextUpright=1
EdgeCutLineWidth=0.15
CourtyardLineWidth=0.05
OthersLineWidth=0.15
OthersTextSizeV=1
OthersTextSizeH=1
OthersTextSizeThickness=0.15
OthersTextItalic=0
OthersTextUpright=1
SolderMaskClearance=0.051
SolderMaskMinWidth=0.25
SolderPasteClearance=0
SolderPasteRatio=0
[pcbnew/Layer.F.Cu]
Name=F.Cu
Type=0
Enabled=1
[pcbnew/Layer.In1.Cu]
Name=In1.Cu
Type=0
Enabled=0
[pcbnew/Layer.In2.Cu]
Name=In2.Cu
Type=0
Enabled=0
[pcbnew/Layer.In3.Cu]
Name=In3.Cu
Type=0
Enabled=0
[pcbnew/Layer.In4.Cu]
Name=In4.Cu
Type=0
Enabled=0
[pcbnew/Layer.In5.Cu]
Name=In5.Cu
Type=0
Enabled=0
[pcbnew/Layer.In6.Cu]
Name=In6.Cu
Type=0
Enabled=0
[pcbnew/Layer.In7.Cu]
Name=In7.Cu
Type=0
Enabled=0
[pcbnew/Layer.In8.Cu]
Name=In8.Cu
Type=0
Enabled=0
[pcbnew/Layer.In9.Cu]
Name=In9.Cu
Type=0
Enabled=0
[pcbnew/Layer.In10.Cu]
Name=In10.Cu
Type=0
Enabled=0
[pcbnew/Layer.In11.Cu]
Name=In11.Cu
Type=0
Enabled=0
[pcbnew/Layer.In12.Cu]
Name=In12.Cu
Type=0
Enabled=0
[pcbnew/Layer.In13.Cu]
Name=In13.Cu
Type=0
Enabled=0
[pcbnew/Layer.In14.Cu]
Name=In14.Cu
Type=0
Enabled=0
[pcbnew/Layer.In15.Cu]
Name=In15.Cu
Type=0
Enabled=0
[pcbnew/Layer.In16.Cu]
Name=In16.Cu
Type=0
Enabled=0
[pcbnew/Layer.In17.Cu]
Name=In17.Cu
Type=0
Enabled=0
[pcbnew/Layer.In18.Cu]
Name=In18.Cu
Type=0
Enabled=0
[pcbnew/Layer.In19.Cu]
Name=In19.Cu
Type=0
Enabled=0
[pcbnew/Layer.In20.Cu]
Name=In20.Cu
Type=0
Enabled=0
[pcbnew/Layer.In21.Cu]
Name=In21.Cu
Type=0
Enabled=0
[pcbnew/Layer.In22.Cu]
Name=In22.Cu
Type=0
Enabled=0
[pcbnew/Layer.In23.Cu]
Name=In23.Cu
Type=0
Enabled=0
[pcbnew/Layer.In24.Cu]
Name=In24.Cu
Type=0
Enabled=0
[pcbnew/Layer.In25.Cu]
Name=In25.Cu
Type=0
Enabled=0
[pcbnew/Layer.In26.Cu]
Name=In26.Cu
Type=0
Enabled=0
[pcbnew/Layer.In27.Cu]
Name=In27.Cu
Type=0
Enabled=0
[pcbnew/Layer.In28.Cu]
Name=In28.Cu
Type=0
Enabled=0
[pcbnew/Layer.In29.Cu]
Name=In29.Cu
Type=0
Enabled=0
[pcbnew/Layer.In30.Cu]
Name=In30.Cu
Type=0
Enabled=0
[pcbnew/Layer.B.Cu]
Name=B.Cu
Type=0
Enabled=1
[pcbnew/Layer.B.Adhes]
Enabled=1
[pcbnew/Layer.F.Adhes]
Enabled=1
[pcbnew/Layer.B.Paste]
Enabled=1
[pcbnew/Layer.F.Paste]
Enabled=1
[pcbnew/Layer.B.SilkS]
Enabled=1
[pcbnew/Layer.F.SilkS]
Enabled=1
[pcbnew/Layer.B.Mask]
Enabled=1
[pcbnew/Layer.F.Mask]
Enabled=1
[pcbnew/Layer.Dwgs.User]
Enabled=1
[pcbnew/Layer.Cmts.User]
Enabled=1
[pcbnew/Layer.Eco1.User]
Enabled=1
[pcbnew/Layer.Eco2.User]
Enabled=1
[pcbnew/Layer.Edge.Cuts]
Enabled=1
[pcbnew/Layer.Margin]
Enabled=1
[pcbnew/Layer.B.CrtYd]
Enabled=1
[pcbnew/Layer.F.CrtYd]
Enabled=1
[pcbnew/Layer.B.Fab]
Enabled=1
[pcbnew/Layer.F.Fab]
Enabled=1
[pcbnew/Layer.Rescue]
Enabled=0
[pcbnew/Netclasses]
[pcbnew/Netclasses/Default]
Name=Default
Clearance=0.2
TrackWidth=0.25
ViaDiameter=0.8
ViaDrill=0.4
uViaDiameter=0.3
uViaDrill=0.1
dPairWidth=0.2
dPairGap=0.25
dPairViaGap=0.25

View File

@ -0,0 +1,406 @@
EESchema Schematic File Version 4
LIBS:beieliscale-kicad-cache
EELAYER 29 0
EELAYER END
$Descr A4 11693 8268
encoding utf-8
Sheet 1 1
Title ""
Date ""
Rev ""
Comp ""
Comment1 ""
Comment2 ""
Comment3 ""
Comment4 ""
$EndDescr
$Comp
L SEN-13879:SEN-13879 HX711-1
U 1 1 5C8AE7C5
P 4550 3350
F 0 "HX711-1" H 4550 4117 50 0000 C CNN
F 1 "SEN-13879" H 4550 4026 50 0000 C CNN
F 2 "SPARKFUN_SEN-13879" H 4550 3350 50 0001 L BNN
F 3 "Load Cell Amp Hx711" H 4550 3350 50 0001 L BNN
F 4 "SparkFun" H 4550 3350 50 0001 L BNN "Field4"
F 5 "Unavailable" H 4550 3350 50 0001 L BNN "Field5"
F 6 "SEN-13879" H 4550 3350 50 0001 L BNN "Field6"
F 7 "None" H 4550 3350 50 0001 L BNN "Field7"
F 8 "None" H 4550 3350 50 0001 L BNN "Field8"
1 4550 3350
1 0 0 -1
$EndComp
Wire Wire Line
6700 3550 6700 3150
Wire Wire Line
6700 3150 6850 3150
Wire Wire Line
5050 3550 6700 3550
Wire Wire Line
5050 3450 6750 3450
Wire Wire Line
6750 3450 6750 3250
Wire Wire Line
6750 3250 6850 3250
NoConn ~ 6850 2950
NoConn ~ 4050 3650
NoConn ~ 4050 3750
Wire Wire Line
5050 2950 5600 2950
Wire Wire Line
5600 2950 5600 2850
$Comp
L power:PWR_FLAG #FLG0101
U 1 1 5C8B848F
P 6000 2850
F 0 "#FLG0101" H 6000 2925 50 0001 C CNN
F 1 "PWR_FLAG" H 6000 3024 50 0000 C CNN
F 2 "" H 6000 2850 50 0001 C CNN
F 3 "~" H 6000 2850 50 0001 C CNN
1 6000 2850
1 0 0 -1
$EndComp
Wire Wire Line
5050 2850 5550 2850
Connection ~ 5600 2850
Wire Wire Line
5500 3850 5500 3050
NoConn ~ 8700 3050
NoConn ~ 8700 3250
NoConn ~ 8700 3650
NoConn ~ 8700 3750
NoConn ~ 8700 3850
$Comp
L power:PWR_FLAG #FLG0102
U 1 1 5C8C2453
P 5250 3850
F 0 "#FLG0102" H 5250 3925 50 0001 C CNN
F 1 "PWR_FLAG" H 5250 4024 50 0000 C CNN
F 2 "" H 5250 3850 50 0001 C CNN
F 3 "~" H 5250 3850 50 0001 C CNN
1 5250 3850
1 0 0 -1
$EndComp
Connection ~ 5250 3850
$Comp
L SEN-13879:SEN-13879 HX711-2
U 1 1 5C8CD302
P 4550 5100
F 0 "HX711-2" H 4550 5867 50 0000 C CNN
F 1 "SEN-13879" H 4550 5776 50 0000 C CNN
F 2 "SPARKFUN_SEN-13879" H 4550 5100 50 0001 L BNN
F 3 "Load Cell Amp Hx711" H 4550 5100 50 0001 L BNN
F 4 "SparkFun" H 4550 5100 50 0001 L BNN "Field4"
F 5 "Unavailable" H 4550 5100 50 0001 L BNN "Field5"
F 6 "SEN-13879" H 4550 5100 50 0001 L BNN "Field6"
F 7 "None" H 4550 5100 50 0001 L BNN "Field7"
F 8 "None" H 4550 5100 50 0001 L BNN "Field8"
1 4550 5100
1 0 0 -1
$EndComp
NoConn ~ 4050 5400
NoConn ~ 4050 5500
Wire Wire Line
5050 4600 5600 4600
Wire Wire Line
5600 4600 5600 2950
Connection ~ 5600 2950
Wire Wire Line
5050 4700 5600 4700
Wire Wire Line
5600 4700 5600 4600
Connection ~ 5600 4600
Wire Wire Line
5050 5600 5250 5600
Wire Wire Line
5250 5600 5250 3850
Wire Wire Line
6650 5300 6650 3350
Wire Wire Line
6650 3350 6850 3350
Wire Wire Line
5050 5300 6650 5300
$Comp
L MiscellaneousDevices:ADAFRUIT_FEATHER U1
U 1 1 5C8BFC62
P 8950 3300
F 0 "U1" H 9128 3353 60 0000 L CNN
F 1 "MCCI 4610" H 9128 3247 60 0000 L CNN
F 2 "" H 9100 3100 60 0000 C CNN
F 3 "" H 9100 3100 60 0000 C CNN
1 8950 3300
1 0 0 -1
$EndComp
NoConn ~ 8700 2850
$Comp
L Connector:Conn_01x02_Female BAT1
U 1 1 5C92F7C2
P 4350 1900
F 0 "BAT1" H 4244 1575 50 0000 C CNN
F 1 "Conn_01x02_Female" H 4244 1666 50 0000 C CNN
F 2 "Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical" H 4350 1900 50 0001 C CNN
F 3 "~" H 4350 1900 50 0001 C CNN
1 4350 1900
-1 0 0 1
$EndComp
Wire Wire Line
8150 1900 8150 2750
Wire Wire Line
8150 2750 8700 2750
Wire Wire Line
5250 3850 5500 3850
$Comp
L T4145015051-001:T4145015051-001 J2
U 1 1 5CA65D1D
P 2950 5000
F 0 "J2" V 2483 5050 50 0000 C CNN
F 1 "T4145015051-001" V 2574 5050 50 0000 C CNN
F 2 "T4145015051-001" H 2950 5000 50 0001 L BNN
F 3 "Circular Metric Connectors; M12 R/A F PNLREAR STAMPED PIN A-CODE 5P" H 2950 5000 50 0001 L BNN
F 4 "https://www.te.com/usa-en/product-T4145015051-001.html?te_bu=Cor&te_type=disp&te_campaign=seda_glo_cor-seda-global-disp-prtnr-fy19-seda-model-bom-cta_sma-317_1&elqCampaignId=32493" H 2950 5000 50 0001 L BNN "Field4"
F 5 "TE Connectivity" H 2950 5000 50 0001 L BNN "Field5"
F 6 "Unavailable" H 2950 5000 50 0001 L BNN "Field6"
F 7 "None" H 2950 5000 50 0001 L BNN "Field7"
F 8 "T4145015051-001" H 2950 5000 50 0001 L BNN "Field8"
F 9 "T4145015051-001" H 2950 5000 50 0001 L BNN "Field9"
F 10 "None" H 2950 5000 50 0001 L BNN "Field10"
1 2950 5000
0 1 1 0
$EndComp
$Comp
L T4145015051-001:T4145015051-001 J1
U 1 1 5CA66C31
P 2950 3250
F 0 "J1" V 2483 3300 50 0000 C CNN
F 1 "T4145015051-001" V 2574 3300 50 0000 C CNN
F 2 "T4145015051-001" H 2950 3250 50 0001 L BNN
F 3 "Circular Metric Connectors; M12 R/A F PNLREAR STAMPED PIN A-CODE 5P" H 2950 3250 50 0001 L BNN
F 4 "https://www.te.com/usa-en/product-T4145015051-001.html?te_bu=Cor&te_type=disp&te_campaign=seda_glo_cor-seda-global-disp-prtnr-fy19-seda-model-bom-cta_sma-317_1&elqCampaignId=32493" H 2950 3250 50 0001 L BNN "Field4"
F 5 "TE Connectivity" H 2950 3250 50 0001 L BNN "Field5"
F 6 "Unavailable" H 2950 3250 50 0001 L BNN "Field6"
F 7 "None" H 2950 3250 50 0001 L BNN "Field7"
F 8 "T4145015051-001" H 2950 3250 50 0001 L BNN "Field8"
F 9 "T4145015051-001" H 2950 3250 50 0001 L BNN "Field9"
F 10 "None" H 2950 3250 50 0001 L BNN "Field10"
1 2950 3250
0 1 1 0
$EndComp
Wire Wire Line
4050 3050 3350 3050
Wire Wire Line
4050 3150 3450 3150
Wire Wire Line
3450 3150 3450 3450
Wire Wire Line
3450 3450 3350 3450
Wire Wire Line
4050 3250 3650 3250
Wire Wire Line
3650 3250 3650 3650
Wire Wire Line
3650 3650 2650 3650
Wire Wire Line
2650 3650 2650 3450
Wire Wire Line
4050 3350 3750 3350
Wire Wire Line
3750 3350 3750 3800
Wire Wire Line
3750 3800 2450 3800
Wire Wire Line
2450 3800 2450 3050
Wire Wire Line
2450 3050 2650 3050
Wire Wire Line
4050 3450 3850 3450
Wire Wire Line
3850 3450 3850 3900
Wire Wire Line
3850 3900 3250 3900
Wire Wire Line
3250 3900 3250 3250
Wire Wire Line
4050 4800 3350 4800
Wire Wire Line
4050 4900 3500 4900
Wire Wire Line
3500 4900 3500 5200
Wire Wire Line
3500 5200 3350 5200
Wire Wire Line
4050 5000 3600 5000
Wire Wire Line
3600 5000 3600 5400
Wire Wire Line
3600 5400 2500 5400
Wire Wire Line
2500 5400 2500 5200
Wire Wire Line
2500 5200 2650 5200
Wire Wire Line
4050 5100 3700 5100
Wire Wire Line
3700 5100 3700 5550
Wire Wire Line
3700 5550 2400 5550
Wire Wire Line
2400 5550 2400 4800
Wire Wire Line
2400 4800 2650 4800
Wire Wire Line
3750 5200 3750 5600
Wire Wire Line
3750 5600 3250 5600
Wire Wire Line
3250 5600 3250 5000
Wire Wire Line
3750 5200 4050 5200
Wire Wire Line
4550 1800 5150 1800
Wire Wire Line
5050 3850 5150 3850
Wire Wire Line
5150 3850 5250 3850
Connection ~ 5150 3850
Wire Wire Line
5150 1800 5150 3850
$Comp
L Connector:Conn_01x02_Female SWITCH1
U 1 1 5CAD5AEF
P 6300 1700
F 0 "SWITCH1" H 6194 1375 50 0000 C CNN
F 1 "Conn_01x02_Female" H 6194 1466 50 0000 C CNN
F 2 "Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical" H 6300 1700 50 0001 C CNN
F 3 "~" H 6300 1700 50 0001 C CNN
1 6300 1700
0 -1 -1 0
$EndComp
Wire Wire Line
6400 1900 8150 1900
Wire Wire Line
4550 1900 6300 1900
NoConn ~ 8700 2950
$Comp
L Connector:Conn_01x05_Female J3
U 1 1 5CBF85B5
P 6000 3850
F 0 "J3" H 6150 4050 50 0000 C CNN
F 1 "Conn_01x05_Female" H 5950 4150 50 0000 C CNN
F 2 "Connector_JST:JST_PH_B5B-PH-K_1x05_P2.00mm_Vertical" H 6000 3850 50 0001 C CNN
F 3 "~" H 6000 3850 50 0001 C CNN
1 6000 3850
-1 0 0 1
$EndComp
Wire Wire Line
6600 4050 6600 2850
Wire Wire Line
6600 2850 6850 2850
Wire Wire Line
6200 4050 6600 4050
Wire Wire Line
6200 3950 6500 3950
Wire Wire Line
6500 3950 6500 3050
Wire Wire Line
5500 3050 6500 3050
Connection ~ 6500 3050
Wire Wire Line
6500 3050 6850 3050
Wire Wire Line
6200 3650 6400 3650
Wire Wire Line
6400 3650 6400 2750
Wire Wire Line
6400 2750 6850 2750
Wire Wire Line
6200 3850 6400 3850
Wire Wire Line
6400 3850 6400 4450
Wire Wire Line
8500 4450 8500 3550
Wire Wire Line
8500 3550 8700 3550
Wire Wire Line
6200 3750 6550 3750
Wire Wire Line
6550 3750 6550 4350
Wire Wire Line
8400 4350 8400 3450
Wire Wire Line
8400 3450 8700 3450
Wire Wire Line
6550 4350 8400 4350
Wire Wire Line
6400 4450 8500 4450
NoConn ~ 6850 3550
$Comp
L MiscellaneousDevices:ADAFRUIT_FEATHER U1
U 2 1 5C8C025E
P 7100 3500
F 0 "U1" H 7278 3606 60 0000 L CNN
F 1 "MCCI 4610" H 7278 3500 60 0000 L CNN
F 2 "Modules:ADAFRUIT_FEATHER" H 7278 3394 60 0000 L CNN
F 3 "" H 7250 3300 60 0000 C CNN
2 7100 3500
1 0 0 -1
$EndComp
NoConn ~ 6850 4250
NoConn ~ 6850 3950
NoConn ~ 6850 3850
NoConn ~ 6850 3750
NoConn ~ 6850 3650
NoConn ~ 6850 3450
Wire Wire Line
8600 5200 5050 5200
Wire Wire Line
8600 3150 8600 5200
Wire Wire Line
8600 3150 8700 3150
$Comp
L Connector:Conn_01x03_Male J4
U 1 1 5CFBD036
P 6000 4900
F 0 "J4" H 6108 5181 50 0000 C CNN
F 1 "Conn_01x03_Male" H 6108 5090 50 0000 C CNN
F 2 "Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical" H 6000 4900 50 0001 C CNN
F 3 "~" H 6000 4900 50 0001 C CNN
1 6000 4900
1 0 0 -1
$EndComp
Wire Wire Line
6200 5600 6200 5000
Wire Wire Line
6200 4800 6750 4800
Wire Wire Line
6750 4800 6750 4050
Wire Wire Line
6750 4050 6850 4050
Wire Wire Line
6200 4900 6800 4900
Wire Wire Line
6800 4900 6800 4150
Wire Wire Line
6800 4150 6850 4150
NoConn ~ 8700 3350
Wire Wire Line
5250 5600 6200 5600
Connection ~ 5250 5600
Wire Wire Line
5600 2850 6000 2850
$Comp
L Connector_Generic:Conn_01x01 VOUT1
U 1 1 5CFF362B
P 5750 2300
F 0 "VOUT1" H 5830 2342 50 0000 L CNN
F 1 "Conn_01x01" H 5830 2251 50 0000 L CNN
F 2 "Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical" H 5750 2300 50 0001 C CNN
F 3 "~" H 5750 2300 50 0001 C CNN
1 5750 2300
1 0 0 -1
$EndComp
Wire Wire Line
5550 2300 5550 2850
Connection ~ 5550 2850
Wire Wire Line
5550 2850 5600 2850
$EndSCHEMATC

View File

@ -0,0 +1,5 @@
(page_layout
(setup (textsize 1.5 1.5)(linewidth 0.15)(textlinewidth 0.15)
(left_margin 10)(right_margin 10)(top_margin 10)(bottom_margin 10))
(line (name segm1:Line) (start 0 0) (end 0 0))
)

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,5.0.2-5.fc29*
G04 #@! TF.CreationDate,2019-03-22T17:39:01+01:00*
G04 #@! TF.ProjectId,beieliscale-kicad,62656965-6c69-4736-9361-6c652d6b6963,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Paste,Bot*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 5.0.2-5.fc29) date Fr 22 Mär 2019 17:39:01 CET*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 APERTURE END LIST*
M02*

View File

@ -0,0 +1,15 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,5.0.2-5.fc29*
G04 #@! TF.CreationDate,2019-03-22T17:39:01+01:00*
G04 #@! TF.ProjectId,beieliscale-kicad,62656965-6c69-4736-9361-6c652d6b6963,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Legend,Bot*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 5.0.2-5.fc29) date Fr 22 Mär 2019 17:39:01 CET*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 APERTURE END LIST*
M02*

View File

@ -0,0 +1,112 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,5.0.2-5.fc29*
G04 #@! TF.CreationDate,2019-03-22T17:39:01+01:00*
G04 #@! TF.ProjectId,beieliscale-kicad,62656965-6c69-4736-9361-6c652d6b6963,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Profile,NP*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 5.0.2-5.fc29) date Fr 22 Mär 2019 17:39:01 CET*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%ADD10C,0.150000*%
G04 #@! TA.AperFunction,NonConductor*
%ADD11C,0.100000*%
G04 #@! TD*
G04 #@! TA.AperFunction,NonConductor*
%ADD12C,0.000100*%
G04 #@! TD*
G04 APERTURE END LIST*
D10*
X74930000Y-49900000D02*
X74930000Y-99900000D01*
X194000000Y-49900000D02*
X74930000Y-49900000D01*
X194000000Y-99900000D02*
X194000000Y-49900000D01*
X74930000Y-99900000D02*
X194000000Y-99900000D01*
D11*
G04 #@! TO.C,J1*
X192765000Y-93180000D02*
X192765000Y-92180000D01*
X192765000Y-92180000D02*
X190565000Y-92180000D01*
X190565000Y-92180000D02*
X190565000Y-93180000D01*
X190565000Y-93180000D02*
X192765000Y-93180000D01*
X192765000Y-80380000D02*
X192765000Y-79380000D01*
X192765000Y-79380000D02*
X190565000Y-79380000D01*
X190565000Y-79380000D02*
X190565000Y-80380000D01*
X190565000Y-80380000D02*
X192765000Y-80380000D01*
G04 #@! TO.C,J2*
X78240000Y-92180000D02*
X76040000Y-92180000D01*
X78240000Y-93180000D02*
X78240000Y-92180000D01*
X76040000Y-93180000D02*
X78240000Y-93180000D01*
X76040000Y-92180000D02*
X76040000Y-93180000D01*
X78240000Y-79380000D02*
X76040000Y-79380000D01*
X78240000Y-80380000D02*
X78240000Y-79380000D01*
X76040000Y-80380000D02*
X78240000Y-80380000D01*
X76040000Y-79380000D02*
X76040000Y-80380000D01*
G04 #@! TO.C,J3*
X80090000Y-62685000D02*
X77390000Y-62685000D01*
X77390000Y-62685000D02*
G75*
G02X77390000Y-61685000I0J500000D01*
G01*
X77390000Y-61685000D02*
X80090000Y-61685000D01*
X80090000Y-61685000D02*
G75*
G02X80090000Y-62685000I0J-500000D01*
G01*
X80090000Y-71185000D02*
X77390000Y-71185000D01*
X77390000Y-71185000D02*
G75*
G02X77390000Y-70185000I0J500000D01*
G01*
X77390000Y-70185000D02*
X80090000Y-70185000D01*
X80090000Y-70185000D02*
G75*
G02X80090000Y-71185000I0J-500000D01*
G01*
D12*
X79890000Y-66435000D02*
G75*
G03X79890000Y-66435000I-350000J0D01*
G01*
X79890000Y-65135000D02*
G75*
G03X79890000Y-65135000I-350000J0D01*
G01*
X79890000Y-67735000D02*
G75*
G03X79890000Y-67735000I-350000J0D01*
G01*
X78290000Y-67085000D02*
G75*
G03X78290000Y-67085000I-350000J0D01*
G01*
X78290000Y-65785000D02*
G75*
G03X78290000Y-65785000I-350000J0D01*
G01*
G04 #@! TD*
M02*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,5.0.2-5.fc29*
G04 #@! TF.CreationDate,2019-03-22T17:39:01+01:00*
G04 #@! TF.ProjectId,beieliscale-kicad,62656965-6c69-4736-9361-6c652d6b6963,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Paste,Top*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 5.0.2-5.fc29) date Fr 22 Mär 2019 17:39:01 CET*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 APERTURE END LIST*
M02*

View File

@ -0,0 +1,912 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,5.0.2-5.fc29*
G04 #@! TF.CreationDate,2019-03-22T17:39:01+01:00*
G04 #@! TF.ProjectId,beieliscale-kicad,62656965-6c69-4736-9361-6c652d6b6963,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Legend,Top*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 5.0.2-5.fc29) date Fr 22 Mär 2019 17:39:01 CET*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%ADD10C,0.200000*%
%ADD11C,0.150000*%
%ADD12C,0.127000*%
%ADD13C,0.300000*%
%ADD14C,0.100000*%
%ADD15C,0.120000*%
G04 APERTURE END LIST*
D10*
X123240952Y-54632380D02*
X123240952Y-53965714D01*
X123240952Y-54060952D02*
X123288571Y-54013333D01*
X123383809Y-53965714D01*
X123526666Y-53965714D01*
X123621904Y-54013333D01*
X123669523Y-54108571D01*
X123669523Y-54632380D01*
X123669523Y-54108571D02*
X123717142Y-54013333D01*
X123812380Y-53965714D01*
X123955238Y-53965714D01*
X124050476Y-54013333D01*
X124098095Y-54108571D01*
X124098095Y-54632380D01*
X124574285Y-54632380D02*
X124574285Y-53965714D01*
X124574285Y-53632380D02*
X124526666Y-53680000D01*
X124574285Y-53727619D01*
X124621904Y-53680000D01*
X124574285Y-53632380D01*
X124574285Y-53727619D01*
X125050476Y-53965714D02*
X125050476Y-54632380D01*
X125050476Y-54060952D02*
X125098095Y-54013333D01*
X125193333Y-53965714D01*
X125336190Y-53965714D01*
X125431428Y-54013333D01*
X125479047Y-54108571D01*
X125479047Y-54632380D01*
X125955238Y-54632380D02*
X125955238Y-53965714D01*
X125955238Y-53632380D02*
X125907619Y-53680000D01*
X125955238Y-53727619D01*
X126002857Y-53680000D01*
X125955238Y-53632380D01*
X125955238Y-53727619D01*
X126431428Y-54251428D02*
X127193333Y-54251428D01*
X127669523Y-54632380D02*
X127669523Y-53632380D01*
X127669523Y-54013333D02*
X127764761Y-53965714D01*
X127955238Y-53965714D01*
X128050476Y-54013333D01*
X128098095Y-54060952D01*
X128145714Y-54156190D01*
X128145714Y-54441904D01*
X128098095Y-54537142D01*
X128050476Y-54584761D01*
X127955238Y-54632380D01*
X127764761Y-54632380D01*
X127669523Y-54584761D01*
X128955238Y-54584761D02*
X128860000Y-54632380D01*
X128669523Y-54632380D01*
X128574285Y-54584761D01*
X128526666Y-54489523D01*
X128526666Y-54108571D01*
X128574285Y-54013333D01*
X128669523Y-53965714D01*
X128860000Y-53965714D01*
X128955238Y-54013333D01*
X129002857Y-54108571D01*
X129002857Y-54203809D01*
X128526666Y-54299047D01*
X129431428Y-54632380D02*
X129431428Y-53965714D01*
X129431428Y-53632380D02*
X129383809Y-53680000D01*
X129431428Y-53727619D01*
X129479047Y-53680000D01*
X129431428Y-53632380D01*
X129431428Y-53727619D01*
X130288571Y-54584761D02*
X130193333Y-54632380D01*
X130002857Y-54632380D01*
X129907619Y-54584761D01*
X129860000Y-54489523D01*
X129860000Y-54108571D01*
X129907619Y-54013333D01*
X130002857Y-53965714D01*
X130193333Y-53965714D01*
X130288571Y-54013333D01*
X130336190Y-54108571D01*
X130336190Y-54203809D01*
X129860000Y-54299047D01*
X130907619Y-54632380D02*
X130812380Y-54584761D01*
X130764761Y-54489523D01*
X130764761Y-53632380D01*
X131288571Y-54632380D02*
X131288571Y-53965714D01*
X131288571Y-53632380D02*
X131240952Y-53680000D01*
X131288571Y-53727619D01*
X131336190Y-53680000D01*
X131288571Y-53632380D01*
X131288571Y-53727619D01*
X131764761Y-54537142D02*
X131812380Y-54584761D01*
X131764761Y-54632380D01*
X131717142Y-54584761D01*
X131764761Y-54537142D01*
X131764761Y-54632380D01*
X132669523Y-54584761D02*
X132574285Y-54632380D01*
X132383809Y-54632380D01*
X132288571Y-54584761D01*
X132240952Y-54537142D01*
X132193333Y-54441904D01*
X132193333Y-54156190D01*
X132240952Y-54060952D01*
X132288571Y-54013333D01*
X132383809Y-53965714D01*
X132574285Y-53965714D01*
X132669523Y-54013333D01*
X133098095Y-54632380D02*
X133098095Y-53632380D01*
X133526666Y-54632380D02*
X133526666Y-54108571D01*
X133479047Y-54013333D01*
X133383809Y-53965714D01*
X133240952Y-53965714D01*
X133145714Y-54013333D01*
X133098095Y-54060952D01*
X134764761Y-54251428D02*
X135526666Y-54251428D01*
X137098095Y-54108571D02*
X137240952Y-54156190D01*
X137288571Y-54203809D01*
X137336190Y-54299047D01*
X137336190Y-54441904D01*
X137288571Y-54537142D01*
X137240952Y-54584761D01*
X137145714Y-54632380D01*
X136764761Y-54632380D01*
X136764761Y-53632380D01*
X137098095Y-53632380D01*
X137193333Y-53680000D01*
X137240952Y-53727619D01*
X137288571Y-53822857D01*
X137288571Y-53918095D01*
X137240952Y-54013333D01*
X137193333Y-54060952D01*
X137098095Y-54108571D01*
X136764761Y-54108571D01*
X138145714Y-54584761D02*
X138050476Y-54632380D01*
X137860000Y-54632380D01*
X137764761Y-54584761D01*
X137717142Y-54489523D01*
X137717142Y-54108571D01*
X137764761Y-54013333D01*
X137860000Y-53965714D01*
X138050476Y-53965714D01*
X138145714Y-54013333D01*
X138193333Y-54108571D01*
X138193333Y-54203809D01*
X137717142Y-54299047D01*
X138621904Y-54632380D02*
X138621904Y-53965714D01*
X138621904Y-53632380D02*
X138574285Y-53680000D01*
X138621904Y-53727619D01*
X138669523Y-53680000D01*
X138621904Y-53632380D01*
X138621904Y-53727619D01*
X139479047Y-54584761D02*
X139383809Y-54632380D01*
X139193333Y-54632380D01*
X139098095Y-54584761D01*
X139050476Y-54489523D01*
X139050476Y-54108571D01*
X139098095Y-54013333D01*
X139193333Y-53965714D01*
X139383809Y-53965714D01*
X139479047Y-54013333D01*
X139526666Y-54108571D01*
X139526666Y-54203809D01*
X139050476Y-54299047D01*
X140098095Y-54632380D02*
X140002857Y-54584761D01*
X139955238Y-54489523D01*
X139955238Y-53632380D01*
X140479047Y-54632380D02*
X140479047Y-53965714D01*
X140479047Y-53632380D02*
X140431428Y-53680000D01*
X140479047Y-53727619D01*
X140526666Y-53680000D01*
X140479047Y-53632380D01*
X140479047Y-53727619D01*
X140907619Y-54584761D02*
X141050476Y-54632380D01*
X141288571Y-54632380D01*
X141383809Y-54584761D01*
X141431428Y-54537142D01*
X141479047Y-54441904D01*
X141479047Y-54346666D01*
X141431428Y-54251428D01*
X141383809Y-54203809D01*
X141288571Y-54156190D01*
X141098095Y-54108571D01*
X141002857Y-54060952D01*
X140955238Y-54013333D01*
X140907619Y-53918095D01*
X140907619Y-53822857D01*
X140955238Y-53727619D01*
X141002857Y-53680000D01*
X141098095Y-53632380D01*
X141336190Y-53632380D01*
X141479047Y-53680000D01*
X142336190Y-54584761D02*
X142240952Y-54632380D01*
X142050476Y-54632380D01*
X141955238Y-54584761D01*
X141907619Y-54537142D01*
X141860000Y-54441904D01*
X141860000Y-54156190D01*
X141907619Y-54060952D01*
X141955238Y-54013333D01*
X142050476Y-53965714D01*
X142240952Y-53965714D01*
X142336190Y-54013333D01*
X143193333Y-54632380D02*
X143193333Y-54108571D01*
X143145714Y-54013333D01*
X143050476Y-53965714D01*
X142860000Y-53965714D01*
X142764761Y-54013333D01*
X143193333Y-54584761D02*
X143098095Y-54632380D01*
X142860000Y-54632380D01*
X142764761Y-54584761D01*
X142717142Y-54489523D01*
X142717142Y-54394285D01*
X142764761Y-54299047D01*
X142860000Y-54251428D01*
X143098095Y-54251428D01*
X143193333Y-54203809D01*
X143812380Y-54632380D02*
X143717142Y-54584761D01*
X143669523Y-54489523D01*
X143669523Y-53632380D01*
X144574285Y-54584761D02*
X144479047Y-54632380D01*
X144288571Y-54632380D01*
X144193333Y-54584761D01*
X144145714Y-54489523D01*
X144145714Y-54108571D01*
X144193333Y-54013333D01*
X144288571Y-53965714D01*
X144479047Y-53965714D01*
X144574285Y-54013333D01*
X144621904Y-54108571D01*
X144621904Y-54203809D01*
X144145714Y-54299047D01*
X145812380Y-54632380D02*
X145812380Y-53632380D01*
X145812380Y-54013333D02*
X145907619Y-53965714D01*
X146098095Y-53965714D01*
X146193333Y-54013333D01*
X146240952Y-54060952D01*
X146288571Y-54156190D01*
X146288571Y-54441904D01*
X146240952Y-54537142D01*
X146193333Y-54584761D01*
X146098095Y-54632380D01*
X145907619Y-54632380D01*
X145812380Y-54584761D01*
X146621904Y-53965714D02*
X146860000Y-54632380D01*
X147098095Y-53965714D02*
X146860000Y-54632380D01*
X146764761Y-54870476D01*
X146717142Y-54918095D01*
X146621904Y-54965714D01*
X148240952Y-53965714D02*
X148240952Y-54632380D01*
X148240952Y-54060952D02*
X148288571Y-54013333D01*
X148383809Y-53965714D01*
X148526666Y-53965714D01*
X148621904Y-54013333D01*
X148669523Y-54108571D01*
X148669523Y-54632380D01*
X149145714Y-54632380D02*
X149145714Y-53632380D01*
X149145714Y-54013333D02*
X149240952Y-53965714D01*
X149431428Y-53965714D01*
X149526666Y-54013333D01*
X149574285Y-54060952D01*
X149621904Y-54156190D01*
X149621904Y-54441904D01*
X149574285Y-54537142D01*
X149526666Y-54584761D01*
X149431428Y-54632380D01*
X149240952Y-54632380D01*
X149145714Y-54584761D01*
X150050476Y-54632380D02*
X150050476Y-53965714D01*
X150050476Y-53632380D02*
X150002857Y-53680000D01*
X150050476Y-53727619D01*
X150098095Y-53680000D01*
X150050476Y-53632380D01*
X150050476Y-53727619D01*
X150383809Y-53965714D02*
X150764761Y-53965714D01*
X150526666Y-53632380D02*
X150526666Y-54489523D01*
X150574285Y-54584761D01*
X150669523Y-54632380D01*
X150764761Y-54632380D01*
X151860000Y-54632380D02*
X151860000Y-53632380D01*
X152336190Y-53965714D02*
X152336190Y-54632380D01*
X152336190Y-54060952D02*
X152383809Y-54013333D01*
X152479047Y-53965714D01*
X152621904Y-53965714D01*
X152717142Y-54013333D01*
X152764761Y-54108571D01*
X152764761Y-54632380D01*
X153098095Y-53965714D02*
X153479047Y-53965714D01*
X153240952Y-54632380D02*
X153240952Y-53775238D01*
X153288571Y-53680000D01*
X153383809Y-53632380D01*
X153479047Y-53632380D01*
X153955238Y-54632380D02*
X153860000Y-54584761D01*
X153812380Y-54537142D01*
X153764761Y-54441904D01*
X153764761Y-54156190D01*
X153812380Y-54060952D01*
X153860000Y-54013333D01*
X153955238Y-53965714D01*
X154098095Y-53965714D01*
X154193333Y-54013333D01*
X154240952Y-54060952D01*
X154288571Y-54156190D01*
X154288571Y-54441904D01*
X154240952Y-54537142D01*
X154193333Y-54584761D01*
X154098095Y-54632380D01*
X153955238Y-54632380D01*
X154717142Y-54632380D02*
X154717142Y-53965714D01*
X154717142Y-54156190D02*
X154764761Y-54060952D01*
X154812380Y-54013333D01*
X154907619Y-53965714D01*
X155002857Y-53965714D01*
X155336190Y-54632380D02*
X155336190Y-53965714D01*
X155336190Y-54060952D02*
X155383809Y-54013333D01*
X155479047Y-53965714D01*
X155621904Y-53965714D01*
X155717142Y-54013333D01*
X155764761Y-54108571D01*
X155764761Y-54632380D01*
X155764761Y-54108571D02*
X155812380Y-54013333D01*
X155907619Y-53965714D01*
X156050476Y-53965714D01*
X156145714Y-54013333D01*
X156193333Y-54108571D01*
X156193333Y-54632380D01*
X157098095Y-54632380D02*
X157098095Y-54108571D01*
X157050476Y-54013333D01*
X156955238Y-53965714D01*
X156764761Y-53965714D01*
X156669523Y-54013333D01*
X157098095Y-54584761D02*
X157002857Y-54632380D01*
X156764761Y-54632380D01*
X156669523Y-54584761D01*
X156621904Y-54489523D01*
X156621904Y-54394285D01*
X156669523Y-54299047D01*
X156764761Y-54251428D01*
X157002857Y-54251428D01*
X157098095Y-54203809D01*
X157431428Y-53965714D02*
X157812380Y-53965714D01*
X157574285Y-53632380D02*
X157574285Y-54489523D01*
X157621904Y-54584761D01*
X157717142Y-54632380D01*
X157812380Y-54632380D01*
X158145714Y-54632380D02*
X158145714Y-53965714D01*
X158145714Y-53632380D02*
X158098095Y-53680000D01*
X158145714Y-53727619D01*
X158193333Y-53680000D01*
X158145714Y-53632380D01*
X158145714Y-53727619D01*
X158621904Y-54632380D02*
X158621904Y-53632380D01*
X158717142Y-54251428D02*
X159002857Y-54632380D01*
X159002857Y-53965714D02*
X158621904Y-54346666D01*
X160717142Y-53680000D02*
X160621904Y-53632380D01*
X160479047Y-53632380D01*
X160336190Y-53680000D01*
X160240952Y-53775238D01*
X160193333Y-53870476D01*
X160145714Y-54060952D01*
X160145714Y-54203809D01*
X160193333Y-54394285D01*
X160240952Y-54489523D01*
X160336190Y-54584761D01*
X160479047Y-54632380D01*
X160574285Y-54632380D01*
X160717142Y-54584761D01*
X160764761Y-54537142D01*
X160764761Y-54203809D01*
X160574285Y-54203809D01*
X161193333Y-54632380D02*
X161193333Y-53965714D01*
X161193333Y-54060952D02*
X161240952Y-54013333D01*
X161336190Y-53965714D01*
X161479047Y-53965714D01*
X161574285Y-54013333D01*
X161621904Y-54108571D01*
X161621904Y-54632380D01*
X161621904Y-54108571D02*
X161669523Y-54013333D01*
X161764761Y-53965714D01*
X161907619Y-53965714D01*
X162002857Y-54013333D01*
X162050476Y-54108571D01*
X162050476Y-54632380D01*
X162526666Y-54632380D02*
X162526666Y-53632380D01*
X162526666Y-54013333D02*
X162621904Y-53965714D01*
X162812380Y-53965714D01*
X162907619Y-54013333D01*
X162955238Y-54060952D01*
X163002857Y-54156190D01*
X163002857Y-54441904D01*
X162955238Y-54537142D01*
X162907619Y-54584761D01*
X162812380Y-54632380D01*
X162621904Y-54632380D01*
X162526666Y-54584761D01*
X163431428Y-54632380D02*
X163431428Y-53632380D01*
X163431428Y-54108571D02*
X164002857Y-54108571D01*
X164002857Y-54632380D02*
X164002857Y-53632380D01*
X165240952Y-54251428D02*
X166002857Y-54251428D01*
X167145714Y-53965714D02*
X167383809Y-54632380D01*
X167621904Y-53965714D01*
X168526666Y-54632380D02*
X167955238Y-54632380D01*
X168240952Y-54632380D02*
X168240952Y-53632380D01*
X168145714Y-53775238D01*
X168050476Y-53870476D01*
X167955238Y-53918095D01*
X168955238Y-54537142D02*
X169002857Y-54584761D01*
X168955238Y-54632380D01*
X168907619Y-54584761D01*
X168955238Y-54537142D01*
X168955238Y-54632380D01*
X169955238Y-54632380D02*
X169383809Y-54632380D01*
X169669523Y-54632380D02*
X169669523Y-53632380D01*
X169574285Y-53775238D01*
X169479047Y-53870476D01*
X169383809Y-53918095D01*
X171145714Y-54251428D02*
X171907619Y-54251428D01*
X173098095Y-53727619D02*
X173145714Y-53680000D01*
X173240952Y-53632380D01*
X173479047Y-53632380D01*
X173574285Y-53680000D01*
X173621904Y-53727619D01*
X173669523Y-53822857D01*
X173669523Y-53918095D01*
X173621904Y-54060952D01*
X173050476Y-54632380D01*
X173669523Y-54632380D01*
X174288571Y-53632380D02*
X174383809Y-53632380D01*
X174479047Y-53680000D01*
X174526666Y-53727619D01*
X174574285Y-53822857D01*
X174621904Y-54013333D01*
X174621904Y-54251428D01*
X174574285Y-54441904D01*
X174526666Y-54537142D01*
X174479047Y-54584761D01*
X174383809Y-54632380D01*
X174288571Y-54632380D01*
X174193333Y-54584761D01*
X174145714Y-54537142D01*
X174098095Y-54441904D01*
X174050476Y-54251428D01*
X174050476Y-54013333D01*
X174098095Y-53822857D01*
X174145714Y-53727619D01*
X174193333Y-53680000D01*
X174288571Y-53632380D01*
X175574285Y-54632380D02*
X175002857Y-54632380D01*
X175288571Y-54632380D02*
X175288571Y-53632380D01*
X175193333Y-53775238D01*
X175098095Y-53870476D01*
X175002857Y-53918095D01*
X176050476Y-54632380D02*
X176240952Y-54632380D01*
X176336190Y-54584761D01*
X176383809Y-54537142D01*
X176479047Y-54394285D01*
X176526666Y-54203809D01*
X176526666Y-53822857D01*
X176479047Y-53727619D01*
X176431428Y-53680000D01*
X176336190Y-53632380D01*
X176145714Y-53632380D01*
X176050476Y-53680000D01*
X176002857Y-53727619D01*
X175955238Y-53822857D01*
X175955238Y-54060952D01*
X176002857Y-54156190D01*
X176050476Y-54203809D01*
X176145714Y-54251428D01*
X176336190Y-54251428D01*
X176431428Y-54203809D01*
X176479047Y-54156190D01*
X176526666Y-54060952D01*
D11*
G04 #@! TO.C,U2*
X135890000Y-63540000D02*
X85090000Y-63540000D01*
X135890000Y-86400000D02*
X135890000Y-63540000D01*
X85090000Y-86400000D02*
X135890000Y-86400000D01*
X85090000Y-63540000D02*
X85090000Y-86400000D01*
D12*
G04 #@! TO.C,HX711-2*
X160020000Y-90375000D02*
X137160000Y-90375000D01*
X137160000Y-90375000D02*
X137160000Y-59895000D01*
X137160000Y-59895000D02*
X160020000Y-59895000D01*
X160020000Y-59895000D02*
X160020000Y-90375000D01*
D13*
X143406000Y-91391000D02*
G75*
G03X143406000Y-91391000I-150000J0D01*
G01*
D12*
G04 #@! TO.C,HX711-1*
X184150000Y-90375000D02*
X161290000Y-90375000D01*
X161290000Y-90375000D02*
X161290000Y-59895000D01*
X161290000Y-59895000D02*
X184150000Y-59895000D01*
X184150000Y-59895000D02*
X184150000Y-90375000D01*
D13*
X167536000Y-91391000D02*
G75*
G03X167536000Y-91391000I-150000J0D01*
G01*
D12*
G04 #@! TO.C,J1*
X193965000Y-79730000D02*
X193965000Y-92830000D01*
X193965000Y-92830000D02*
X193725000Y-92830000D01*
X186165000Y-92830000D02*
X189605000Y-92830000D01*
X186165000Y-79730000D02*
X189495000Y-79730000D01*
X193965000Y-79730000D02*
X193725000Y-79730000D01*
D14*
X185575000Y-85370000D02*
G75*
G03X185575000Y-85370000I-200000J0D01*
G01*
G04 #@! TO.C,J2*
X83630000Y-87190000D02*
G75*
G03X83630000Y-87190000I-200000J0D01*
G01*
D12*
X74840000Y-92830000D02*
X75080000Y-92830000D01*
X82640000Y-92830000D02*
X79310000Y-92830000D01*
X82640000Y-79730000D02*
X79200000Y-79730000D01*
X74840000Y-79730000D02*
X75080000Y-79730000D01*
X74840000Y-92830000D02*
X74840000Y-79730000D01*
G04 #@! TO.C,J3*
X80540000Y-56935000D02*
X80540000Y-60935000D01*
X80540000Y-63535000D02*
X80540000Y-69335000D01*
X80540000Y-72035000D02*
X80540000Y-75935000D01*
X80540000Y-75935000D02*
X74960000Y-75935000D01*
X74960000Y-56935000D02*
X80540000Y-56935000D01*
D10*
X81138000Y-65025000D02*
G75*
G03X81138000Y-65025000I-100000J0D01*
G01*
D12*
X74960000Y-56935000D02*
X74960000Y-75935000D01*
D15*
G04 #@! TO.C,J4*
X108605000Y-61085000D02*
X108605000Y-55115000D01*
X108605000Y-55115000D02*
X100985000Y-55115000D01*
X100985000Y-55115000D02*
X100985000Y-61085000D01*
X100985000Y-61085000D02*
X108605000Y-61085000D01*
X105295000Y-61075000D02*
X105295000Y-60325000D01*
X105295000Y-60325000D02*
X104295000Y-60325000D01*
X104295000Y-60325000D02*
X104295000Y-61075000D01*
X104295000Y-61075000D02*
X105295000Y-61075000D01*
X108595000Y-61075000D02*
X108595000Y-60325000D01*
X108595000Y-60325000D02*
X106795000Y-60325000D01*
X106795000Y-60325000D02*
X106795000Y-61075000D01*
X106795000Y-61075000D02*
X108595000Y-61075000D01*
X102795000Y-61075000D02*
X102795000Y-60325000D01*
X102795000Y-60325000D02*
X100995000Y-60325000D01*
X100995000Y-60325000D02*
X100995000Y-61075000D01*
X100995000Y-61075000D02*
X102795000Y-61075000D01*
X108595000Y-58825000D02*
X107845000Y-58825000D01*
X107845000Y-58825000D02*
X107845000Y-57485000D01*
X104795000Y-55875000D02*
X106785000Y-55875000D01*
X100995000Y-58825000D02*
X101745000Y-58825000D01*
X101745000Y-58825000D02*
X101745000Y-55875000D01*
X101745000Y-55875000D02*
X104795000Y-55875000D01*
X107645000Y-61375000D02*
X108895000Y-61375000D01*
X108895000Y-61375000D02*
X108895000Y-60125000D01*
G04 #@! TO.C,SW1*
X185545000Y-71465000D02*
X185545000Y-70165000D01*
X186845000Y-71465000D02*
X185545000Y-71465000D01*
X193195000Y-59215000D02*
X193195000Y-69115000D01*
X186495000Y-69115000D02*
X186495000Y-59215000D01*
X192445000Y-71165000D02*
X187245000Y-71165000D01*
X187245000Y-57165000D02*
X192445000Y-57165000D01*
G04 #@! TO.C,HX711-2*
D14*
X152924088Y-92607908D02*
X152924088Y-91607848D01*
X152924088Y-92084067D02*
X153495551Y-92084067D01*
X153495551Y-92607908D02*
X153495551Y-91607848D01*
X153876526Y-91607848D02*
X154543233Y-92607908D01*
X154543233Y-91607848D02*
X153876526Y-92607908D01*
X154828964Y-91607848D02*
X155495671Y-91607848D01*
X155067074Y-92607908D01*
X156400487Y-92607908D02*
X155829024Y-92607908D01*
X156114756Y-92607908D02*
X156114756Y-91607848D01*
X156019512Y-91750713D01*
X155924268Y-91845957D01*
X155829024Y-91893579D01*
X157352925Y-92607908D02*
X156781462Y-92607908D01*
X157067194Y-92607908D02*
X157067194Y-91607848D01*
X156971950Y-91750713D01*
X156876706Y-91845957D01*
X156781462Y-91893579D01*
X157781522Y-92226932D02*
X158543473Y-92226932D01*
X158972070Y-91703091D02*
X159019692Y-91655470D01*
X159114936Y-91607848D01*
X159353045Y-91607848D01*
X159448289Y-91655470D01*
X159495911Y-91703091D01*
X159543533Y-91798335D01*
X159543533Y-91893579D01*
X159495911Y-92036445D01*
X158924448Y-92607908D01*
X159543533Y-92607908D01*
G04 #@! TO.C,HX711-1*
X161814088Y-92607908D02*
X161814088Y-91607848D01*
X161814088Y-92084067D02*
X162385551Y-92084067D01*
X162385551Y-92607908D02*
X162385551Y-91607848D01*
X162766526Y-91607848D02*
X163433233Y-92607908D01*
X163433233Y-91607848D02*
X162766526Y-92607908D01*
X163718964Y-91607848D02*
X164385671Y-91607848D01*
X163957074Y-92607908D01*
X165290487Y-92607908D02*
X164719024Y-92607908D01*
X165004756Y-92607908D02*
X165004756Y-91607848D01*
X164909512Y-91750713D01*
X164814268Y-91845957D01*
X164719024Y-91893579D01*
X166242925Y-92607908D02*
X165671462Y-92607908D01*
X165957194Y-92607908D02*
X165957194Y-91607848D01*
X165861950Y-91750713D01*
X165766706Y-91845957D01*
X165671462Y-91893579D01*
X166671522Y-92226932D02*
X167433473Y-92226932D01*
X168433533Y-92607908D02*
X167862070Y-92607908D01*
X168147801Y-92607908D02*
X168147801Y-91607848D01*
X168052558Y-91750713D01*
X167957314Y-91845957D01*
X167862070Y-91893579D01*
G04 #@! TO.C,J1*
X186556296Y-93986773D02*
X186556296Y-94701851D01*
X186508624Y-94844867D01*
X186413280Y-94940211D01*
X186270265Y-94987883D01*
X186174921Y-94987883D01*
X187557406Y-94987883D02*
X186985343Y-94987883D01*
X187271375Y-94987883D02*
X187271375Y-93986773D01*
X187176031Y-94129788D01*
X187080687Y-94225132D01*
X186985343Y-94272804D01*
G04 #@! TO.C,J2*
X81581296Y-93986773D02*
X81581296Y-94701851D01*
X81533624Y-94844867D01*
X81438280Y-94940211D01*
X81295265Y-94987883D01*
X81199921Y-94987883D01*
X82010343Y-94082116D02*
X82058015Y-94034445D01*
X82153359Y-93986773D01*
X82391719Y-93986773D01*
X82487062Y-94034445D01*
X82534734Y-94082116D01*
X82582406Y-94177460D01*
X82582406Y-94272804D01*
X82534734Y-94415820D01*
X81962671Y-94987883D01*
X82582406Y-94987883D01*
G04 #@! TO.C,J3*
X79675383Y-54900272D02*
X79675383Y-55617308D01*
X79627580Y-55760715D01*
X79531976Y-55856320D01*
X79388569Y-55904122D01*
X79292964Y-55904122D01*
X80057802Y-54900272D02*
X80679233Y-54900272D01*
X80344616Y-55282691D01*
X80488023Y-55282691D01*
X80583628Y-55330494D01*
X80631430Y-55378296D01*
X80679233Y-55473901D01*
X80679233Y-55712913D01*
X80631430Y-55808517D01*
X80583628Y-55856320D01*
X80488023Y-55904122D01*
X80201209Y-55904122D01*
X80105604Y-55856320D01*
X80057802Y-55808517D01*
G04 #@! TO.C,J4*
X98726666Y-59982380D02*
X98726666Y-60696666D01*
X98679047Y-60839523D01*
X98583809Y-60934761D01*
X98440952Y-60982380D01*
X98345714Y-60982380D01*
X99631428Y-60315714D02*
X99631428Y-60982380D01*
X99393333Y-59934761D02*
X99155238Y-60649047D01*
X99774285Y-60649047D01*
G04 #@! TO.C,SW1*
X185991666Y-56074761D02*
X186134523Y-56122380D01*
X186372619Y-56122380D01*
X186467857Y-56074761D01*
X186515476Y-56027142D01*
X186563095Y-55931904D01*
X186563095Y-55836666D01*
X186515476Y-55741428D01*
X186467857Y-55693809D01*
X186372619Y-55646190D01*
X186182142Y-55598571D01*
X186086904Y-55550952D01*
X186039285Y-55503333D01*
X185991666Y-55408095D01*
X185991666Y-55312857D01*
X186039285Y-55217619D01*
X186086904Y-55170000D01*
X186182142Y-55122380D01*
X186420238Y-55122380D01*
X186563095Y-55170000D01*
X186896428Y-55122380D02*
X187134523Y-56122380D01*
X187325000Y-55408095D01*
X187515476Y-56122380D01*
X187753571Y-55122380D01*
X188658333Y-56122380D02*
X188086904Y-56122380D01*
X188372619Y-56122380D02*
X188372619Y-55122380D01*
X188277380Y-55265238D01*
X188182142Y-55360476D01*
X188086904Y-55408095D01*
G04 #@! TD*
M02*

View File

@ -0,0 +1,122 @@
M48
;DRILL file {KiCad 5.0.2-5.fc29} date Fr 22 Mär 2019 17:51:56 CET
;FORMAT={-:-/ absolute / inch / decimal}
FMAT,2
INCH,TZ
T1C0.0157
T2C0.0276
T3C0.0354
T4C0.0390
T5C0.0394
T6C0.0400
T7C0.0472
T8C0.0866
T9C0.1000
T10C0.0472
%
G90
G05
T1
X4.425Y-3.1831
T2
X3.0685Y-2.59
X3.0685Y-2.6411
X3.1315Y-2.5644
X3.1315Y-2.6156
X3.1315Y-2.6667
T3
X7.425Y-2.3687
X7.425Y-2.5262
X7.425Y-2.6837
X3.2299Y-3.2197
X3.2299Y-3.2906
X3.2299Y-3.3614
X3.2299Y-3.4323
X3.2299Y-3.5031
X3.2299Y-3.574
X7.353Y-3.2197
X7.353Y-3.2906
X7.353Y-3.3614
X7.353Y-3.4323
X7.353Y-3.5031
X7.353Y-3.574
T4
X3.6Y-3.3516
X3.7Y-3.3516
X3.8Y-3.3516
X3.9Y-3.3516
X4.Y-2.5516
X4.Y-3.3516
X4.1Y-2.5516
X4.1Y-3.3516
X4.2Y-2.5516
X4.2Y-3.3516
X4.3Y-2.5516
X4.3Y-3.3516
X4.4Y-2.5516
X4.4Y-3.3516
X4.5Y-2.5516
X4.5Y-3.3516
X4.6Y-2.5516
X4.6Y-3.3516
X4.7Y-2.5516
X4.7Y-3.3516
X4.8Y-2.5516
X4.8Y-3.3516
X4.9Y-2.5516
X4.9Y-3.3516
X5.Y-2.5516
X5.Y-3.3516
X5.1Y-2.5516
X5.1Y-3.3516
T5
X3.037Y-3.1449
X3.037Y-3.6488
X3.1Y-2.4482
X3.1Y-2.7829
X7.5459Y-3.1449
X7.5459Y-3.6488
X4.0766Y-2.3081
X4.175Y-2.3081
T6
X6.6Y-2.4081
X6.6Y-3.5081
X6.7Y-2.4081
X6.7Y-3.5081
X6.8Y-2.4081
X6.8Y-3.5081
X6.9Y-2.4081
X6.9Y-2.7081
X6.9Y-3.5081
X7.Y-2.4081
X7.Y-2.7081
X7.Y-3.5081
X5.65Y-2.4081
X5.65Y-3.5081
X5.75Y-2.4081
X5.75Y-3.5081
X5.85Y-2.4081
X5.85Y-3.5081
X5.95Y-2.4081
X5.95Y-2.7081
X5.95Y-3.5081
X6.05Y-2.4081
X6.05Y-2.7081
X6.05Y-3.5081
T7
X7.3561Y-2.29
X7.3561Y-2.7624
X7.5923Y-2.29
X7.5923Y-2.7624
T8
X3.0646Y-3.3969
X7.5183Y-3.3969
T9
X3.45Y-2.6016
X3.45Y-3.3016
X5.25Y-2.6016
X5.25Y-3.3016
T10
X4.238Y-2.2293
T0
M30

View File

@ -0,0 +1,3 @@
(sym_lib_table
(lib (name beieliscale-kicad-eagle-import)(type Legacy)(uri ${KIPRJMOD}/beieliscale-kicad-eagle-import.lib)(options "")(descr ""))
)

27
Python/serial_monitor_USB0.py Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/python3
from time import sleep
import serial
import sys
import datetime
while (True):
serial_not_open = True
while serial_not_open:
try:
ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)
except:
sleep(0.5)
else:
serial_not_open = False
no_exception = True
while no_exception:
try:
while True:
while ser.inWaiting():
print(ser.read().decode('utf-8'), end='')
sleep(0.1)
except:
print(datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S")," - Serial Device disappeared...")
ser.close()
no_exception = False

27
Python/serial_monitor_USB1.py Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/python3
from time import sleep
import serial
import sys
import datetime
while (True):
serial_not_open = True
while serial_not_open:
try:
ser = serial.Serial('/dev/ttyUSB1', 115200, timeout=1)
except:
sleep(0.5)
else:
serial_not_open = False
no_exception = True
while no_exception:
try:
while True:
while ser.inWaiting():
print(ser.read().decode('utf-8'), end='')
sleep(0.1)
except:
print(datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S")," - Serial Device disappeared...")
ser.close()
no_exception = False