Voting resources, early voting, and poll worker information - VOTE. ... Adafruit is open and shipping.
0

Garbage Characters on RGB_LCD Shield
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Garbage Characters on RGB_LCD Shield

by 3nginerd on Wed Jun 24, 2020 12:13 am

Hello,

I assembled an RGB_LCD Shield and stacked on top of my Uno Wifi Rev 2.

When using the example "Hello, World" sketch, it works fine.
However, when I initiate even the simplest communications with other items over I2C, I am getting garbage printed all over the screen (S's, U's, hieroglyphics, etc.). This is happening without the other items (specifically a Pololu Tic T249 Stepper Motor Controller) even connected to my Arduino. Only the shield and USB cable.

I saw some reference to this behavior here, but the shield is connected through the stacking header pins, so I can't exactly add series resistance. I tried implementing the SCL "wiggle" mentioned here (A5 on Uno WiFi Rev 2 instead of 21), to no effect.

Has anyone experienced this before and has any advice?

Thank you!

3nginerd
 
Posts: 19
Joined: Sat Oct 19, 2019 11:11 pm

Re: Garbage Characters on RGB_LCD Shield

by adafruit_support_bill on Wed Jun 24, 2020 5:21 am

When using the example "Hello, World" sketch, it works fine.
However, when I initiate even the simplest communications with other items over I2C,

What other devices do you have on the bus? What are their i2c addresses?

I tried implementing the SCL "wiggle" mentioned here (A5 on Uno WiFi Rev 2 instead of 21), to no effect.

I don't know why Arduino.cc markets that board as an UNO. Other than being the same color and shape, It uses a completely different processor and is NOT pin-for-pin compatible. In particular, A5 is not SCL on that board. https://store.arduino.cc/usa/arduino-uno-wifi-rev2

adafruit_support_bill
 
Posts: 78723
Joined: Sat Feb 07, 2009 10:11 am

Re: Garbage Characters on RGB_LCD Shield

by 3nginerd on Wed Jun 24, 2020 12:29 pm

Thanks for the help, Bill!
adafruit_support_bill wrote:What other devices do you have on the bus? What are their i2c addresses?

Only two of the Tic T249's mentioned previously (product page here). Their addresses are 13 and 14. I am getting the random characters just in testing when not connected to the T249's. I've had it connected to the T249's before (at breakout SDA/SCL pins) without issue.
adafruit_support_bill wrote:I don't know why Arduino.cc markets that board as an UNO. Other than being the same color and shape, It uses a completely different processor and is NOT pin-for-pin compatible. In particular, A5 is not SCL on that board. https://store.arduino.cc/usa/arduino-uno-wifi-rev2

I had spent a lot of time studying their pinout file pdf for some other capabilities (main page shown in below image). My understanding was that A5=D19 as seen at bottom left, and D19=SCL as seen as top right. I thought this was a physical property of the traces being connected for A5 to D19 and D19 to SCL, such that they are indistinguishable inputs/outputs from the software side. Can you please help me understand what I'm missing?
Image

3nginerd
 
Posts: 19
Joined: Sat Oct 19, 2019 11:11 pm

Re: Garbage Characters on RGB_LCD Shield

by adafruit_support_bill on Wed Jun 24, 2020 12:51 pm

If you post some photos showing soldering and connections we can take a look for anything else that might be causing problems.

My understanding was that A5=D19 as seen at bottom left, and D19=SCL as seen as top right.

Interesting. The diagram also shows them as being on different IO ports (PAxx vs PDxx). Not sure how they have mapped those under the hood. But we do see a lot of compatibility issues with these boards.

adafruit_support_bill
 
Posts: 78723
Joined: Sat Feb 07, 2009 10:11 am

Re: Garbage Characters on RGB_LCD Shield

by 3nginerd on Wed Jun 24, 2020 3:46 pm

adafruit_support_bill wrote:If you post some photos showing soldering and connections we can take a look for anything else that might be causing problems.


Gladly! Thanks! Sorry about the quality; I tried to zoom in as much as possible and I think I didn't get the focal length right with my phone. No other connections were made during this testing, just the shield into the uno wifi rev 2.

Image

Image Image Image ImageImageImageImage

3nginerd
 
Posts: 19
Joined: Sat Oct 19, 2019 11:11 pm

Re: Garbage Characters on RGB_LCD Shield

by adafruit_support_bill on Wed Jun 24, 2020 4:00 pm

I don't see any problems with the soldering. Can you post the code that is causing trouble?

adafruit_support_bill
 
Posts: 78723
Joined: Sat Feb 07, 2009 10:11 am

Re: Garbage Characters on RGB_LCD Shield

by 3nginerd on Wed Jun 24, 2020 7:30 pm

Of course!

To be complete, I'm showing you everything in my sketch folder. Sorry It's a mess; multi-tab file I will paste in order with normal text breaks between tabs. I wasn't yet ready for code review/critique, but now that it's out there, I welcome all pointers!

I can try to generate a simplified test case and get that back to you soon. Thanks for the help!

Autocharger.ino:
Code: Select all | TOGGLE FULL SIZE
/* Author: Andrew Roley
   Revision   Date        Comments
   0.1        18 May 20   Started with AMIS30543 accelStepper example, added another instance and renamed constants.
   0.2        18 May 20   Added Dual TB9051FTG Motor Driver (md) Shield example code
   0.3        20 May 20   Abandoned AMIS and TB9051FTG for taking too many pins
   0.4        31 May 20   Added LCD Shield Stuff
   0.5        03 Jun 20   Limit Switch pins moved to Tic
   0.6        03 Jun 20   Removed AccelSteppers as Tics can do accelerations themselves.
   0.7        03 Jun 20   branched out to multi-tab
   0.8        13 Jun 20   switched X & Y due to Roboclaw homing function limitations; started core code
   0.9        14 Jun 20   added movement functions in mm units and cleaned up claw functions
   1.0        17 Jun 20   Major functionality in place. Primarily cleanup required, plus HX711, RPi, and HTTP to go.
   1.1        18 Jun 20   Delineated to-do's between measure, design, build, program

   Coordinates: (Right hand system)
   X: Backwards out of garage
   Y: Towards car from wall
   Z: Upwards
   Yaw: about Z, Right Hand sense

   TinkerCad file showing connections: https://www.tinkercad.com/things/9lXtYzLemmG-autocharger/editel
    current: https://www.tinkercad.com/things/9lXtYzLemmG-autocharger/editel
    0.2: https://www.tinkercad.com/things/kM5H2NRF3SC-autocharger/editel

   20 INITIAL PINS
   HX711: two pins each, for SCK and DAT
   16 REMAIN
   ROBOCLAW: Serial Comms (2 pins: 0 and 1)
   14 REMAIN
   Tic Controller x 2: I2C (SDA & SCL [A4/A5])
   12 REMAIN
   LCD Backpack: I2C
   12 REMAIN
   Limit Switches x 6: 2 pins each, 4 pins to Roboclaw, 4 pins to each Tic.
   12  REMAIN


   Pin      Function
   0        Roboclaw
   1        Roboclaw
   2        HX711-Y-DAT
   3          RESERVE: PWM
   4        HX711-Y-SCK
   5          RESERVE: PWM
   6          RESERVE: PWM
   7        HX711-Z-DAT
   8        HX711-Z-SCK
   9          RESERVE: PWM
   10
   11
   12
   13
   14(A0)
   15(A1)
   16(A2)
   17(A3)
   18(A4)   SDA (I2C: LCD Screen, Tics, ...)
   19(A5)   SCL (I2C: LCD Screen, Tics, ...)

*/

/*
 * *************************************************************************************************************
 *
 *                                           GLOBAL CONSTANTS
 *
 * *************************************************************************************************************
 */

// Y/Z physical constants
const byte PITCH = 8;         // 8mm/rev for a TR8x8 lead screw (see product links below)
const byte SPR = 200;         // steps per revolution (see product links below)
const byte Y_CM_ROD_LEN = 38; // https://www.pololu.com/product/2690
const byte Z_CM_ROD_LEN = 28; // https://www.pololu.com/product/2268

