Shifting contents of an array

Hello, Im very confused with some of the examples Im using to learn about arrays. I have an array containing custom characters. I understand how to reference the data in an array. How ever, Im not getting any happy results with what Iv read and put together.

My desired result is to print out the data from the array shifted from left to right, or right to left in an increment as dictated by the position of a potentiometer. Meaning, for ever change in the value of the ohms, the array will move in a direction depending on if the resistance rises or drops. But... as the characters are shifted off the end of the array.. they are added onto the opposite end..
Like So :

Start position : ..S....W....N....E..

Rising : S...W....N....E....

Rising: ....W....N....E....S

Rising: ..W....N....E....S..

Rising : W....N....E....S....

This is the code I have been able to put together, and pass the verification, BUT I just cannot get around the If loop to get the

  byte headN[8] = { B10001, B11001, B10101, B10011, B10001, B00100, B00100, B00100 }; // North Marker
  byte headE[8] = { B01110, B01000, B01110, B01000, B01110, B00100, B00100, B00100 }; // East Marker
  byte headS[8] = { B01110, B01000, B01110, B00010, B01110, B00100, B00100, B00100 }; // South Marker
  byte headW[8] = { B10001, B10101, B10101, B01010, B01010, B00100, B00100, B00100 }; // West Marker
  byte headA[8] = { B00000, B00000, B00000, B00100, B00100, B10101, B01110, B00100 }; // Heading Arrow

  lcd.createChar(0, headA); // Defines Heading Arrow
  lcd.createChar(1, headN); // Defines North Marker
  lcd.createChar(2, headS); // Defines South Marker
  lcd.createChar(3, headE); // Defines East Marker
  lcd.createChar(4, headW); // Defines West Marker
  
  byte *array[5] = {headA, headN, headS, headE, headW};

  int CR;           // Compass Rose
  int CH;           // Current heading from potentiometer

  CH = 270;      // Current Heading

  CR =  CH/1.3888 // This will divide the CH by 1.388 wich is the width of 1 character on the LCD.

I don't know how to shift the Array from this point.

Unless you have some ulterior motive for writing code to shift the contents of the array, for this problem I wouldn't shift the array - instead, I'd set the initial array index from the potentiometer and use the modulus operator to wrap the array index. This is very simple to do and is how circular queues are typically implemented.

Put on your programming hat, because no programmer would do it that way.

The characters should stay fixed in the array, but the update to the screen should be done with an offset.
The offset is defined by the CH variable (direction in degrees).

Most lcd displays have internal memory with an offset. So you could even keep the data in the lcd display fixed and update the internal offset of the display itself.

If you upload a photo of the display and a short description of what you want and also a full sketch, it is more likely that others can help.

While I was typing this, PeterH wrote the same.

Caltoa:
Put on your programming hat, because no programmer would do it that way.

The characters should stay fixed in the array, but the update to the screen should be done with an offset.
The offset is defined by the CH variable (direction in degrees).

Most lcd displays have internal memory with an offset. So you could even keep the data in the lcd display fixed and update the internal offset of the display itself.

If you upload a photo of the display and a short description of what you want and also a full sketch, it is more likely that others can help.

While I was typing this, PeterH wrote the same.

Iv been bashing my head off the wall with this for about a week. I'm not a programmer.. But I am trying. I'm reading all of the tutorials and trying to get this to work better then I have to this point. The link below is to the other post on the entire project.

http://forum.arduino.cc/index.php?topic=212170.0

Again. Thank you for your help!!

Here is a copy of my current code, with the array printing but NOT shifting either way. Iv commented out my old code, so that you can see where I am coming from. The array is displayed on the screen, but I just cant figure out how to move it so that it wraps on the display on the same line as if Its continuous.

/* Amateur Radio Antenna Rotator Controller
 
 This is a beta version, very very basic. Its intended to be used as a testing and learning platform for myself.
 Hopfuly it will help others with some of the problems and milestones I have been facing.
 
 Powered by Arduino Uno Rev. 3

 I2C 20 x 4 LCD Display
   * 5v  -- Vcc
   * Gnd -- Gnd
   * A4  -- SDA
   * A5  -- SCL

 Potentiometer
   * 5v  -- Pwr
   * Gnd -- GND
   * A0  -- Wiper
   
 Original code by John Brent (VA3WPN) 2014 */

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <serLCD.h>

