Serial Data Transfer Errors, Servo movement...

Status
Not open for further replies.

thomen

Well-known member
I've been getting a few crc errors transferring data between the pi and teensy since moving the teensy to a smaller breadboard.. (could be totally unrelated and need blocking code etc) but it generally occurs when a servo pulses.

I've got a class that moves a servo every minute to simulate the sun moving through the day..

The teensy is also getting data from a pi via serial.
I've implemented CRC32 and ever since introducing the sun class I've been seeing errors in data transfer usually occuring around the same point. I have lowered the baud and increased the data transfer timeout but it is still getting errors.

Just wondering if anyone could suggest any more solutions.
Please see source (please forgive me there's some pretty dirty code in there)

The main areas are setpulselengthandrotate and the Update function:

The output from the serial monitor is
Packet data length is 155
CRC32 Check Error!
{"weatherinfo": {"moonrise": "6:49:00", "moonset": "19:12:00", "temp_c": 24.2, "weather": "cloudy lightningra",set ":00 "t "/01 "rrme :000 eciti: ",sis: :0

as you can see the json is getting cut off

--SUN--
Code:
// Sun.h
#ifndef Sun_H
#define Sun_H

////#include <Arduino.h>
#include <FastLED.h>
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <TimeLib.h>
#include <string.h>

class Sun
{
	private:
		void SetPulseLenAndRotate(float degreeToRotateTo);
		Adafruit_PWMServoDriver pwm;
		void UpdateSunLEDColour();
		void Setup();
	public:
		Sun();
		void Initialise(Adafruit_PWMServoDriver pwmDriver,time_t sunrise,time_t sunset,time_t currentDate);
		void Update();
		void Reset(time_t sunrise,time_t sunset);
		bool IsInitialised();
};

#endif

Code:
//Sun.cpp
//Date time from string function sourced here http://stackoverflow.com/questions/32523230/how-convert-string-to-datetime-c
//Fast LED initialisation checked from here: http://hybrid.chadmeby.com/2016/10/25/simple-fastled-analog-input-to-hue/
#include <Arduino.h>
#include "Sun.h"
#include <TimeLib.h>
#include <time.h>

#define PWM_LOCATION  0
#define DATA_PIN 21
#define NUM_LEDS 1
#define CHIPSET     WS2811   //led chipset (neopixels)
#define COLOR_ORDER GRB
#define BRIGHTNESS  200


#define SERVOMIN  150 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  600 // this is the 'maximum' pulse length count (out of 4096)

#define SUN_START_ANGLE  15.0
#define SUN_END_ANGLE  165.0

static CRGB leds[NUM_LEDS];

const double hslSaturation = 100.0;
const double hslLightness = 50.0;

static float numMinsSinceSunrise;
static float minutesBetweenSunriseSunset;

static time_t sunriseDateTime;
static time_t sunsetDateTime;

float degreesPerMinute;
boolean isInitialised;

const unsigned long interval = 60000;   // update every 1 min as milliseconds
static unsigned long lastUpdate; // last update of storm cycle

typedef struct {
  double h;
  double s;
  double l;
} HSL;

HSL coloursOfTheDay [9] = {{30.0, hslSaturation, hslLightness}, {31.0, hslSaturation, hslLightness}, {33.0, hslSaturation, hslLightness}, {33.0, hslSaturation, hslLightness}, {60.0, hslSaturation, hslLightness}, {1.0, hslSaturation, hslLightness}, {212.0, hslSaturation, hslLightness}, {31.0, hslSaturation, hslLightness}, {30.0, hslSaturation, hslLightness}};

double previousColourHue = 0;
int count = 0;
double currentColourFromBand[3];

int previousColourOfTheDayBand = 0;
int minsInColourBand = 0;
static int colourOfTheDayBand = 0;

static time_t currentDate;

double lerp(double a, double b, double f)
{
  return a + f * (b - a);
}

Sun::Sun()
{
}

void Sun::Initialise(Adafruit_PWMServoDriver pwmDriver,time_t sunrise,time_t sunset,time_t newDate) {
      currentDate = newDate;

    FastLED.setMaxPowerInVoltsAndMilliamps(3.3,500);
    FastLED.addLeds<CHIPSET, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050); //magic happens here
    FastLED.setBrightness(BRIGHTNESS);
    pwm = pwmDriver;


    sunriseDateTime = sunrise;
    sunsetDateTime = sunset;
    Setup();
}

void Sun::Setup() {
   SetPulseLenAndRotate(SUN_START_ANGLE);

    previousColourHue = currentColourFromBand[0];
    previousColourOfTheDayBand = 0;    
    
    // Actually, it is same, what do you think about it?
    minutesBetweenSunriseSunset = (sunsetDateTime - sunriseDateTime) / 60;
    degreesPerMinute = (SUN_END_ANGLE - SUN_START_ANGLE) / minutesBetweenSunriseSunset;
    isInitialised = true;
}

bool Sun::IsInitialised() {
  return isInitialised;
}

void Sun::Reset(time_t sunrise,time_t sunset) {
    sunriseDateTime = sunrise;
    sunsetDateTime = sunset;
    Setup();
}

void Sun::SetPulseLenAndRotate(float degreeToRotateTo) {
  int pulselen = map(degreeToRotateTo, SUN_START_ANGLE, SUN_END_ANGLE, SERVOMIN, SERVOMAX);
  for(uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
      pwm.setPWM(PWM_LOCATION, 0, pulselen);
    }
  delay(500); //Give the servo time to get there;
}

void Sun::UpdateSunLEDColour() {
Serial.println("mins since sunrise");
    Serial.println(numMinsSinceSunrise);
    
    float numMinsInEachColourBand = minutesBetweenSunriseSunset / sizeof(coloursOfTheDay);

    Serial.println("num mins in each colour band");
    Serial.println(numMinsInEachColourBand);
    
    //Get the number of minutes inside each colour band so we can use it to calculate the steps we're on
    Serial.println("Previous Colour of the day band");
    Serial.println(previousColourOfTheDayBand);
    
    Serial.println("Color of the day band");
    Serial.println(colourOfTheDayBand);

    Serial.println("mins in colour band");
    Serial.println(minsInColourBand);

    Serial.println("Current colour index");
    Serial.println(colourOfTheDayBand);

    currentColourFromBand[0] = coloursOfTheDay[colourOfTheDayBand].h;
    currentColourFromBand[1] = coloursOfTheDay[colourOfTheDayBand].s;
    currentColourFromBand[2] = coloursOfTheDay[colourOfTheDayBand].l;

    Serial.println("CURRENT COLOUR FROM BAND");
    Serial.println(currentColourFromBand[0]);
    

    int nextColourOfTheDayBand = min(colourOfTheDayBand + 1, sizeof(coloursOfTheDay));
    Serial.println(nextColourOfTheDayBand);
    Serial.println("NEXT COLOUR FROM DAY BAND H");
    Serial.println(coloursOfTheDay[nextColourOfTheDayBand].h);
    double nextColourFromBand[3] = { coloursOfTheDay[nextColourOfTheDayBand].h, coloursOfTheDay[nextColourOfTheDayBand].s, coloursOfTheDay[nextColourOfTheDayBand].l};

    //Where are we in our current colour range for lerp-ing
    float stepIncrement = minsInColourBand / numMinsInEachColourBand;
    double nextColourHueLerped = lerp(previousColourHue, nextColourFromBand[0], stepIncrement);

    Serial.println("Next colour hue lerped");
    Serial.println(nextColourHueLerped);

    Serial.println("Previous Colour Hue Lerped");
    Serial.println(previousColourHue);

    Serial.println("current colour from band hsl");
    Serial.println(currentColourFromBand[0]);
    
    int distance = (int)nextColourHueLerped - previousColourHue;
    if (distance < 0)
    {
      distance = distance + 360;
    }
    
    double hueStepValue = 0.0;
    Serial.println("distance");
    Serial.println(distance);
    
    for (int i = 0; i < distance; i = i + 2)
    {
      hueStepValue = previousColourHue + i;
      
      if (hueStepValue > 359)
      {
        hueStepValue = hueStepValue - 360;
      }
      if (hueStepValue < 0)
      {
        hueStepValue = hueStepValue + 360;
      }

      Serial.print("Hue Step Value");
      Serial.print(hueStepValue);
      
      leds[0] = CHSV(hueStepValue, hslSaturation, hslLightness);
      FastLED.show();

      previousColourHue = hueStepValue;
    }

    previousColourHue = (int)nextColourHueLerped;
    minsInColourBand++;
    numMinsSinceSunrise++;

    if(minsInColourBand >= numMinsInEachColourBand) {
       Serial.println("colour of the DOES NOT match previous");
      previousColourOfTheDayBand = colourOfTheDayBand;
      colourOfTheDayBand++;
      colourOfTheDayBand = min(colourOfTheDayBand,11);
      minsInColourBand = 0;
    }
}

void Sun::Update() {
  unsigned long currentMillis = millis();

  if(now() < sunsetDateTime && now() > sunriseDateTime) {
    unsigned long secondsSinceCurrentTimeAndSunrise = sunsetDateTime  - sunriseDateTime;
    if(numMinsSinceSunrise < minutesBetweenSunriseSunset) {
      if(currentMillis - lastUpdate > interval) {
        lastUpdate = millis();

        float angleToRotateTo = numMinsSinceSunrise * degreesPerMinute;
        SetPulseLenAndRotate(angleToRotateTo);
        UpdateSunLEDColour();
        numMinsSinceSunrise++;
      }
    }
  } else {
    leds[0] = CHSV(0,0,0);
    FastLED.show();

  }
}


Relevant Parts of the Teensy Code

Code:
// CRC32
#include <FastCRC.h>
#include <ArduinoJson.h>
#include <TimeLib.h>
#define MAX_BUFFLEN 1024

FastCRC32 CRC32;
static uint16_t dataBufferIndex = 0;
static char Buff[MAX_BUFFLEN]; // This is common beffer for Serial communiaction between RPI and teensy.
static uint32_t nTime = millis();
static bool isReceivingData = false;
const uint32_t serialDataTimeout = 500;

Code:
void setup() {

  Serial.begin(9600);
  while (!Serial && millis() < 2000); // Teensy is fast so we pause here to get a chance to capture all the serial debugging
  Serial.println("Start cloud setup");

   //pciSetup(2);

  // BEGIN SETTING UP STEPPER MOTOR
  /*The device starts by setting the home position on a stepper motor using
   * a hall effect sensor. When it reaches the bottom of its run and activates the hall effect sensor
   * we are ready to begin
   */
  weatherCloud.setSpeed(1600);
  
  weatherCloud.Initialise();
  //Serial.println("End cloud initialise home");

  //END SETTING UP STEPPER MOTOR

  //BEGIN SETTING UP USER LOCATIONS
  //firstUserLocation.Initialise(pwm, 1,1);
  //secondUserLocation.Initialise(pwm, 2,1);
  
  // Serial speed set for Pi to Teensy communication
  Serial1.begin(9600);

  //Screen doesn't work yet??
  tft.begin();
   tft.fillScreen(BLACK);
  tft.setCursor(0, 5);
  tft.setTextColor(RED);  
  tft.setTextSize(1);
  tft.println("Hello World!");

  Serial.println("init");
}

Code:
uint8_t ProcessMessage(char* message)
{
  StaticJsonBuffer<MAX_BUFFLEN> jsonBuffer;
  
  JsonObject& root = jsonBuffer.parseObject(message);

  if (root == JsonObject::invalid())
    return false;

  JsonObject& weather = root["weatherinfo"].asObject();

  if(weather != JsonObject::invalid())
  {
    Serial.println("Weather Json");
    
    // This message is for weather 
    const char* tempArray = root["weatherinfo"]["temp_c"];
    String tempDegreesCelcius = String(tempArray);
    // 
    const char* weatherArray = root["weatherinfo"]["weather"];
    String weather = String(weatherArray);

    const char* precipitationArray = root["weatherinfo"]["precipitation"];//
    String precipitationDescription = String(precipitationArray);

    //This would need a lot more fleshing out and better smarts but for the purposes of a demo it's ok
    if(weather.length() > 0) {
      boolean cloudActive = false;
      String weatherLowered = weather.toLowerCase();
      if(weatherLowered.indexOf("cloudy") > 0 || weatherLowered.indexOf("clouds") > 0) {
        weatherCloud.stepperStatus = FORWARD;
        cloudActive = true;
      }
      if(weatherLowered.indexOf("lightning") > 0) {
        weatherCloud.cloudWeatherStatus = LIGHTNING;
        if(weatherLowered.indexOf("rain") > 0 || weatherLowered.indexOf("thunderstorm") > 0) {
          weatherCloud.cloudWeatherStatus = LIGHTNING_AND_RAIN;
        }
        cloudActive = true;
      }
      if(weatherLowered.indexOf("rain") > 0 || weatherLowered.indexOf("showers") > 0 || weatherLowered.indexOf("thunderstorm") > 0) {
        weatherCloud.cloudWeatherStatus = RAIN;
      }
    }

    const char* currentTimeArray = root["weatherinfo"]["currtime"];
    String currentTime = String(currentTimeArray);
    // 
    Serial.print("Currrent time is ");
    Serial.println(currentTime);
    timeFromString(currentTimeArray);

    const char* sunrise = root["weatherinfo"]["sunrise"];
    String strsunrise = String(sunrise);
    // 
    time_t sunriseTime = timeFromString(sunrise);
    //Serial.println(sunrise_time);

    const char* sunsetArray = root["weatherinfo"]["sunset"];
    String sunset = String(sunsetArray);
    time_t sunsetTime = timeFromString(sunsetArray);

    const char* dateArray = root["weatherinfo"]["date"];
    String date = String(dateArray);

    time_t dateFromWeather = dateFromString(dateArray);
    if(!sun.IsInitialised()) {
      currentDate = dateFromWeather;
    }
    //If we are in to the next day we need to reset the sun
    if(day(dateFromWeather) != day(currentDate)) {
      Serial.println("Resetting sun to the start");
      sun.Reset(sunriseTime, sunsetTime);
      currentDate = dateFromWeather;
    }

    const char* moonriseArray = root["weatherinfo"]["moonrise"];
    String moonrise = String(moonriseArray);
    // 

    const char* moonsetArray = root["weatherinfo"]["moonset"];
    String moonset = String(moonsetArray);


    if(!sun.IsInitialised()) {
      sun.Initialise(pwm, sunriseTime, sunsetTime, currentDate);
    }
  }
  else
  {
    JsonObject& Person = root["person"].asObject();
    if(Person != JsonObject::invalid())
    {
        // User ID
        int userID = Person["userID"].as<uint8_t>(); 
        Serial.println(userID);
        
        // User Name
        const char* userName = Person["userName"];
        String struserName = String(userName);
        Serial.println(struserName);

        // Location ID
        int locationID = Person["locationID"].as<uint8_t>(); 
        Serial.println(locationID);
 
      //please don't judge me.. I know this is horrible :)
      switch (userID) {
      case 1:
        firstUserLocation.RotateToLocation(locationID);
      break;
      case 2:
        secondUserLocation.RotateToLocation(locationID);
      break;
    break;
  }
    }
  }

void loop() {

  // Receing Packet 
  if(Serial1.available())
  { // If teensy is receiving data, 
    isReceivingData = true;
    nTime = millis();
    char ch = Serial1.read();
    Buff[dataBufferIndex] = ch;
    dataBufferIndex++; // 
    if(dataBufferIndex >= MAX_BUFFLEN)
      dataBufferIndex = 0;
  }

  if(nTime + serialDataTimeout < millis() && isReceivingData == true)
  { 
    // End Packet
    isReceivingData = false;
    Serial.print("Packet data length is ");
    Serial.println(dataBufferIndex - 4);

    uint32_t CRC1st = Buff[dataBufferIndex - 4];
    CRC1st <<= 8;
    CRC1st += Buff[dataBufferIndex - 3];
    CRC1st <<= 8;
    CRC1st += Buff[dataBufferIndex - 2];
    CRC1st <<= 8;
    CRC1st += Buff[dataBufferIndex - 1];

    uint32_t CRC2nd = CRC32.crc32((uint8_t*)Buff, dataBufferIndex - 4);
    
    
    if(CRC1st != CRC2nd)
    {
      Serial.println("CRC32 Check Error!");
      for(uint16_t idx = 0; idx < dataBufferIndex - 4; idx++)
        Serial.print((char)Buff[idx]);
      Serial.println();

    }
    else
    {
      Serial.println("Received message:");
      for(uint16_t idx = 0; idx < dataBufferIndex - 4; idx++)
        Serial.print((char)Buff[idx]);
      Serial.println();
      Buff[dataBufferIndex - 4] = '\0';
      
      Buff[dataBufferIndex - 3] = '\0';
      Buff[dataBufferIndex - 2] = '\0';
      Buff[dataBufferIndex - 1] = '\0';
      ProcessMessage(Buff);
    }
    dataBufferIndex = 0;
  }

  //if(!isReceivingData) {
  weatherCloud.Update();
  sun.Update();
  //}
}

Thanks for your time,
Sorry for the code dump.
Tom
 
Last edited:
Probably the first step is trying to understand if the errors are related to hardware, such as the large amount of power a servo motor draws when it's moving fast. (which would be my 1st guess) If you can run the project without the motor connected, that would be the ideal way to see whether the errors still occur without the extra power. The solution might be as simple as a better power supply or improved wiring directly from the supply to the motor, so that huge current doesn't need to flow along the same wires that are also serving as communication between boards.
 
Which RPI? If it is an RPI3, maybe issue with Baud rate is not correct on the RPI side...
I was trying out communications between a pI3 and T3.2 at a high baud rate(20000000) and it was failing. Looking at the output on Logic Analyzer the speed was not close enough to the right baud rate and it failed. I tried a 1mbaud and it appeared to be working better.

Searching the web I found there are issues with RPI3 with the hardware Serial port as the one with the better support in the RPI was taken over by BT... There may be ways to resolve this. Example: http://spellfoundry.com/2016/05/29/configuring-gpio-serial-port-raspbian-jessie-including-pi-3/ I need to get back to this and see if this makes sense.... Or I may go back to one of my Odroid boards...

If you have a scope or Logic Analyzer you might look at the serial stream and see if it is correct.
 
So it seems that CRC is not the problem, the packet is cut off before reading all the incoming message.
The problem could be that for every loop you only read one single char, and the serialDataTimeout will hit you before the complete message is read :
Code:
void loop() {

  // Receing Packet 
  if(Serial1.available())
  { // If teensy is receiving data, 
    isReceivingData = true;
    nTime = millis();
    char ch = Serial1.read();

Try something like
Code:
void loop() {

  // Receing Packet 
  [B]while ((Serial1.available() && (NotEndOfPacket))[/B]
  { // If teensy is receiving data, 
    isReceivingData = true;
    nTime = millis();
    char ch = Serial1.read();
   
    ...

    [B]/* Add some code to check for end of packet here */[/B]
 
Status
Not open for further replies.
Back
Top