// X/Yaw physical constants
const byte X_DRIVE_TEETH = 20;                                   // number of teeth on drive shaft
const byte YAW_DRIVE_TEETH = 20;                                 // number of teeth on drive shaft
const byte YAW_DRIVEN_TEETH = 50;                                // number of teeth on final output shaft
const byte GEAR_RATIO = 150;                                     // motor gearbox ratio for https://www.pololu.com/product/4697
const byte ENC_RATIO = 64;                                       // encoder counts per motor shaft revolution for pololu metal gearmotors
const unsigned int CLAW_REVS_TO_PULSES = GEAR_RATIO * ENC_RATIO; // encoder pulses per output shaft revolution

// Y/Z software constants; TO DO (Test): max speeds, microsteps under load
const unsigned int TIC_MAX_SPD = 12500;                           // pulses per second, set/measured from Tic Control Center
const unsigned int TIC_SPD_TIME = 10 * 1000;                      // speed is in pulses per 10,000 seconds
const byte MICROSTEPS = 2;                                        // set in Tic Control Center, or via library. Making constant until intended otherwise
const byte Z_MM_MAX_SPD = TIC_MAX_SPD * PITCH / SPR / MICROSTEPS; // 250: with 12,500, 8mm, 200, and 2.
const byte Y_MM_MAX_SPD = TIC_MAX_SPD * PITCH / SPR / MICROSTEPS; // 250: with 12,500, 8mm, 200, and 2.

// X/Yaw software constants:
const byte ADDRESS = 0x80;            // available addresses in Roboclaw firmware are 0x80 to 0x87 (128 to 135)
const unsigned int ACCEL_DFLT = 6000; // pulses per second squared;
                                      // TBD, up to full speed in one second seems quite reasonable
const unsigned int LAT_SPD = 6000;    // approx max encoder pps as seen in MotionStudio
const unsigned int ANG_SPD = 2000;    // approx speed to move full Yaw range in 2 seconds
                                      // (actually 5000/3, but not a round #, trying to avoid floating math)

/*
 * ************************************ CONVERSIONS ****************************************************
 */

// Y&Z conversions
const unsigned long MM2TIC_SPD = SPR * MICROSTEPS * TIC_SPD_TIME / PITCH; // 500,000 with 200, 2, 10,000, 8
const byte MM2TIC_POS = SPR * MICROSTEPS / PITCH;                         // 50 with 200, 2, 8
const byte D_MMPI = 254;                                                  // deci-millimeters per inch

// X conversions
const byte XmmPerTooth = 2; // GT2 belt: teeth are 2mm apart // TO PROGRAM: Constants uppercase
const byte XteethPerShaftRev = X_DRIVE_TEETH;
const byte XmmToPulses = CLAW_REVS_TO_PULSES / XmmPerTooth / XteethPerShaftRev;
// 240 with given encoder & gear ratios, belt, and drive gear

// Yaw conversions
const unsigned long centiDegPerRevOut = 36000;   // 360 * 100 was getting integer overflow warning
const byte YawTeethPerRevOut = YAW_DRIVEN_TEETH; // teeth traveled on final output shaft
const byte YawTeethPerRevIn = YAW_DRIVE_TEETH;   // teeth on output shaft = teeth on input shaft
const byte YawPulsesToMilliDeg = 10 * centiDegPerRevOut * YawTeethPerRevIn / YawTeethPerRevOut / CLAW_REVS_TO_PULSES;
// 15 with given encoder & gear ratios, belt, and drive/driven gears

/*
 * ************************************ Hardcoded Position Information **********************************
 */

// hardcoded safety limits (TO DESIGN / consider: implement limit switches
const byte maxXin = 31; // max X travel distance (should use limit switch)
const int maxXmm = maxXin * D_MMPI / 10;
const byte SAFE_YAW_DEG = 80; // measured max Yaw Degrees without worrying about Y extent (should use limit switch)
const int SAFE_CENTI_DEG = SAFE_YAW_DEG * 100;
const int MAX_CENTI_DEG = 90 * 100;

// staging for Port Insertion/Extraction
const byte YmmSafeOffset = 254;          // one inch away from goal
const unsigned int YmmExtractDist = 500; // dist to fully extract from port
const byte YmmRedzone = 25;              // last 25mm of insertion where alignment is almost certainly angular at port

/*
// known safe positions go here; TO CONSIDER: Verify them! (and decide when to use)
//const unsigned int CentiYawSafe = ;
//const unsigned int XmmSafe;
//const unsigned int ZmmSafe;
*/

// positions to stow for next time
const unsigned int stowCentiDeg = 90 *100; // TO PROGRAM: Decied on consistent CentiYaw vs CentiDeg convention.
const unsigned int stowXmm = maxXmm;
const byte stowYmm = 57; // measured

/*
 * *************************************************************************************************************
 *
 *                                              GLOBAL VARIABLES
 *
 * *************************************************************************************************************
 */

// Process variables
char phase = 'S';    // N(one) I start the program doing samples; available chars: https://www.arduino.cc/en/Reference/ASCIIchart
                     // fuller here: https://en.cppreference.com/w/cpp/language/ascii
byte step = 255;     // initialize to step which should never exist

// Target (the charge port) Data to be filled from RPi
unsigned int XmmTarget = 0;      // TO DESIGN: Determine safe initialization for these and goals
unsigned int YmmTarget = 0;      // safe initialization: clear car
unsigned int ZmmTarget = 0;      // safe initialization: clear mirror
unsigned int centiDegTarget = 0; // safe initialization: clear car body and Horizontal H beam

// Encoder/Position Data passed directly from Tics & Claw:
long YCurrPosition = 0;
long ZCurrPosition = 0;
long YawCurrEnc = 0;
long XCurrEnc = 0;
// Goal Data to be calculated for joint positions
unsigned int XmmArmGoal = 0;                   // safe initialization
unsigned int YmmArmGoal = 0;                   // safe initialization: clear car
unsigned int YmmReady = 0;                     // ready position safely outside port
unsigned int ZmmArmGoal = 2800;                // safe initialization: clear mirror upwards
unsigned int centiDegArmGoal = SAFE_CENTI_DEG; // safe initialization: clear car body and Horizontal H beam
// Claw/Tic status vars: what they are driving to.
unsigned long YawGoalEnc = 0;
unsigned long XGoalEnc = 0;
unsigned long YGoalPosition = 0;
unsigned long ZGoalPosition = 0;

// booleans for readability
bool XinPosition = false;
bool YinPosition = false;
bool ZinPosition = false;
bool YawInPosition = false;


Claw.ino:

Code: Select all | TOGGLE FULL SIZE
// See Claw_Functions for a list of library functions
// This code assumes:
// motor controlling  X  movement is attached to M1 / Enc1
// motor controlling Yaw movement is attached to M2 / Enc2

//Includes required to use Roboclaw library

#include "RoboClaw.h"

// Uno Wifi: Serial1 (that's a one) is pins 0 and 1; Serial is only USB; Serial2 goes to NINA
RoboClaw roboclaw(&Serial1, 10000); // 10,000 microseconds command timeout before motor shutdown

void clawSetup()
{
  // Open roboclaw serial ports, Values between 2400-460800 bps are available
  roboclaw.begin(460800); //On wifi board, increase Serial comms to 460800bps.
  //
}

void setXmmSpeed(int XmmSpeed)
{
  XmmSpeed = constrain(XmmSpeed, -LAT_SPD, LAT_SPD);
  int Xspeed = XmmSpeed * XmmToPulses;
  roboclaw.SpeedM1(ADDRESS, Xspeed);
}

void setYawCentiOmega(int centiOmega)
{
  centiOmega = constrain(centiOmega, -ANG_SPD, ANG_SPD);
  int YawSpeed = centiOmega * 10 / YawPulsesToMilliDeg;
  roboclaw.SpeedM2(ADDRESS, YawSpeed);
}

void setYawWithXSpeed(int centiOmega)
{
  int YawSpeed = centiOmega * 10 / YawPulsesToMilliDeg;
  int Xspeed;                                    // = - fwdDiffKinXYaw(YawSpeed); // forward differential kinematics in x direction, given YAW speed
  roboclaw.SpeedM1M2(ADDRESS, Xspeed, YawSpeed); // TO PROGRAM: Jacobian to find X speed given Xe = 0, YawSpeed is above.
}

void homeX()
{
  setXmmSpeed(-25); // TO MEASURE: determine speed here
}