int headPin = 0; // Pin connected to heading pot wiper
int potHead;     // Variable to read heading pot value
int prevHead;
  
LiquidCrystal_I2C lcd(0x20,20,4);  // set the LCD address to 0x20 for a 20 chars and 4 line display

byte headN[8] = { B10001, B11001, B10101, B10011, B10001, B00100, B00100, B00100 }; // North Marker
byte headE[8] = { B01110, B01000, B01110, B01000, B01110, B00100, B00100, B00100 }; // East Marker
byte headS[8] = { B01110, B01000, B01110, B00010, B01110, B00100, B00100, B00100 }; // South Marker
byte headW[8] = { B10001, B10101, B10101, B01010, B01010, B00100, B00100, B00100 }; // West Marker
byte headA[8] = { B00000, B00000, B00000, B00100, B00100, B10101, B01110, B00100 }; // Heading Arrow
byte degree[8] = { B00111, B00101, B00111, B00000, B00000, B00000, B00000, B00000 }; // Degree °

void setup()

{
  Serial.begin(9600);
  lcd.init();                      // initialize the lcd 
  lcd.backlight();                 // Turns on the back light

  lcd.setCursor(1, 1);
  lcd.print("Rotator Controller"); // Sets Callsign of Station
  lcd.setCursor(6, 2);
  lcd.print("V1.0 Beta");          // Displays Time
  delay(3000);
  lcd.clear();
  
// Station Info
  lcd.setCursor(0, 0);             // Sets cursor to callsign posision
  lcd.print("CALL");               // Sets Callsign of Station
  lcd.setCursor(15, 0);            // Sets cursor to time posision
  lcd.print("19:30");              // Displays Time
}

void loop()
{
  potHead = analogRead(headPin);              // Reads the value of the heading pot
//  delay(1);
  if (prevHead!=potHead) 
    {
      clearLine(1);
    }
  potHead = map(potHead, 0, 690, 0, 360);  // Maps the heading pot value to the heading display
  setHeading(potHead);  // Displays the heading information on the display
  prevHead = potHead;
//  delay(5);
}

void setHeading(int potHead) // Build the setHeading Function
{
  
  lcd.createChar(0, headA); // Defines Heading Arrow
  lcd.createChar(1, headN); // Defines North Marker
  lcd.createChar(2, headS); // Defines South Marker
  lcd.createChar(3, headE); // Defines East Marker
  lcd.createChar(4, headW); // Defines West Marker
  lcd.createChar(5, degree); // Defines Degree Marker

  lcd.setCursor(9, 1);
  lcd.print(potHead);  // Displays Heading in Degrese
  lcd.write(5);
  lcd.setCursor(10, 2);
  lcd.write(0);        // Display Arrow
  lcd.setCursor(0, 3);

  char headingArray[4] = {2, 4, 1, 3}; // Create Ordered Array

  int CR;           // Compass Rose
  int CH;           // Current heading from potentiometer

  CH = 270;      // Current Heading

  CR =  CH/17; // This will divide the CH by 1.388 wich is the width of 1 character on the LCD.

  int i;
  for (i = 0; i < 4; i = i + 1) {
    lcd.print(headingArray[i]);   // For loop prints Array
    lcd.print("....");            // Compass Deviders between N E S W
  }
/* Old Code

  int cR;
  cR = potHead/17;

  lcd.write(0);  // Heading Indicator

  lcd.setCursor(cR, 3);
  lcd.write(2); // South
  lcd.print("....");
  lcd.write(4); // West
  lcd.print("....");
  lcd.write(1);  // North
  lcd.print("....");
  lcd.write(3); // East
  lcd.print("....");

  Serial.println(potHead); // Used for Serial Debugging
*/
}

void clearLine(int line)
{
    lcd.setCursor(0,line);
    lcd.print("                    ");
    lcd.setCursor(0,line);
}

Here is my hardware :

arduino_rotator_controller.bmp (607 KB)

Why do you have to create custom characters, it looks like they are all standard. Or do you need pixel-level movement?

