CubeCell Servo library conflicts with LoRaWan_APP

Key details

Project: remote controlled valve using a servo.
Board: CubeCell-Board (HTCC-AB01)
Libraries: LoRaWan_APP, CubeCellServoTimers
Host OS: Ubuntu 20.04.3 LTS
Arduino IDE: 1.8.14 Hourly Build 2021/04/08 03:25

The CubeCell Arduino libraries are up-to-date, I have downloaded them today and followed the instructions found here.

Issue

The servo library works fine on its own. Notice the test loop at the beginning of loop(), everything works fine there. However, when the board receives a downlink message and changes the servo to a new position, it only works the first time.

Example program cycle:

  1. Program start/board reset :heavy_check_mark:
  2. Receive new downlink message :heavy_check_mark:
  3. Change servo position :heavy_check_mark:
  4. Sleep cycle :heavy_check_mark:
  5. Receive new downlink message :heavy_check_mark:
  6. Change servo position :x:
  7. Sleep cycle :heavy_check_mark:
  8. Receive new downlink message :heavy_check_mark:
  9. Change servo position :x:

This behaviour is really weird since the servo works perfectly inside the test loop. So the only reason I supsect this is happening is because of a conflict with the LoRaWan library cycle.

Code

#include <LoRaWan_APP.h>
#include <Arduino.h>

#include "CubeCellServoTimers.h"

uint32_t  license[4] = { /* ... */ };

/* OTAA parameters */
uint8_t devEui[] = { /* ... */ };
uint8_t appEui[] = { /* ... */ };
uint8_t appKey[] = { /* ... */ };

/* ABP parameters */
uint8_t nwkSKey[] = { /* ... */ };
uint8_t appSKey[] = { /* ... */ };
uint32_t devAddr =  (uint32_t) 0x00000000;

uint16_t userChannelsMask[6] = { 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 };

LoRaMacRegion_t loraWanRegion = ACTIVE_REGION;
DeviceClass_t loraWanClass = CLASS_A;

uint32_t appTxDutyCycle = 50000;
bool overTheAirActivation = true;
bool loraWanAdr = true;
bool keepNet = true;
bool isTxConfirmed = false;
uint8_t appPort = 2;
uint8_t confirmedNbTrials = 8;

#define VALVE_OPEN        100
#define VALVE_CLOSE       0
#define VALVE_HALF_CLOSE  35

#define SERVO_MIN         500
#define SERVO_MAX         2500
#define SERVO_IN_MIN      0
#define SERVO_IN_MAX      4096
#define SERVO_MIN_DEGREE  0
#define SERVO_MAX_DEGREE  180

#define SERVO_PIN         GPIO2
#define SERVO_INPUT_PIN   ADC

#define TEST_OVERRIDE_PIN GPIO1

Servo servo;

long getServoPosition() {
  return map(analogRead(SERVO_INPUT_PIN), SERVO_IN_MIN, SERVO_IN_MAX, SERVO_MAX_DEGREE, SERVO_MIN_DEGREE);
}

void setServo(int spos) {
  servo.write(spos);
  delay(5000);
}

static void prepareTxFrame(uint8_t port) {
}

void downLinkDataHandle(McpsIndication_t *mcpsIndication) {
  int action = 0;
  for(uint8_t i = 0; i < mcpsIndication->BufferSize; i++) {
    Serial.print("READ: ");
    Serial.println(mcpsIndication->Buffer[i]);
    
    action = mcpsIndication->Buffer[i];
    switch (action) {
      case 0:
        setServo(VALVE_CLOSE);
        break;
      case 1:
        setServo(VALVE_OPEN);
        break;
      case 2:
        setServo(VALVE_HALF_CLOSE);
        break;
      default:
        break;
    }
  }
  
  Serial.println();
  uint32_t color = mcpsIndication->Buffer[0] << 16 | mcpsIndication->Buffer[1] << 8 | mcpsIndication->Buffer[2];

#if(LoraWan_RGB == 1)
  turnOnRGB(color, 5000);
  turnOffRGB();
#endif
}

void setup() {
  Serial.begin(115200);
  while (!Serial);

#if(AT_SUPPORT)
  enableAt();
#endif
  deviceState = DEVICE_STATE_INIT;
  LoRaWAN.ifskipjoin();
  
  servo.attach(SERVO_PIN, SERVO_MIN, SERVO_MAX);
  pinMode(TEST_OVERRIDE_PIN, INPUT);
}

void loop() {
  if (digitalRead(TEST_OVERRIDE_PIN) == HIGH) {
    int positions[] = { VALVE_OPEN, VALVE_CLOSE, VALVE_HALF_CLOSE };

    for (int i = 0; i < sizeof(positions) / sizeof(positions[0]); i++) {
      int spos = positions[i];
      setServo(spos);
      delay(1000);
      int inPos = analogRead(SERVO_INPUT_PIN);
      int servoPos = map(inPos, SERVO_IN_MIN, SERVO_IN_MAX, SERVO_MAX_DEGREE, SERVO_MIN_DEGREE);
      Serial.printf("spos: %dº | %dmV -> %dº = %d\n", spos, inPos, servoPos, abs(servoPos - spos));
    }
  } else {
    switch (deviceState) {
      case DEVICE_STATE_INIT:
      {
      #if(AT_SUPPORT)
        getDevParam();
      #endif
        printDevParam();
        LoRaWAN.init(loraWanClass, loraWanRegion);
        deviceState = DEVICE_STATE_JOIN;
        break;
      }
      case DEVICE_STATE_JOIN:
      {
        LoRaWAN.join();
        break;
      }
      case DEVICE_STATE_SEND:
      {
        prepareTxFrame(appPort);
        LoRaWAN.send();
        deviceState = DEVICE_STATE_CYCLE;
        break;
      }
      case DEVICE_STATE_CYCLE:
      {
        txDutyCycleTime = appTxDutyCycle + randr(0, APP_TX_DUTYCYCLE_RND);
        LoRaWAN.cycle(txDutyCycleTime);
        deviceState = DEVICE_STATE_SLEEP;
        break;
      }
      case DEVICE_STATE_SLEEP:
      {
        LoRaWAN.sleep();
        break;
      }
      default:
      {
        deviceState = DEVICE_STATE_INIT;
        break;
      }
    }
  }
}

Extra details

Downlink actions:

  • 00: Close valve
  • 01: Open valve
  • 02: Half close valve

Test:

  • run test loop cycle when TEST_OVERRIDE_PIN (default: GPIO1) is high