void homeYaw()
{
  setYawCentiOmega(-10); // TO MEASURE: determine speed here
}

byte goToXmm(int Xmm, uint32_t accel = ACCEL_DFLT, uint32_t speed = LAT_SPD,
             uint32_t deccel = ACCEL_DFLT, uint8_t flag = 0)
{
  if (Xmm > maxXmm)
    return 1;
  if (Xmm < 0)
    return 2;
  XGoalEnc = Xmm * XmmToPulses;                                                       // convert to actual shaft revs, position max is 292608 for 48"
  roboclaw.SpeedAccelDeccelPositionM2(ADDRESS, accel, speed, deccel, XGoalEnc, flag); // end with 1 to put command in buffer
  return 0;
}

byte goToYawCentiDeg(int centiDeg, uint32_t accel = ACCEL_DFLT, uint32_t speed = ANG_SPD,
                     uint32_t deccel = ACCEL_DFLT, uint8_t flag = 0)
{
  if (centiDeg > MAX_CENTI_DEG)
    return 1;
  if (centiDeg < 0)
    return 2;
  YawGoalEnc = centiDeg * 10 / YawPulsesToMilliDeg;
  roboclaw.SpeedAccelDeccelPositionM1(ADDRESS, accel, speed, deccel, YawGoalEnc, flag);
  return 0;
}

byte updateEncCounts()
{
  bool validX, validYaw;
  byte statusX, statusYaw;
  XCurrEnc = roboclaw.ReadEncM1(ADDRESS, &statusX, &validX);
  YawCurrEnc = roboclaw.ReadEncM2(ADDRESS, &statusYaw, &validYaw);
  if (!(statusX && statusYaw))
    return 1;
  else
    return 0;
}

void clawSample()
{
  roboclaw.ForwardM1(ADDRESS, 64);         //start Motor1 forward at half speed
  roboclaw.BackwardM2(ADDRESS, 64);        //start Motor2 backward at half speed
  delayWhileResettingCommandTimeout(2000); // this is to keep tics alive

  roboclaw.BackwardM1(ADDRESS, 64);
  roboclaw.ForwardM2(ADDRESS, 64);
  delayWhileResettingCommandTimeout(2000);

  roboclaw.ForwardBackwardM1(ADDRESS, 96); //start Motor1 forward at half speed
  roboclaw.ForwardBackwardM2(ADDRESS, 32); //start Motor2 backward at half speed
  delayWhileResettingCommandTimeout(2000);

  roboclaw.ForwardBackwardM1(ADDRESS, 32);
  roboclaw.ForwardBackwardM2(ADDRESS, 96);
  delayWhileResettingCommandTimeout(2000);
}

CommsExample.ino:

Code: Select all | TOGGLE FULL SIZE
 /* Manual: https://www.tesla.com/sites/default/files/model_3_owners_manual_north_america_en.pdf
  * Support Article: https://www.tesla.com/support/charging#:~:text=what%20do%20the%20colors,fault%20message
  * What do the colors mean around the charge port?
  *
  * White: The charge port door is open. Your car is ready to charge, but the connector is not inserted, or the latch is released and the connector is ready to be removed.
  * Blue: Your car detects that a connector has been plugged in.
  * Blinking Blue: Your car is communicating with the connector. Either it is preparing to charge, or a charging session is scheduled to begin at a specified future time.
  * Blinking Green: Charging is in progress. As your car approaches a full charge, the frequency of the blinking slows.
  * Solid Green: Charging is complete.
  * Solid Amber: The connector is not fully plugged in. Realign the connector to the charge port and insert fully.
  * Blinking Amber: Your car is charging at a reduced current (AC charging only).
  * Red: A fault is detected and charging has stopped. Check the instrument panel or touchscreen for a fault message.
  *
  */


// From https://forum.arduino.cc/index.php?topic=225329.0
// See also: https://forum.arduino.cc/index.php?topic=396450.0
// See also also for simple comms: https://roboticsbackend.com/raspberry-pi-arduino-serial-communication/

#include <Servo.h>

Servo myServo;
byte servoPin = 8;
byte servoMin = 10;
byte servoMax = 170;
byte servoPos = 0;
byte newServoPos = servoMin;

const byte numLEDs = 2;
byte ledPin[numLEDs] = {12, 13};
unsigned long LEDinterval[numLEDs] = {200, 400};
unsigned long prevLEDmillis[numLEDs] = {0, 0};

const byte buffSize = 40;
char inputBuffer[buffSize];
const char startMarker = '<';
const char endMarker = '>';
byte bytesRecvd = 0;
boolean readInProgress = false;
boolean newDataFromRPi = false;

char messageFromRPi[buffSize] = {0};
int newFlashInterval = 0;
float servoFraction = 0.0; // fraction of servo range to move


unsigned long curMillis;

unsigned long prevReplyToRPimillis = 0;
unsigned long replyToRPiinterval = 1000;

//=============

void commsExSetup() {
  Serial.begin(57600);
 
    // flash LEDs so we know we are alive
  for (byte n = 0; n < numLEDs; n++) {
     pinMode(ledPin[n], OUTPUT);
     digitalWrite(ledPin[n], HIGH);
  }
  delay(500); // delay() is OK in setup as it only happens once
 
  for (byte n = 0; n < numLEDs; n++) {
     digitalWrite(ledPin[n], LOW);
  }
 
    // initialize the servo
  myServo.attach(servoPin);
  moveServo();
 
    // tell the RPi we are ready
  Serial.println("<Arduino is ready>");
}

//=============

void commsExLoop() {
  curMillis = millis();
  getDataFromRPi();
  updateFlashInterval();
  updateServoPos();
  replyToRPi();
  flashLEDs();
  moveServo();
}

//=============

void getDataFromRPi() {

    // receive data from RPi and save it into inputBuffer
   
  if(Serial.available() > 0) {

    char x = Serial.read();

      // the order of these IF clauses is significant
     
    if (x == endMarker) {
      readInProgress = false;
      newDataFromRPi = true;
      inputBuffer[bytesRecvd] = 0;
      parseData();
    }
   
    if(readInProgress) {
      inputBuffer[bytesRecvd] = x;
      bytesRecvd ++;
      if (bytesRecvd == buffSize) {
        bytesRecvd = buffSize - 1;
      }
    }

    if (x == startMarker) {
      bytesRecvd = 0;
      readInProgress = true;
    }
  }
}

//=============
 
void parseData() {

    // split the data into its parts
   
  char * strtokIndx; // this is used by strtok() as an index
 
  strtokIndx = strtok(inputBuffer,",");      // get the first part - the string
  strcpy(messageFromRPi, strtokIndx); // copy it to messageFromRPi
 
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  newFlashInterval = atoi(strtokIndx);     // convert this part to an integer
 
  strtokIndx = strtok(NULL, ",");
  servoFraction = atof(strtokIndx);     // convert this part to a float

}

//=============

void replyToRPi() {

  if (newDataFromRPi) {
    newDataFromRPi = false;
    Serial.print("<Msg ");
    Serial.print(messageFromRPi);
    Serial.print(" NewFlash ");
    Serial.print(newFlashInterval);
    Serial.print(" SrvFrac ");
    Serial.print(servoFraction);
    Serial.print(" SrvPos ");
    Serial.print(newServoPos);
    Serial.print(" Time ");
    Serial.print(curMillis >> 9); // divide by 512 is approx = half-seconds
    Serial.println(">");
  }
}

//============

void updateFlashInterval() {

   // this illustrates using different inputs to call different functions
  if (strcmp(messageFromRPi, "LED1") == 0) {
     updateLED1();
  }
 
  if (strcmp(messageFromRPi, "LED2") == 0) {
     updateLED2();
  }
}

//=============

void updateLED1() {

  if (newFlashInterval > 100) {
    LEDinterval[0] = newFlashInterval;
  }
}

//=============

void updateLED2() {

  if (newFlashInterval > 100) {
    LEDinterval[1] = newFlashInterval;
  }
}

//=============

void flashLEDs() {

  for (byte n = 0; n < numLEDs; n++) {
    if (curMillis - prevLEDmillis[n] >= LEDinterval[n]) {
       prevLEDmillis[n] += LEDinterval[n];
       digitalWrite( ledPin[n], ! digitalRead( ledPin[n]) );
    }
  }
}