EDIT: This a character-based LCD, so there will normally be a gap between characters, so how will bit-shifting characters work (or at least look any good) at all?


Rob

Have you looked at the characters? They not standard. They are created to save from having a second line.. Basically they squish a letter (N S E W) on top of a small vertical dash. I don't need it to bit shift. I need it to shift one character at a time. I depend on the gap between the characters to give the compass rose scale.

The portion that I want to wrap the screen on one line and shift left and right represents a compass heading. Like a ship or air craft navigation navigational heading compass.

Thank you for allowing me to explain that a little better.

Your code reads the pot value into the potHead variable and after mapping it to a value between 0 and 360 it passes it to the setHeading() function then all it does with the value is to print it at a fixed position (9, 1). You have nothing in your code to use the value to influence what is displayed.

You could set up an array holding the display information, such as ..S....W....N....E.. and then use the mapped potHead value to determine the point in the array from which to start displaying in the left-hand column. Display each column and when you reach the end of the array reset the array pointer back to zero and carry on printing until you have written to all of the columns.

If you look at the code you will see :

int CR;

This CR stands for compass rose. The calculation :

CR = pothead/17;

Is how I device the potentiometers value into one of 20 segments. Each segment represents one of the 20 segments per line on my display. This is how I have been trying to move the compass rose.

And your suggestion of printing from the character within the array then continuing until you restart the array and finish on the last character before the first one you printed... Is brilliant.. And exactly what I have been trying to do. But my abilities... Are unable to fathom this. All of what I try just doesn't work. Of just seems to print the array out as usual.

Sorry, I missed the assignment to CR.

So, on entry to the function you know the current heading and you want the compass rose to show that heading in the centre of the display.

Set an array index variable to the position in the array that holds the heading to be displayed in the centre.
Set a display index to the position of the centre of the display. (Is that actually possible if the display has 20 segments ?)

Start of loop
Show the character at the array index position at the display index position
Increment the display index. If it exceeds the display width set it back to zero
Increment the array index. If it exceeds the array boundary set it back to zero
Keep looping until all characters have been printed

It would be neater to do this strictly from left to right but this way avoids and need to calculate the offset between the centre character and the far left character to be printed.

Yes I did wonder why the characters had a vertical line below them, but the pic didn't show that. I guess though that simulated LCD only allows standard chars.

Here is some test code that seems to work, it basically implements what the guys have been saying.

char Compass[] = {"N....E....S....W...."};

#define N_CHARS  ((sizeof(Compass)/sizeof(Compass[0]))-1)

void setup() {
	int potVal;
	
	Serial.begin (115200);

  	potVal = analogRead(A0); 
	potVal = 512;	// for testing because I don't have a pot connected
	
	setHeading(potVal);

}

void loop() {

}

void setHeading(int potVal) {

	int index = map(potVal,0,1023,0,N_CHARS);
	
	for (int i = 0; i < N_CHARS; i++) {
		if(index == N_CHARS) {
			index = 0;
		}
		Serial.print(Compass[index++]);	
	}
}

You add the stuff to print to the LCD etc. Also as is it only works with standard chars, you'll have to figure out how to map to the custom chars in the LCD.


Rob

Graynomad:
Yes I did wonder why the characters had a vertical line below them, but the pic didn't show that. I guess though that simulated LCD only allows standard chars.

Here is some test code that seems to work, it basically implements what the guys have been saying.

char Compass[] = {"N....E....S....W...."};

#define N_CHARS  ((sizeof(Compass)/sizeof(Compass[0]))-1)

void setup() {
int potVal;

Serial.begin (115200);

potVal = analogRead(A0);
potVal = 512; // for testing because I don't have a pot connected

setHeading(potVal);

}

void loop() {

}

void setHeading(int potVal) {

int index = map(potVal,0,1023,0,N_CHARS);

for (int i = 0; i < N_CHARS; i++) {
	if(index == N_CHARS) {
		index = 0;
	}
	Serial.print(Compass[index++]);	
}

}

Thank you, but that does not seem to be working for me, Its giving me a Christmass Tree of Errors about expected "," or ";" Im trouble shooting this as we go. If this works out for me.. Its the Byte to char that Im going to have to hit up next.

