/* mini-beieli-node.ino BeieliScale, see https://mini-beieli.ch Joerg Lehmann, nbit Informatik GmbH */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mini_beieli_node.h" using namespace McciCatena; // forwards static void settleDoneCb(osjob_t* pSendJob); static void warmupDoneCb(osjob_t* pSendJob); static void startNewIterationCb(osjob_t* pJob); static void txNotProvisionedCb(osjob_t *pSendJob); static void sleepDoneCb(osjob_t* pSendJob); static Arduino_LoRaWAN::SendBufferCbFn sendBufferDoneCb; static Arduino_LoRaWAN::ReceivePortBufferCbFn receiveMessage; // Additional Commands // forward reference to the command function cCommandStream::CommandFn cmdHello; cCommandStream::CommandFn cmdGetCalibrationSettings; cCommandStream::CommandFn cmdGetSensorReadings; cCommandStream::CommandFn cmdGetScaleA; cCommandStream::CommandFn cmdGetScaleB; cCommandStream::CommandFn cmdCalibrateZeroScaleA; cCommandStream::CommandFn cmdCalibrateZeroScaleB; cCommandStream::CommandFn cmdCalibrateScaleA; cCommandStream::CommandFn cmdCalibrateScaleB; cCommandStream::CommandFn cmdSetDebugLevel; cCommandStream::CommandFn cmdGetDebugLevel; // 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_scale_a", cmdCalibrateZeroScaleA }, { "calibrate_zero_scale_b", cmdCalibrateZeroScaleB }, { "calibrate_scale_a", cmdCalibrateScaleA }, { "calibrate_scale_b", cmdCalibrateScaleB }, { "set_debug_level", cmdSetDebugLevel }, { "get_debug_level", cmdGetDebugLevel }, // 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*/ ); /****************************************************************************\ | | VARIABLES | \****************************************************************************/ 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; long iteration = 0; // what iteration number do we have, starts with 0 // 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; // 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 iterationJob; static osjob_t sendJob; void setup(void) { gCatena.begin(); ClearLoraData(); // Use D10 to regulate power pinMode(D10, OUTPUT); setup_platform(); setup_bme280(); //setup_scales(); setup_flash(); setup_uplink(); } void setup_platform(void) { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - setup_platform\n", millis()); } /* add our application-specific commands */ gCatena.addCommands( sMyExtraCommands_top, nullptr ); // read config_data from fram... if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - Reading Calibration Config from FRAM...\n", millis()); } gCatena.getFram()->getField(cFramStorage::kBme680Cal, (uint8_t *)&config_data, sizeof(config_data)); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - setup_platform, this is the configuration\n", millis()); 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("debug_level: %d\n", (int)config_data.debug_level); gCatena.SafePrintf("Size of config_data: %d\n", sizeof(config_data)); gCatena.SafePrintf("Size of lora_data_first: %d\n", sizeof(lora_data_first)); gCatena.SafePrintf("Size of lora_data: %d\n", sizeof(lora_data)); } #ifdef USBCON // if running unattended, don't wait for USB connect. if (!(gCatena.GetOperatingFlags() & static_cast(gCatena.OPERATING_FLAGS::fUnattended))) { while (!Serial) /* wait for USB attach */ yield(); } #endif gCatena.SafePrintf("\n"); gCatena.SafePrintf("-------------------------------------------------------------------------------\n"); gCatena.SafePrintf("mini-beieli.ch - BeieliScale Version %d.\n", fwVersion); { 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); 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 (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - setup_bme280\n", millis()); } if (gBME280.begin(BME280_ADDRESS, Adafruit_BME280::OPERATING_MODE::Sleep)) { fBme = true; } else { fBme = false; gCatena.SafePrintf("No BME280 found: check wiring\n"); } } bool setup_scales(void) { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - setup_scales\n", millis()); } bool res; res = true; // 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. LoadCell.begin(A1, A0); if (!(LoadCell.wait_ready_timeout(2000))) { gCatena.SafePrintf("%010d - Scale not ready after Init.\n"); res = false; } if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - setup_scale done\n", millis()); } return res; } void setup_flash(void) { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - setup_flash\n", millis()); } if (gFlash.begin(&gSPI2, Catena::PIN_SPI2_FLASH_SS)) { fFlash = true; gFlash.powerDown(); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - FLASH found, but power down\n", millis()); } } else { fFlash = false; gFlash.end(); gSPI2.end(); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - No FLASH found: check hardware\n", millis()); } } } void setup_uplink(void) { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - setup_uplink\n", millis()); } // Do an unjoin, so every reboot will trigger a join if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - do an unjoin...\n", millis()); } LMIC_unjoin(); /* trigger a join by sending the first packet */ if (!(gCatena.GetOperatingFlags() & static_cast(gCatena.OPERATING_FLAGS::fManufacturingTest))) { if (!gLoRaWAN.IsProvisioned()) gCatena.SafePrintf("%010d - LoRaWAN not provisioned yet. Use the commands to set it up.\n"); else { gLed.Set(LedPattern::Joining); /* trigger a join by sending the first packet */ StartNewIteration(); } } } // 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.fw_version = fwVersion; 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; // We initialize last_sensor_reading last_sensor_reading.vbat = 0; last_sensor_reading.weight1 = 0; last_sensor_reading.weight2 = 0; last_sensor_reading.weight = 0; last_sensor_reading.temperature = 0; last_sensor_reading.humidity = 0; last_sensor_reading.pressure = 0; } void ShowLORAData(bool firstTime) { if (firstTime) { gCatena.SafePrintf("{\n"); gCatena.SafePrintf(" \"version\": \"%d\",\n", lora_data_first.version); gCatena.SafePrintf(" \"fw_version\": \"%d\",\n", lora_data_first.fw_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\": \"%d\",\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 DoDeepSleep() { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - now going to deep sleep\n", millis()); } // Prepare Deep Sleep gLed.Set(LedPattern::Off); Serial.end(); Wire.end(); SPI.end(); if (fFlash) gSPI2.end(); // Now sleeping... gCatena.Sleep(CATCFG_T_INTERVAL); // Recover from wakeup... Serial.begin(); Wire.begin(); SPI.begin(); if (fFlash) gSPI2.begin(); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - done with deep sleep\n", millis()); } } void ReadSensors(SENSOR_data &sensor_data) { SENSOR_data res; int32_t weight_current32; // vBat gCatena.poll(); int vbat_mv = (int)(gCatena.ReadVbat() * 1000.0f); res.vbat = GetVBatValue(vbat_mv); if (config_data.debug_level > 0) { gCatena.SafePrintf("vBat: %d mV\n", vbat_mv); } // Read Scales if (setup_scales()) { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - HX711 LoadCell is ready.\n", millis()); } LoadCell.set_gain(128); gCatena.poll(); res.weight1 = (int32_t)LoadCell.read_average(5); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - Load_cell 1 weight1_current: %ld\n", millis(), res.weight1); } gCatena.poll(); LoadCell.set_gain(32); res.weight2 = (int32_t)LoadCell.read_average(5); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - Load_cell 2 weight2_current: %ld\n", millis(), res.weight2); } } else { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - HX711 LoadCell not ready.\n", millis()); } } // Disable Power gCatena.poll(); digitalWrite(D10, LOW); // Gewicht berechnen weight_current32 = (int32_t)((((res.weight1 - config_data.cal_w1_0) / config_data.cal_w1_factor) + ((res.weight2 - 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; } res.weight = (uint16_t)weight_current32; if (fBme) { gCatena.poll(); /* warm up the BME280 by discarding a measurement */ (void)gBME280.readTemperature(); gCatena.poll(); 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. if (config_data.debug_level > 0) { gCatena.SafePrintf( "%010d - BME280: T: %d P: %d RH: %d\n", millis(), (int)m.Temperature, (int)m.Pressure, (int)m.Humidity); } res.temperature = (int16_t)((m.Temperature) * 10); res.humidity = (uint8_t)m.Humidity; res.pressure = (uint8_t)((m.Pressure / 100) - PRESSURE_OFFSET); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - pressure_current: %d\n", millis(), res.pressure); } } sensor_data = res; } void StartNewIteration() { // we increment the iteration counter iteration++; SENSOR_data current_sensor_reading; ReadSensors(current_sensor_reading); int16_t temp_change; // vBus float vBus = gCatena.ReadVbus(); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - vBus: %d mV\n", millis(), (int)(vBus * 1000.0f)); } fUsbPower = (vBus > 4.3) ? true : false; if (iteration == 1) { lora_data_first.vbat = current_sensor_reading.vbat; lora_data_first.weight1 = current_sensor_reading.weight1; lora_data_first.weight2 = current_sensor_reading.weight2; lora_data_first.weight = current_sensor_reading.weight; lora_data_first.temperature = current_sensor_reading.temperature; lora_data_first.humidity = current_sensor_reading.humidity; lora_data_first.pressure = current_sensor_reading.pressure; } else { lora_data.vbat = current_sensor_reading.vbat; lora_data.weight[my_position] = current_sensor_reading.weight; if (my_position == 0) { lora_data.temperature = current_sensor_reading.temperature; } else { temp_change = current_sensor_reading.temperature - last_sensor_reading.temperature; 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] = current_sensor_reading.humidity; lora_data.pressure[my_position] = current_sensor_reading.pressure; } if (my_position == 0) { timer_pos0 = millis(); } if (config_data.debug_level > 0) { ShowLORAData(iteration == 1); } 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 threshold or the first measurement is // more than one hour old (which should not happen :-) ) if ( (iteration == 1) || (my_position >= MAX_VALUES_TO_SEND) || ((last_sensor_reading.weight - current_sensor_reading.weight) > SEND_DIFF_THRESHOLD_5GRAMS) || ((millis() - timer_pos0) > 3600000)) { lora_data.offset_last_reading = (uint8_t)((millis() - timer_pos0) / 1000 / 60); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - startSendingUplink(), my_position: %d, iteration: %d\n", millis(), my_position, iteration); } startSendingUplink(iteration == 1); gLed.Set(LedPattern::TwoShort); // If we have any critical jobs to complete in the next 8 seconds, do not sleep but process them long loopCount = 0; while (os_queryTimeCriticalJobs(ms2osticks(8000)) != 0) { loopCount++; gCatena.poll(); yield(); } } if (iteration > 1) { // we make the current sensor reading to the last one... last_sensor_reading = current_sensor_reading; } if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - now going to sleep for %d seconds...\n", millis(), CATCFG_T_INTERVAL); if (fUsbPower) { gCatena.SafePrintf("USB Power is on\n"); } else { gCatena.SafePrintf("USB Power is off\n"); } //Serial.flush(); } gLed.Set(LedPattern::Sleeping); if (!fUsbPower) { DoDeepSleep(); os_setTimedCallback( &iterationJob, os_getTime() + sec2osticks(2), startNewIterationCb); } else { gCatena.SafePrintf("%010d - light sleep; os_setTimedCallback for startNewIterationCb in %d...\n", millis(), CATCFG_T_INTERVAL); os_setTimedCallback( &iterationJob, os_getTime() + sec2osticks(CATCFG_T_INTERVAL), startNewIterationCb); } } 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)) { if (config_data.debug_level > 0) { gCatena.SafePrintf("requesting confirmed tx\n"); } fConfirmed = true; } if (firstTime) { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - SendBuffer firstTime\n", millis()); } gLoRaWAN.SendBuffer((uint8_t*)&lora_data_first, sizeof(LORA_data_first), sendBufferDoneCb, NULL, fConfirmed, kUplinkPort); } else { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - SendBuffer not firstTime\n", millis()); } gLoRaWAN.SendBuffer((uint8_t*)&lora_data, sizeof(LORA_data), sendBufferDoneCb, NULL, fConfirmed, kUplinkPort); } ClearLoraData(); } static void sendBufferDoneCb( void* pContext, bool fStatus) { osjobcb_t pFn; gLed.Set(LedPattern::Settling); pFn = settleDoneCb; if (! fStatus) { if (!gLoRaWAN.IsProvisioned()) { // we'll talk about it at the callback. pFn = txNotProvisionedCb; // but prevent join attempts now. gLoRaWAN.Shutdown(); } else if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - send buffer failed\n", millis()); } } os_setTimedCallback( &sendJob, os_getTime() + sec2osticks(CATCFG_T_SETTLE), pFn ); } static void txNotProvisionedCb( osjob_t *pSendJob ) { if (config_data.debug_level > 0) { gCatena.SafePrintf("LoRaWAN not provisioned yet. Use the commands to set it up.\n"); } gLoRaWAN.Shutdown(); gLed.Set(LedPattern::NotProvisioned); } static void settleDoneCb( osjob_t* pSendJob) { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - settleDoneCb\n", millis()); } if (config_data.debug_level > 2) { // Terry vv gCatena.SafePrintf("LMIC.rxDelay: %i\n", LMIC.rxDelay); gCatena.SafePrintf("LMIC.dn2Dr: %i\n", LMIC.dn2Dr); gCatena.SafePrintf("LMIC.dn2Freq: %i\n", LMIC.dn2Freq); gCatena.SafePrintf("LMIC.rx1DrOffset: %i\n", LMIC.rx1DrOffset); // Terry ^^ } sleepDoneCb(pSendJob); } static void sleepDoneCb(osjob_t* pJob) { gLed.Set(LedPattern::WarmingUp); if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - sleepDoneCb\n", millis()); } os_setTimedCallback( pJob, os_getTime() + sec2osticks(CATCFG_T_WARMUP), warmupDoneCb); } static void warmupDoneCb(osjob_t* pJob) { if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - warmupDoneCb\n", millis()); } } static void startNewIterationCb(osjob_t* pJob) { gCatena.SafePrintf("%010d - startNewIterationCb\n", millis()); StartNewIteration(); } static void receiveMessage(void *pContext, uint8_t port, const uint8_t *pMessage, size_t nMessage) { uint32_t gram_A; uint32_t gram_B; SENSOR_data temp_sensor_data; if (config_data.debug_level > 0) { gCatena.SafePrintf("%010d - receiveMessage was called!!!\n", millis()); } if (config_data.debug_level > 2) { // Terry vv if (port == 0) { gCatena.SafePrintf("MAC message:"); for (unsigned i = 0; i < LMIC.dataBeg; ++i) { gCatena.SafePrintf(" %02x", LMIC.frame[i]); } return; } gCatena.SafePrintf("Port: %i\n", port); gCatena.SafePrintf("LMIC.rxDelay: %i\n", LMIC.rxDelay); gCatena.SafePrintf("LMIC.dn2Dr: %i\n", LMIC.dn2Dr); gCatena.SafePrintf("LMIC.dn2Freq: %i\n", LMIC.dn2Freq); gCatena.SafePrintf("LMIC.rx1DrOffset: %i\n", LMIC.rx1DrOffset); // Terry ^^ } if (config_data.debug_level > 0) { gCatena.SafePrintf("message received port(%02x)/length(%x)\n", port, nMessage ); // we print out the received message... gCatena.SafePrintf("Current LMIC.seqnoUp: %d\n", LMIC.seqnoUp); gCatena.SafePrintf("Received Data (Payload): \n"); for (byte i = 0; i < nMessage; i++) { gCatena.SafePrintf("%02x", pMessage[i]); } gCatena.SafePrintf("\n"); } if (LMIC.seqnoUp > 2) { if (config_data.debug_level > 0) { gCatena.SafePrintf("tare with downlink is only possible within first two uplink packets!\n"); return; } } if (port == 1 && nMessage == 1) { if (pMessage[0] == 0) { if (config_data.debug_level > 0) { gCatena.SafePrintf("reset both scales to zero\n"); } ReadSensors(temp_sensor_data); config_data.cal_w1_0 = temp_sensor_data.weight1; config_data.cal_w2_0 = temp_sensor_data.weight2; gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data)); } } if (port == 1 && nMessage == 9) { if (pMessage[0] == 1) { gram_A = 0; gram_A += (long)pMessage[1] << 24; gram_A += (long)pMessage[2] << 16; gram_A += (long)pMessage[3] << 8; gram_A += (long)pMessage[4]; gram_B = 0; gram_B += (long)pMessage[5] << 24; gram_B += (long)pMessage[6] << 16; gram_B += (long)pMessage[7] << 8; gram_B += (long)pMessage[8]; if (config_data.debug_level > 0) { gCatena.SafePrintf("tare scales, A: %d, B: %d\n", gram_A, gram_B); } ReadSensors(temp_sensor_data); config_data.cal_w1_factor = (((float)temp_sensor_data.weight1 - config_data.cal_w1_0) / gram_A); config_data.cal_w2_factor = (((float)temp_sensor_data.weight2 - config_data.cal_w2_0) / gram_B); gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data)); } } } /* 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) { SENSOR_data temp_sensor_data; ReadSensors(temp_sensor_data); pThis->printf("{\n"); pThis->printf(" \"weight\": \"%d\",\n", temp_sensor_data.weight); pThis->printf(" \"weight1_raw\": \"%d\",\n", temp_sensor_data.weight1); pThis->printf(" \"weight2_raw\": \"%d\",\n", temp_sensor_data.weight2); pThis->printf(" \"temperature\": \"%d\",\n", temp_sensor_data.temperature); pThis->printf(" \"humidity\": \"%d\",\n", temp_sensor_data.humidity); pThis->printf(" \"pressure\": \"%d\",\n", temp_sensor_data.pressure); pThis->printf(" \"batt\": \"%d\",\n", temp_sensor_data.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 cmdCalibrateZeroScaleA(cCommandStream *pThis, void *pContext, int argc, char **argv) { SENSOR_data temp_sensor_data; ReadSensors(temp_sensor_data); config_data.cal_w1_0 = temp_sensor_data.weight1; gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data)); pThis->printf("{ \"msg\": \"calibrate_zero_scale_a was successful\" }\n"); return cCommandStream::CommandStatus::kSuccess; } cCommandStream::CommandStatus cmdCalibrateZeroScaleB(cCommandStream *pThis, void *pContext, int argc, char **argv) { SENSOR_data temp_sensor_data; ReadSensors(temp_sensor_data); config_data.cal_w2_0 = temp_sensor_data.weight2; gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data)); pThis->printf("{ \"msg\": \"calibrate_zero_scale_b was successful\" }\n"); return cCommandStream::CommandStatus::kSuccess; } cCommandStream::CommandStatus cmdCalibrateScaleA(cCommandStream *pThis, void *pContext, int argc, char **argv) { SENSOR_data temp_sensor_data; ReadSensors(temp_sensor_data); String w1_gramm(argv[1]); config_data.cal_w1_factor = (((float)temp_sensor_data.weight1 - config_data.cal_w1_0) / w1_gramm.toFloat()); gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data)); gCatena.SafePrintf("temp_sensor_data.weight1: %ld\n", temp_sensor_data.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_scale_a was successful\" }\n"); return cCommandStream::CommandStatus::kSuccess; } cCommandStream::CommandStatus cmdCalibrateScaleB(cCommandStream *pThis, void *pContext, int argc, char **argv) { SENSOR_data temp_sensor_data; ReadSensors(temp_sensor_data); String w2_gramm(argv[1]); config_data.cal_w2_factor = (((float)temp_sensor_data.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_scale_b was successful\" }\n"); return cCommandStream::CommandStatus::kSuccess; } cCommandStream::CommandStatus cmdSetDebugLevel(cCommandStream *pThis, void *pContext, int argc, char **argv) { String s_debug_level(argv[1]); config_data.debug_level = s_debug_level.toInt(); gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data)); pThis->printf("{ \"msg\": \"set_debug_level was successful\" }\n"); return cCommandStream::CommandStatus::kSuccess; } cCommandStream::CommandStatus cmdGetDebugLevel(cCommandStream *pThis, void *pContext, int argc, char **argv) { gCatena.getFram()->saveField(cFramStorage::kBme680Cal, (const uint8_t *)&config_data, sizeof(config_data)); pThis->printf("{ \"msg\": \"debug_level is %d\" }\n", config_data.debug_level); return cCommandStream::CommandStatus::kSuccess; }