//=============

void updateServoPos() {

  byte servoRange = servoMax - servoMin;
  if (servoFraction >= 0 && servoFraction <= 1) {
    newServoPos = servoMin + ((float) servoRange * servoFraction);
  }
}

//=============

void moveServo() {
  if (servoPos != newServoPos) {
    servoPos = newServoPos;
    myServo.write(servoPos);
  }
}


HX711.ino:

Code: Select all | TOGGLE FULL SIZE
// for two or more HX711 modules; Settling time (number of samples)
// and data filtering can be adjusted in the config.h file

#include <HX711_ADC.h>
#include <EEPROM.h>

//pins:
const int HX711_dout_X = 2; //mcu > HX711 no 1 dout pin
const int HX711_sck_X = 4; //mcu > HX711 no 1 sck pin
const int HX711_dout_Z = 7; //mcu > HX711 no 2 dout pin
const int HX711_sck_Z = 8; //mcu > HX711 no 2 sck pin

//HX711 constructor (dout pin, sck pin)
HX711_ADC LoadX(HX711_dout_X, HX711_sck_X); //HX711 for X force
HX711_ADC LoadZ(HX711_dout_Z, HX711_sck_Z); //HX711 for Z force

const int calVal_eepromAdress_X = 0; // eeprom address for calibration value load cell 1 (4 bytes)
const int calVal_eepromAdress_Z = 4; // eeprom address for calibration value load cell 2 (4 bytes)
long t;

int YawXforce = 0; // may split these into two load cells later
int Zforce = 0; // TO DO: Determine best data type
const byte forceScale = 100; // TO DO: determine good # / data type based on Measure below
const int forceMagnitudeZ = 1000; // TO DO: Measure!
const int forceMagnitudeYawX = 1000; // may split into two load cells later

void HX711setup() {
  Serial.begin(57600); delay(10);
  Serial.println();
  Serial.println("Starting...");

  float calibrationValueX; // calibration value load cell X
  float calibrationValueZ; // calibration value load cell Z

  calibrationValueX = 696.0; // uncomment this if you want to set this value in the sketch
  calibrationValueZ = 733.0; // uncomment this if you want to set this value in the sketch
#if defined(ESP8266) || defined(ESP32)
  //EEPROM.begin(512); // uncomment this if you use ESP8266 and want to fetch the value from eeprom
#endif
  //EEPROM.get(calVal_eepromAdressX, calibrationValueX); // uncomment this if you want to fetch the value from eeprom
  //EEPROM.get(calVal_eepromAdressZ, calibrationValueZ); // uncomment this if you want to fetch the value from eeprom

  LoadX.begin();
  LoadZ.begin();
  long stabilizingtime = 2000; // tare preciscion can be improved by adding a few seconds of stabilizing time
  boolean _tare = true; //set this to false if you don't want tare to be performed in the next step
  byte LoadX_rdy = 0;
  byte LoadZ_rdy = 0;
  while ((LoadX_rdy + LoadZ_rdy) < 2) { //run startup, stabilization and tare, both modules simultaniously
    if (!LoadX_rdy) LoadX_rdy = LoadX.startMultiple(stabilizingtime, _tare);
    if (!LoadZ_rdy) LoadZ_rdy = LoadZ.startMultiple(stabilizingtime, _tare);
  }
  if (LoadX.getTareTimeoutFlag()) {
    Serial.println("Timeout, check MCU>HX711_X wiring and pin designations");
  }
  if (LoadZ.getTareTimeoutFlag()) {
    Serial.println("Timeout, check MCU>HX711_Z wiring and pin designations");
  }
  LoadX.setCalFactor(calibrationValueX); // user set calibration value (float)
  LoadZ.setCalFactor(calibrationValueZ); // user set calibration value (float)
  Serial.println("Startup is complete");
}

void HX711sample() {
  static boolean newDataReady = 0;
  const int serialPrintInterval = 0; //default 500, increase value to slow down serial print activity

  // check for new data/start next conversion:
  if (LoadX.update()) newDataReady = true;
  LoadZ.update();

  //get smoothed value from data set
  if ((newDataReady)) {
    if (millis() > t + serialPrintInterval) {
      YawXforce = forceScale * LoadX.getData();
      Zforce = forceScale * LoadZ.getData();
      Serial.print("Load_cell YawX output val: ");
      Serial.print(YawXforce);
      Serial.print("    Load_cell Z output val: ");
      Serial.println(Zforce);
      newDataReady = 0;
      t = millis();
    }
  }

  // receive command from serial terminal, send 't' to initiate tare operation:
  if (Serial.available() > 0) {
    float i;
    char inByte = Serial.read();
    if (inByte == 't') {
      LoadX.tareNoDelay();
      LoadZ.tareNoDelay();
    }
  }

  //check if last tare operation is complete
  if (LoadX.getTareStatus() == true) {
    Serial.println("Tare load cell 1 complete");
  }
  if (LoadZ.getTareStatus() == true) {
    Serial.println("Tare load cell 2 complete");
  }

}


JsonHTTPclientExample (not currently utilizing any Methods, but possibly conflicting, haven't verified):

Code: Select all | TOGGLE FULL SIZE
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
//
// This example shows how to parse a JSON document in an HTTP response.
// It uses the Ethernet library, but can be easily adapted for Wifi.
//
// It performs a GET resquest on arduinojson.org/example.json
// Here is the expected response:
// {
//   "sensor": "gps",
//   "time": 1351824120,
//   "data": [
//     48.756080,
//     2.302038
//   ]
// }
//
// https://arduinojson.org/v6/example/http-client/

#include <ArduinoJson.h>
#include <Ethernet.h> // TO DO: Exchange Ethernet for WifiNina and correct Serial #
#include <SPI.h>

void JSONsetup() {
  // Initialize Serial port
  Serial.begin(57600);
  while (!Serial) continue;

  // Initialize Ethernet library
  byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  if (!Ethernet.begin(mac)) {
    Serial.println(F("Failed to configure Ethernet"));
    return;
  }
  delay(1000);

  Serial.println(F("Connecting..."));

  // Connect to HTTP server
  EthernetClient client;
  client.setTimeout(10000);
  if (!client.connect("arduinojson.org", 80)) {
    Serial.println(F("Connection failed"));
    return;
  }

  Serial.println(F("Connected!"));

  // Send HTTP request
  client.println(F("GET /example.json HTTP/1.0"));
  client.println(F("Host: arduinojson.org"));
  client.println(F("Connection: close"));
  if (client.println() == 0) {
    Serial.println(F("Failed to send request"));
    return;
  }

  // Check HTTP status
  char status[32] = {0};
  client.readBytesUntil('\r', status, sizeof(status));
  // It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
  if (strcmp(status + 9, "200 OK") != 0) {
    Serial.print(F("Unexpected response: "));
    Serial.println(status);
    return;
  }

  // Skip HTTP headers
  char endOfHeaders[] = "\r\n\r\n";
  if (!client.find(endOfHeaders)) {
    Serial.println(F("Invalid response"));
    return;
  }

  // Allocate the JSON document
  // Use arduinojson.org/v6/assistant to compute the capacity.
  const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
  DynamicJsonDocument doc(capacity);

  // Parse JSON object
  DeserializationError error = deserializeJson(doc, client);
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.c_str());
    return;
  }

  // Extract values
  Serial.println(F("Response:"));
  Serial.println(doc["sensor"].as<char*>());
  Serial.println(doc["time"].as<long>());
  Serial.println(doc["data"][0].as<float>(), 6);
  Serial.println(doc["data"][1].as<float>(), 6);

  // Disconnect
  client.stop();
}

void JSONloop() {
  // not used in this example
}

// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization  problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization
// showing how to parse the response from GitHub's API. In the last chapter,
// it shows how to parse the huge documents from OpenWeatherMap
// and Reddit.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤


Kinematics.ino:

Code: Select all | TOGGLE FULL SIZE
// TO MEASURE: these thetas in better light!
const byte theta2a = 20; // for base angled arm, original measure and as-built
const byte theta2b = 22; // for sliding arm, as measured 2 degrees greater than base arm; liely due to gravity load.
                         // plugs in better if starting a little low; slop can then move up or down.
                         // after getting alignment near perfect