Post your whole code as it is now so that the errors can be seen in context.

Well, its taken almost all day (with a lot of breaks, visits, and my kid playing with me), but Iv got it down I think.

Here is my code :

/* Amateur Radio Antenna Rotator Controller
 
 This is a beta version, very very basic. Its intended to be used as a testing and learning platform for myself.
 Hopfuly it will help others with some of the problems and milestones I have been facing.
 
 Powered by Arduino Uno Rev. 3

 I2C 20 x 4 LCD Display
   * 5v  -- Vcc
   * Gnd -- Gnd
   * A4  -- SDA
   * A5  -- SCL

 Potentiometer
   * 5v  -- Pwr
   * Gnd -- GND
   * A0  -- Wiper
   
 Original code by John Brent (VA3WPN) 2014 */
 
 #include <Wire.h>
 #include <LiquidCrystal_I2C.h>
  
  LiquidCrystal_I2C lcd(0x20,20,4);  // set the LCD address to 0x20 for a 20 chars and 4 line display

  char Compass[] = {2,5,5,5,5,4,5,5,5,5,1,5,5,5,5,3,5,5,5,5,5}; // Define the layout of the compass rose

  #define N_CHARS ((sizeof(Compass)/sizeof(Compass[0]))-1) // Figure out what position to start with on the compass rose

 void setup()
 { 
  lcd.init(); // Initialize the LCD
  lcd.backlight(); // Turn on LCD Back Light 
  
  // Define the special characters used in the Compass Rose
  byte headN[8] = { B10001, B11001, B10101, B10011, B10001, B00100, B00100, B00100 }; // North Marker
  byte headE[8] = { B01110, B01000, B01110, B01000, B01110, B00100, B00100, B00100 }; // East Marker
  byte headS[8] = { B01110, B01000, B01110, B00010, B01110, B00100, B00100, B00100 }; // South Marker
  byte headW[8] = { B10001, B10101, B10101, B01010, B01010, B00100, B00100, B00100 }; // West Marker
  byte headA[8] = { B00000, B00000, B00000, B00100, B00100, B10101, B01110, B00100 }; // Heading Arrow
  byte headI[8] = { B00000, B00000, B00000, B00000, B00000, B00000, B00100, B01110 }; // Heading Increment
  byte degree[8] = { B00111, B00101, B00111, B00000, B00000, B00000, B00000, B00000 }; // Degree °
  
  lcd.createChar(0, headA); // Defines Heading Arrow
  lcd.createChar(1, headN); // Defines North Marker
  lcd.createChar(2, headS); // Defines South Marker
  lcd.createChar(3, headE); // Defines East Marker
  lcd.createChar(4, headW); // Defines West Marker
  lcd.createChar(5, headI); // Defines Heading Incriment 
  lcd.createChar(6, degree); // Defines Degree Marker
   
   // Flash Screen
  lcd.setCursor(1, 1);
  lcd.print("Rotator Controller"); // Sets Callsign of Station
  lcd.setCursor(6, 2);
  lcd.print("V1.0 Beta");  // Displays Time
  delay(3000);
  lcd.clear(); 
  
  // Station Info
  lcd.setCursor(0, 0);
  lcd.print("CALL"); // Sets Callsign of Station
  lcd.setCursor(15, 0);
  lcd.print("19:30");  // Displays Time 
 }

 void loop() 
 {
  int potVal; // Initalize the Potentiometer 
  potVal = analogRead(0);  // Read the Potentiometers value 
  
  int Heading;  // Initalize the heading
  Heading = potVal/1.925; // Calculate heading from potVal
  
  lcd.setCursor(9, 1); // Set Position of Heading
  lcd.print(Heading);  // Displays Heading in Degrese
  lcd.write(6);  // Displays °
  lcd.print("  "); // Clears extra "Junk"

  lcd.setCursor(10, 2);// Set Position of Indicator
  lcd.write(byte(0));  // Heading Indicator
  
  lcd.setCursor(0, 3);     // Set the Compass Rose to the last line  
  setHeading(potVal);      // Use the setHeading function
 }
 
 void setHeading(int potVal) // Function to define the compass rose, and display it according the the Potentiometer
 {     
  int index = map(potVal,0,690,0,N_CHARS); // Map the Location of the compass rose to the potentiometer reading
 
  int i;  // Initalize i
  for (i=0; i<N_CHARS; i++) // Loop to print the compass rose in the corr
      {
        if(index==N_CHARS)
            {
              index = 0;
            }
         lcd.print(Compass[index++]); // Print the compass rose to the display
      }
}