// TO MEASURE: Meaure offset of targets from end effector; assuming all less than ~65mm for now.
const unsigned int X_off_mm, Y_off_mm, Z_off_mm, YAW_off_centi_Deg;

// all in microns
unsigned long XmumE, YmumE, ZmumE; // mum = µm, micrometers, ints good for <2.58 in

double YAW;

const unsigned int measure_1, measure_2; // TO MEASURE
const unsigned int L_1 = (1.5 * 2 + .75 - measure_1 - 1.5 / 2 - .25) * D_MMPI * 100; // μm
const unsigned int L_2 = 0.75 * D_MMPI * 100;                                        // μm
const unsigned long L_3 = measure_2 * D_MMPI * 100;                                  // μm, > 2.58" (see above)!
const unsigned int L_4 = (2.375 - 0.616) * D_MMPI * 100;                             // μm
const unsigned int L_5 = (0.75 + 0.5) * D_MMPI * 100;                                // μm
const unsigned int L_6 = 30100;                                                      // μm
const unsigned int L_7 = 2 * D_MMPI * 100 + 9;                                       // μm
const unsigned int L_8 = .25 * D_MMPI * 100;                                         // μm

const double S_2 = sin(theta2a / 360 * M_PI);
const double C_2 = cos(theta2a / 360 * M_PI);

// To simplify equations with parallel lengths:
const unsigned int L_156 = L_1 - L_5 - L_6;
const unsigned long L_37 = L_3 + L_7;
const double L_48 = L_4 + L_8;

double sinD(double DegIn)
{
  return sin(DegIn / 360 * M_PI);
}

double cosD(double DegIn)
{
  return cos(DegIn / 360 * M_PI);
}

void getPose(bool actual = true) // relies on housekeeping call immediately before, and proper homing
{                                //
  unsigned long d_X, d_Z, d_Y;
  double c_1, s_1;
  YAW = 1.0 * YawCurrEnc * YawPulsesToMilliDeg / 1000; // 1.0 casts following to double
  d_X = 1.0 * XCurrEnc / XmmToPulses * 1000;           // 1.0 casts following to double
  d_Y = 1.0 * YCurrPosition / MM2TIC_POS * 1000;       // 1.0 casts following to double
  d_Z = 1.0 * ZCurrPosition / MM2TIC_POS * 1000;       // 1.0 casts following to double
  s_1 = sinD(YAW);
  c_1 = cosD(YAW);

  XmumE = d_X + L_156 * c_1 - L_2 * s_1 - (d_Y + L_37) * C_2 * s_1 - L_48 * s_1 * S_2;
  YmumE = L_156 * s_1 + L_2 * c_1 + (d_Y + L_37) * C_2 * c_1 + L_48 * c_1 * S_2;
  ZmumE = d_Z - (d_Y + L_37) * S_2 + L_48 * C_2;
}

void getGoalsFromTarget()
{

  unsigned long XmumETarg = (XmmTarget + X_off_mm) * 1000;
  unsigned long YmumETarg = (YmmTarget + Y_off_mm) * 1000;
  unsigned long ZmumETarg = (ZmmTarget + Z_off_mm) * 1000;

  unsigned long d_X, d_Z, d_Y;
  double c_1, s_1;
  double YAW_Targ = (centiDegTarget + YAW_off_centi_Deg) / 100.0; // 100.0 casts operation to double (only one operation so end is okay)
  centiDegArmGoal = centiDegTarget + YAW_off_centi_Deg;
  s_1 = sinD(YAW_Targ);
  c_1 = cosD(YAW_Targ);
  d_X = (XmumETarg * c_1 + YmumETarg * s_1 - L_156) / c_1;
  d_Y = (c_1 * (-C_2 * L_37 - L_48 * S_2 - L_2) - L_156 * s_1 + YmumETarg) / (C_2 * c_1);
  d_Z = ((C_2 * ZmumETarg - L_2 * S_2 - L_48) * c_1 - S_2 * (L_156 * s_1 - YmumETarg)) / (C_2 * c_1);
  XmmArmGoal = d_X / 1000; // d's are in microns, goals are in mm; divide by 1000 to convert
  YmmArmGoal = d_Y / 1000; // d's are in microns, goals are in mm; divide by 1000 to convert
  ZmmArmGoal = d_Z / 1000; // d's are in microns, goals are in mm; divide by 1000 to convert
}


LCD.ino:

Code: Select all | TOGGLE FULL SIZE
/*
   LCD includes and examples
*/

#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <utility/Adafruit_MCP23017.h>

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield(); // for uno wifi fix class def w/ "arduino::Print"

// These #defines make it easy to set the backlight color
//    optimize by removing? (NO, define is macro done beforehand)
#define OFF 0x0
#define RED 0x1
#define GREEN 0x2
#define YELLOW 0x3
#define BLUE 0x4
#define VIOLET 0x5
#define TEAL 0x6
#define WHITE 0x7
uint8_t buttons;

void lcdSetup()
{
  pinMode(19, OUTPUT);
  for (int i = 0; i < 8; i++) {
    digitalWrite(21, HIGH);
    delayMicroseconds(3);
    digitalWrite(21, LOW);
    delayMicroseconds(3);
  }
  pinMode(19, INPUT);
  lcd.begin(16, 2); // set up the LCD's number of columns and rows:
}

void lcdHelloWorld()
{
  Serial.begin(57600); // LCD Debugging startup
  int time = millis();
  lcd.print("Hello, world!");
  time = millis() - time;
  Serial.print("Took "); Serial.print(time); Serial.println(" ms"); // LCD Debug message
  Serial.print("SCL stands for: ");Serial.println(SCL);
  lcd.setBacklight(WHITE);
}

void lcdSample()
{
//  uint8_t buttons = lcd.readButtons(); // copied to housekeeping, which doesn't happen when sampling?

//  lcd.setCursor(0, 1); // set the cursor to column 0, line 1 (first line is 0)
//  // print the number of seconds since reset:
//  lcd.print(millis() / 1000);

  if (buttons) {
    lcd.clear();
    lcd.setCursor(0, 0);
    if (buttons & BUTTON_UP) {
      lcd.print("UP ");
      lcd.setBacklight(RED);
    }
    if (buttons & BUTTON_DOWN) {
      lcd.print("DOWN ");
      lcd.setBacklight(YELLOW);
    }
    if (buttons & BUTTON_LEFT) {
      lcd.print("LEFT ");
      lcd.setBacklight(GREEN);
    }
    if (buttons & BUTTON_RIGHT) {
      lcd.print("RIGHT ");
      lcd.setBacklight(TEAL);
    }
    if (buttons & BUTTON_SELECT) {
      lcd.print("SELECT ");
      lcd.setBacklight(VIOLET);
    }
  }

}


Tic.ino:

Code: Select all | TOGGLE FULL SIZE
/*
   Y,Z includes and initializations

    In Tic Control Center:
    Each Tic's control mode must be set to "Serial/I2C/USB".
    Serial device numbers: one left @ default (14), other set to 13

    Arduino GND, SCL (A5), and SDA (A4) pins to matching Tic pins.
    Recommend connecting ERR lines of each Tics so error on one will
    shut down both
    until you reset the Arduino.

    See the comments and instructions in I2CSpeedControl.ino for
    more information.
*/

#include <Tic.h>
TicI2C ticY(14);     // default I2C address is 14; don't need to specify
TicI2C ticZ(13); // must set in Tic Control Center software. Range: 0 to 16383

void TICsetup()
{
  Wire.begin(); // Set up I2C
  delay(20);    // Give Tic some time to start up

  ticY.exitSafeStart();
  ticZ.exitSafeStart();
  /*
     Tells the Tic that it is OK to start driving the motor.  The
     Tic's safe-start feature helps avoid unexpected, accidental
     movement of the motor: if an error happens, the Tic will not
     drive the motor again until it receives the Exit Safe Start
     command.  The safe-start feature can be disbled in the Tic
     Control Center.
  */
}

void keepTICsAlive()
{
  ticY.resetCommandTimeout();
  ticZ.resetCommandTimeout();

  /* Sends a "Reset command timeout" command to each Tic.  We must
     call this at least once per second, or else a command timeout
     error will happen.  The Tic's default command timeout period
     is 1000 ms, but it can be changed or disabled in the Tic
     Control Center.
  */
}

void goToYmm(unsigned int Ymm)
{

  YGoalPosition = Ymm * MM2TIC_POS;
  ticY.setTargetPosition(YGoalPosition);
}

void goToZmm(unsigned int Zmm)
{
  ZGoalPosition = Zmm * MM2TIC_POS;
  ticZ.setTargetPosition(ZGoalPosition);
}

void updateTicPositions()
{
  YCurrPosition= ticY.getCurrentPosition();
  ZCurrPosition=ticZ.getCurrentPosition() ;

  YinPosition = YGoalPosition == YCurrPosition;
  ZinPosition = ZGoalPosition == ZCurrPosition;
}

void delayWhileResettingCommandTimeout(uint32_t ms) // BLOCKING!!
{
  /*
     Delays for the specified number of milliseconds while
     resetting the Tic's command timeout so that its movement does
     not get interrupted by errors.
  */

  uint32_t start = millis();
  do
  {
    keepTICsAlive();
  } while ((uint32_t)(millis() - start) <= ms);
}

void ticSample()
{

 
}


Z_main_functions.ino:

Code: Select all | TOGGLE FULL SIZE
// this is the last tab to ensure previous function definitions are above.

void setup()
{
  // accessories/sensor setup
  lcdSetup();
  lcdHelloWorld(); // edit/remove Hello later
  HX711setup();
  TICsetup();  // Z and Y Setup
  clawSetup(); // Yaw (M1) and X (M2) Setup
  commsExSetup();
}

void loop()
{
  housekeeping();

//  Serial.print("I'm in phase: "); Serial.println(phase);
  switch (phase)
  {
    case 'S':
      samples();
      break;
    case 'I':
      handleID();
      break; //I(dentify) car (in progress/to be done)
    case 'L':
      handleLoc();
      break; //L(ocalize) (get close to port)
    case 'F':
      handleFix();
      break; //F(ix) precise location once known
    case 'E':
      handleEng();
      break; //E(ngage) in plug-in operation
    case 'A':
      handleAss();
      break; //A(ssess)
    case 'R':
      handleRst();
      break; //R(eset)
    default:
      break; // TO DO: send an error, "Unexpected Phase"
  }
}

// all the stuff to do every loop
byte housekeeping()
{
  /****************************************CLAW KEEPING****************************************/
  updateEncCounts();
  XinPosition = XCurrEnc == XGoalEnc;
  YawInPosition = YawCurrEnc == YawGoalEnc;
  // keepClawAlive(); TO DO: implement similar to TICS, command with minimal write and read ops (Candidate: 90, read status)

  /****************************************COMM KEEPING****************************************/
  curMillis = millis(); // for data transfer
  getDataFromRPi();     // TO DO: make specific to me
  getGoalsFromTarget();
  replyToRPi(); // TO DO: make specific to me

  /****************************************TO PROGRAM: HX711 KEEPING****************************************/

  /****************************************LCD  KEEPING****************************************/
  buttons = lcd.readButtons(); // originally declared in same scope as button actions, but I moved declaration to LCD header

  lcd.setCursor(0, 1); // set the cursor to column 0, line 1 (first line is 0)
  // print the number of seconds since reset:
  lcd.print("Phase: "); lcd.print(phase); lcd.print("."); lcd.print(step);



  /****************************************TIC  KEEPING****************************************/
    keepTICsAlive(); // TO DO: make these fire only every so often (Or eliminate since I want to frequently check for pos)
    updateTicPositions();

  // WISH: halt all motors and notify error if outside ranges dependent on phase. (Engage phase expects large values)

}


// TO DO: add switch, case and checks for each position command in every handle before continuing

void handleID()
{
  // TO DO: De-energize everything, go to sleep (maybe) until RasPi tells us to wake up
  //  TO DO: upon wake, Tare HX711's and check for all zeros or huge numbers
  // TO DO: tics exit safe start & wake claw if needed
  if (strcmp(messageFromRPi, "ID") == 0)
    phase = 'L'; // TO DO: else report error
}

void handleLoc()
{
  // mostly reset all positions
  switch (step) {
    case 0: homeX(); step++; break; // moves to motor and resets encoders
    case 1: if (XCurrEnc <= 0) { // TO DO: implement status check on page 74 for home bit mask
        goToXmm(XmmArmGoal); step++;
      } break;
    case 2:
      if (XinPosition)
      {
        ticZ.goHomeForward(); // raises up to clear car when opening;
        step++;
      }
      break;
    case 3:
      if (!ticZ.getHomingActive())
      {
        goToYawCentiDeg(SAFE_CENTI_DEG); // TO DO: determine whether on restart it is safest to move back a set amount
        // or to do homing to zero
        step++;
      }
      break;
    case 4:
      if (YawInPosition)
      {
        ticY.goHomeReverse();
        step++;
      }
      break;
    case 5:
      if (!ticY.getHomingActive())
      {
        homeYaw(); // moves and resets encoders;
        step++;
      }
      break;

    /*

      start to get in local position

    */

    case 6: if (YawCurrEnc <= 0) { // TO PROGRAM: implement status check on page 74 for home bit mask
        goToYawCentiDeg(SAFE_CENTI_DEG); step++;
      } break;
    case 7: if (YawInPosition) {
        YmmReady = max(YmmArmGoal - YmmExtractDist - YmmSafeOffset, 0); goToYmm(YmmReady); step++;
      } break;
    case 8: if (YinPosition) {
        ticZ.goHomeReverse(); step++;// gets goal into positive realm & makes it easier to see april tag
      } break;
    case 9: if (!ticZ.getHomingActive()) {
        // TO PROGRAM: Send Open Command through Tesla API

        step++;
      } break;
    case 10: if (strcmp(messageFromRPi, "Fix") == 0) {
        phase = 'F'; step = 0;
      } break;
    default:
      // Serial.print("invalide localization phase step value; value is: " + step); // TO DO: print location step error
      // and remove gargabe below.
      byte a = 0;
  }
}

void handleFix()
{
  // order of operations:

  // goToZmm(ZmmArmGoal);
  // goToXmm(XmmArmGoal);
  // YmmReady = max(YmmArmGoal - (2 + 1) * D_MMPI / 10 , 0); // Goal - two in. (extracted) - one in. (safety)
  // goToYmm(YmmReady);
  // goToYawCentiDeg(centiDegArmGoal);
  // if (YawInPosition)phase = 'E';

  switch (step)
  { case 0: goToZmm(ZmmArmGoal); step++; break; // moves to motor and resets encoders
    case 1: if (ZinPosition) {
        goToXmm(XmmArmGoal);
        step++;
      } break;
    case 2: if (XinPosition) {
        YmmReady = max(YmmArmGoal - YmmExtractDist - YmmSafeOffset, 0); // doesn't send Ymm into negative territory
        goToYmm(YmmReady);
        step++;
      } break;
    case 3: if (YinPosition) {
        goToYawCentiDeg(centiDegArmGoal);
        step++;
      } break;
    case 4: if (false && YawInPosition) {
        phase = 'E';
        step = 0;
      } break;
    default:
      // Serial.print("invalide localization phase step value; value is: " + step); // TO DO: print location step error
      // and remove gargabe below.
      byte a = 0;
  }
}

void handleEng()
{
  // set max Y speed TO DO: Determine what's best
  switch (step)
  { case 0: goToYmm(YmmArmGoal); step++; break;
    case 1: if (YinPosition || strcmp(messageFromRPi, "BlinkGreen") == 0) { // if we made it to the goal, or find out we're charging, stop driving everything
        step = 0;
        phase = 'A';
      } else {
        int YmmDistFromTarget = (YGoalPosition - YCurrPosition) / MM2TIC_SPD;
        if (YmmDistFromTarget > YmmRedzone) { // if not deep in port, misalignment is likely due to X
          int XmmSpeed = map(YawXforce, -forceMagnitudeYawX, forceMagnitudeYawX, -LAT_SPD, LAT_SPD);
          setXmmSpeed(XmmSpeed);
        } else { // deep in port, misalignment likely Yaw; centiOmega is degrees per 100 seconds
          int centiOmega = map(YawXforce, -forceMagnitudeYawX, forceMagnitudeYawX, -ANG_SPD, ANG_SPD);
          setYawWithXSpeed(centiOmega);// this should use map!
        }
        byte ZmmSpeed = map(Zforce, -forceMagnitudeZ, forceMagnitudeZ, -Z_MM_MAX_SPD, Z_MM_MAX_SPD);
        // constrain not necessary; Tic automatically does it.
        ticZ.setTargetVelocity(ZmmSpeed * MM2TIC_SPD);
      }
      break;
    default:
      byte a = 0;
  }
}