Please let me know what you think, Please remember... This is my first attempt at a real program.

Your code should look like this, simple and sweet.

Using Graynomad's code.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#define N_CHARS  ((sizeof(Compass)/sizeof(Compass[0]))-1)

LiquidCrystal_I2C lcd(0x20,20,4);  // set the LCD address to 0x20 for a 16 chars and 2 line display

char Compass[21] = {"N....E....S....W...."};
int potVal;
void setup()
{
  lcd.init();                      // initialize the lcd  
  lcd.backlight();
}

void loop() {
  potVal = analogRead(A0); 
  //potVal = 512;	// for testing because I don't have a pot connected

  setHeading(potVal);
}

void setHeading(int potVal) 
{
  int index = map(potVal,0,1023,0,N_CHARS);

  for (int i = 0; i < N_CHARS; i++) 
  {
    lcd.setCursor(i,0);
    if(index == N_CHARS) index = 0;
    lcd.print(Compass[index++]);	
  }
}

Yes that looks pretty good. And you have already implemented the next stage which was to change the Compass array to an array of indexes so well done. That was going to be my next suggestion but I thought it more important to get you working with the idea of indexing into the array and wrapping the index around.

Parts like this

 int potVal; // Initalize the Potentiometer 
  potVal = analogRead(0);  // Read the Potentiometers value

Can be done in a single line and really the comments tell us nothing the code doesn't,

 int potVal = analogRead(0);

one reason for using descriptive variable names is that it reduces the amount of commenting required. For example

int x = analogRead(0);

Would need a comment.

But that's just stylistic, and stuff you will pick up as you get more experience. Meanwhile it works and that's all that matters :slight_smile:

EDIT: Hazard, you missed that he needs the custom characters so the loop needs indexes to them, not the actual characters our examples had.


Rob

Oh I just noticed this

  #define N_CHARS ((sizeof(Compass)/sizeof(Compass[0]))-1) // Figure out what position to start with on the compass rose

Firstly the comment is wrong, this code simply calculates the number of bytes in the array, it has nothing to do with start positions etc.

But also the -1 at the end is to accommodate the extra byte (/0) placed at the end of a string, eg

char * x [] = "1234";

actually reserves 5 bytes, whereas

byte x [] = {1,2,3,4};

reserves 4.

So I had -1 to allow for that extra byte, you don't need it and I would expect to see a odd character at times although that will depend on the value in the extra byte. Try

  #define N_CHARS (sizeof(Compass)/sizeof(Compass[0]))

Rob

Graynomad:
Oh I just noticed this

  #define N_CHARS ((sizeof(Compass)/sizeof(Compass[0]))-1) // Figure out what position to start with on the compass rose

Firstly the comment is wrong, this code simply calculates the number of bytes in the array, it has nothing to do with start positions etc.

But also the -1 at the end is to accommodate the extra byte (/0) placed at the end of a string, eg

char * x [] = "1234";

actually reserves 5 bytes, whereas

byte x [] = {1,2,3,4};

reserves 4.

So I had -1 to allow for that extra byte, you don't need it and I would expect to see a odd character at times although that will depend on the value in the extra byte. Try

  #define N_CHARS (sizeof(Compass)/sizeof(Compass[0]))

Rob

also char Compass[] = {2,5,5,5,5,4,5,5,5,5,1,5,5,5,5,3,5,5,5,5}; had one extra 5 on the end.

Thank you, I have been wondering about the -1 in that statement. I had played with it. Making it a 0 a +1 and a -2. It didnt make much of a change.

My code will clean up as I get experience. As you said. All the commenting is so that I can figure out what does what as I go threw the code. And for anyone who tinkers with this to be able to do the same. My final rendition.... AFTER... Its tried and tested... And after the hardware is built.. Will have more explanation in the header and little to no commenting.