void handleAss() // that's what she said!
{ // Assess For status
  if (strcmp(messageFromRPi, "SolidAmber") == 0)
  {
    goToYmm(YCurrPosition / MM2TIC_POS + 1);
  }
  else if (strcmp(messageFromRPi, "BlinkGreen") == 0)
  {
    // TO PROGRAM: de-energize everything (set booleans to control housekeeping)
  }
  else if (strcmp(messageFromRPi, "SolidGreen") == 0)
  {
    // TO PROGRAM: Arduino tell Tesla to unlock charge port
  }
  else if (strcmp(messageFromRPi, "BlinkAmber") == 0)
  {
    // TO PROGRAM: Send error report; port now latched and we're not getting further.
  }
  else if (strcmp(messageFromRPi, "Red") == 0)
  {
    // TO PROGRAM: Send error report
  }
  else if (strcmp(messageFromRPi, "White") == 0)
  {
    phase = 'R';
  }
}

void handleRst()
{
  switch (step) {
    case 0:
      goToYmm(YmmReady); //
      step++;
      break;
    case 1:
      if (YinPosition)
      {
        goToYawCentiDeg(SAFE_CENTI_DEG);
        step++;
      }
      break;
    case 2:
      if (YawInPosition)
      {
        ticY.goHomeReverse(); //    b. Y home backwards,
        step++;
      }
      break;
    case 3:
      if (!ticY.getHomingActive())
      {
        goToYmm(stowYmm); //  then ### mm to clear Ho part of H frame
        step++;
      }
      break;
    case 4:
      if (YinPosition)
      {
        goToYawCentiDeg(stowCentiDeg); // c. YAW to stowed position
        step++;
      }
      break;
    case 5:
      if (YawInPosition)
      {
        homeX(); // X home towards motor //
        step++;
      }
      break;
    case 6:
      if (XCurrEnc <= 0)
      { // TO PROGRAM: implement status check on page 74 for home bit mask
        phase = 'I';
        step = 0;
      }
      break;
    default:
      // Serial.print("invalide localization phase step value; value is: " + step); // TO PROGRAM: print location step error
      // and remove garbage below.
      byte a = 0;
  }
}

void samples()
{
  // PLAYGROUND (NOT TO BE USED IN FINAL):
  lcdSample();

  //  ticSample();
  //  clawSample();
  //  goToXmm(20.0);
  //  goToYawCentiDeg(30.0);
  //  HX711sample();
  //  commsExLoop();
}

3nginerd
 
Posts: 19
Joined: Sat Oct 19, 2019 11:11 pm

Re: Garbage Characters on RGB_LCD Shield

by adafruit_support_bill on Wed Jun 24, 2020 8:13 pm

Looks like quite a lot going on there. If you can reduce it to the minimal subset that exhibits the problem, that would simplify the diagnosis.

adafruit_support_bill
 
Posts: 78723
Joined: Sat Feb 07, 2009 10:11 am

Re: Garbage Characters on RGB_LCD Shield

by 3nginerd on Thu Jun 25, 2020 12:02 am

There seems to be some complicated interactions between various Serial functions and I2C. At one point with the larger program, I was able to delete one line of code that seemed to fix it, but trimming down more to simplify for posting here resulted in that function only making the difference between garbage characters and other LCD problems. I was able to narrow it down to a smaller selection of functions that appeared to cause basic problems. The current error is the LCD thinks the select button is being pressed frequently, but manifests mostly on startup, and after pressing right. Pressing left or down does not result in Select flashing again, and up only sometimes has Select take over.

Adafruit.ino (mostly constants and variables introduced):
Code: Select all | TOGGLE FULL SIZE

// X/Yaw software constants:
const byte ADDRESS = 0x80;            // available addresses in Roboclaw firmware are 0x80 to 0x87 (128 to 135)

// Process variables
char phase = 'S';    // N(one) I start the program doing samples; available chars: https://www.arduino.cc/en/Reference/ASCIIchart
                     // fuller here: https://en.cppreference.com/w/cpp/language/ascii
byte step = 255;     // initialize to step which should never exist


// Encoder/Position Data passed directly from Tics & Claw:
long YCurrPosition = 0;
long ZCurrPosition = 0;
long YawCurrEnc = 0;
long XCurrEnc = 0;

unsigned long YawGoalEnc = 0;
unsigned long XGoalEnc = 0;
unsigned long YGoalPosition = 0;
unsigned long ZGoalPosition = 0;

// booleans for readability
bool XinPosition = false;
bool YinPosition = false;
bool ZinPosition = false;
bool YawInPosition = false;


Claw.ino (a device operating on Serial1, which is the uno wifi Rev 2 UART pins attached to D0/D1, separate from Serial, which is UART only going to USB port).
Explanation mostly for others' benefit, not yours Bill.

Code: Select all | TOGGLE FULL SIZE
#include "RoboClaw.h"

// Uno Wifi: Serial1 (that's a one) is pins 0 and 1; Serial is only USB; Serial2 goes to NINA
RoboClaw roboclaw(&Serial1, 10000); // 10,000 microseconds command timeout before motor shutdown


void clawSetup()
{
  // Open roboclaw serial ports, Values between 2400-460800 bps are available
  roboclaw.begin(460800); //On wifi board, increase Serial comms to 460800bps.
  //
}

byte updateEncCounts()
{
  bool validX, validYaw;
  byte statusX, statusYaw;
  XCurrEnc = roboclaw.ReadEncM1(ADDRESS, &statusX, &validX);
  YawCurrEnc = roboclaw.ReadEncM2(ADDRESS, &statusYaw, &validYaw);
  if (!(statusX && statusYaw))
    return 1;
  else
    return 0;
}


LCD.ino : mostly the Hello World example with only the germaine portions remaining

Code: Select all | TOGGLE FULL SIZE
/*
   LCD includes and examples
*/

#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <utility/Adafruit_MCP23017.h>

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield(); // for uno wifi fix class def w/ "arduino::Print"

// These #defines make it easy to set the backlight color
//    optimize by removing? (NO, define is macro done beforehand)
#define OFF 0x0
#define RED 0x1
#define GREEN 0x2
#define YELLOW 0x3
#define BLUE 0x4
#define VIOLET 0x5
#define TEAL 0x6
#define WHITE 0x7
uint8_t buttons;

void lcdSetup()
{
  pinMode(19, OUTPUT);
  for (int i = 0; i < 8; i++) {
    digitalWrite(21, HIGH);
    delayMicroseconds(3);
    digitalWrite(21, LOW);
    delayMicroseconds(3);
  }
  pinMode(19, INPUT);
  lcd.begin(16, 2); // set up the LCD's number of columns and rows:
}



void lcdSample()
{

  if (buttons) {
    lcd.clear();
    lcd.setCursor(0, 0);
    if (buttons & BUTTON_UP) {
      lcd.print("UP ");
      lcd.setBacklight(RED);
    }
    if (buttons & BUTTON_DOWN) {
      lcd.print("DOWN ");
      lcd.setBacklight(YELLOW);
    }
    if (buttons & BUTTON_LEFT) {
      lcd.print("LEFT ");
      lcd.setBacklight(GREEN);
    }
    if (buttons & BUTTON_RIGHT) {
      lcd.print("RIGHT ");
      lcd.setBacklight(TEAL);
    }
    if (buttons & BUTTON_SELECT) {
      lcd.print("SELECT ");
      lcd.setBacklight(VIOLET);
    }
  }

}


Tic.ino: the other I2C devices:

Code: Select all | TOGGLE FULL SIZE
#include <Tic.h>
TicI2C ticY;     // default I2C address is 14; don't need to specify
TicI2C ticZ(13); // must set in Tic Control Center software. Range: 0 to 16383

void updateTicPositions()
{
  YCurrPosition = ticY.getCurrentPosition();
  ZCurrPosition = ticZ.getCurrentPosition();

  YinPosition = YGoalPosition == YCurrPosition;
  ZinPosition = ZGoalPosition == ZCurrPosition;
}


Z_main_functions.ino: where all the brains of my program are to be implemented:

Code: Select all | TOGGLE FULL SIZE
// this is the last tab to ensure previous function definitions are above.

void setup()
{
  lcdSetup();
  clawSetup(); // if this is commented out, LCD stays white and doesn't responde to button presses
 

}

void loop()
{
  /****************************************CLAW KEEPING****************************************/
  updateEncCounts();
  /****************************************LCD  KEEPING****************************************/
  buttons = lcd.readButtons(); // originally declared in same scope as button actions,
  // but I moved declaration to LCD header
  lcdSample();
 
  /* When the below LCD section is uncommented, garbage characters result.
    When commented out, LCD thinks select is being repeatedly pressed.
    (but mostly on startup or after right is pressed)
  */

//    lcd.setCursor(0, 1); // set the cursor to column 0, line 1 (first line is 0)
//    // print the number of seconds since reset:
//    lcd.print("Phase: "); lcd.print(phase); lcd.print("."); lcd.print(step);

  /****************************************TIC  KEEPING****************************************/
  updateTicPositions(); // if this is commented out, normal operations occur
}



3nginerd
 
Posts: 19
Joined: Sat Oct 19, 2019 11:11 pm

Re: Garbage Characters on RGB_LCD Shield

by 3nginerd on Thu Jun 25, 2020 12:33 am

And all in one sketch if easier to read:

Code: Select all | TOGGLE FULL SIZE
#include "RoboClaw.h"

// Uno Wifi: Serial1 (that's a one) is pins 0 and 1; Serial is only USB; Serial2 goes to NINA
RoboClaw roboclaw(&Serial1, 10000); // 10,000 microseconds command timeout before motor shutdown

#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <utility/Adafruit_MCP23017.h>

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield(); // for uno wifi fix class def w/ "arduino::Print"

// These #defines make it easy to set the backlight color
//    optimize by removing? (NO, define is macro done beforehand)
#define OFF 0x0
#define RED 0x1
#define GREEN 0x2
#define YELLOW 0x3
#define BLUE 0x4
#define VIOLET 0x5
#define TEAL 0x6
#define WHITE 0x7
uint8_t buttons;


#include <Tic.h>
TicI2C ticY;     // default I2C address is 14; don't need to specify
TicI2C ticZ(13); // must set in Tic Control Center software. Range: 0 to 16383


const byte ADDRESS = 0x80;            // available addresses in Roboclaw firmware are 0x80 to 0x87 (128 to 135)
char phase = 'S';    // N(one) I start the program doing samples; available chars: https://www.arduino.cc/en/Reference/ASCIIchart
byte step = 255;     // initialize to step which should never exist

long YCurrPosition = 0;
long ZCurrPosition = 0;
long YawCurrEnc = 0;
long XCurrEnc = 0;

unsigned long YawGoalEnc = 0;
unsigned long XGoalEnc = 0;
unsigned long YGoalPosition = 0;
unsigned long ZGoalPosition = 0;

bool XinPosition = false;
bool YinPosition = false;
bool ZinPosition = false;
bool YawInPosition = false;

void setup()
{
  lcd.begin(16, 2); // set up the LCD's number of columns and rows:
 
   
   // if this is commented out, LCD stays white and doesn't responde to button presses
   // Open roboclaw serial ports, Values between 2400-460800 bps are available
  roboclaw.begin(460800); //On wifi board, increase Serial comms to 460800bps.

}

void loop()
{
  /****************************************CLAW KEEPING****************************************/
  updateEncCounts();
  /****************************************LCD  KEEPING****************************************/
  buttons = lcd.readButtons(); // originally declared in same scope as button actions,
  // but I moved declaration to LCD header
  lcdSample();
 
  /* When the below LCD section is uncommented, garbage characters result.
    When commented out, LCD thinks select is being repeatedly pressed.
    (but mostly on startup or after right is pressed)
  */

//    lcd.setCursor(0, 1); // set the cursor to column 0, line 1 (first line is 0)
//    // print the number of seconds since reset:
//    lcd.print("Phase: "); lcd.print(phase); lcd.print("."); lcd.print(step);

  /****************************************TIC  KEEPING****************************************/
  updateTicPositions(); // if this is commented out, normal operations occur
}

byte updateEncCounts()
{
  bool validX, validYaw;
  byte statusX, statusYaw;
  XCurrEnc = roboclaw.ReadEncM1(ADDRESS, &statusX, &validX);
  YawCurrEnc = roboclaw.ReadEncM2(ADDRESS, &statusYaw, &validYaw);
  if (!(statusX && statusYaw))
    return 1;
  else
    return 0;
}


void lcdSample()
{

  if (buttons) {
    lcd.clear();
    lcd.setCursor(0, 0);
    if (buttons & BUTTON_UP) {
      lcd.print("UP ");
      lcd.setBacklight(RED);
    }
    if (buttons & BUTTON_DOWN) {
      lcd.print("DOWN ");
      lcd.setBacklight(YELLOW);
    }
    if (buttons & BUTTON_LEFT) {
      lcd.print("LEFT ");
      lcd.setBacklight(GREEN);
    }
    if (buttons & BUTTON_RIGHT) {
      lcd.print("RIGHT ");
      lcd.setBacklight(TEAL);
    }
    if (buttons & BUTTON_SELECT) {
      lcd.print("SELECT ");
      lcd.setBacklight(VIOLET);
    }
  }

}

void updateTicPositions()
{
  YCurrPosition = ticY.getCurrentPosition();
  ZCurrPosition = ticZ.getCurrentPosition();

  YinPosition = YGoalPosition == YCurrPosition;
  ZinPosition = ZGoalPosition == ZCurrPosition;
}

3nginerd
 
Posts: 19
Joined: Sat Oct 19, 2019 11:11 pm

Re: Garbage Characters on RGB_LCD Shield

by adafruit_support_bill on Thu Jun 25, 2020 7:03 am

/****************************************TIC KEEPING****************************************/
updateTicPositions(); // if this is commented out, normal operations occur


Is the Tic connected when you run this or does the problem occur with only the LCD shield connected?

adafruit_support_bill
 
Posts: 78723
Joined: Sat Feb 07, 2009 10:11 am

Re: Garbage Characters on RGB_LCD Shield

by 3nginerd on Thu Jun 25, 2020 9:35 am

It was happening when I didn't have anything connected other than the LCD shield; but when I connected the TICs, it seems the problem is gone?
Is this expected behavior?

3nginerd
 
Posts: 19
Joined: Sat Oct 19, 2019 11:11 pm

Re: Garbage Characters on RGB_LCD Shield

by adafruit_support_bill on Thu Jun 25, 2020 10:21 am

Sounds like maybe some instability on the i2c bus. How long are the wires going to the Tic? When you disconnect the TIC are you disconnecting at the UNO end or at the TIC end?

adafruit_support_bill
 
Posts: 78723
Joined: Sat Feb 07, 2009 10:11 am

Re: Garbage Characters on RGB_LCD Shield

by 3nginerd on Thu Jun 25, 2020 11:45 am

Thanks for the continued help, Bill!

Wires are 3" jumpers from arduino to breadboard, then 12" long pre-made servo cables to the Tics.

Disconnecting jumpers from arduino.

Another odd occurrence: sometimes after pressing DOWN, the arduino will freeze after ~3-8 minutes. It doesn't seem to happen with other button presses, but I've only tested up to 20 minutes or so.

3nginerd
 
Posts: 19
Joined: Sat Oct 19, 2019 11:11 pm

Re: Garbage Characters on RGB_LCD Shield

by adafruit_support_bill on Thu Jun 25, 2020 11:50 am

The Arduino Wire library is known to freeze up on bus problems.

Can you post some overall photos showing the rest of your wiring and connections?

adafruit_support_bill
 
Posts: 78723
Joined: Sat Feb 07, 2009 10:11 am

Please be positive and constructive with your questions and comments.