PROGMEM char array

consider this snippet, compiled with Arduino IDE:

PROGMEM  char charSet[]  = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xA , 0x6, 0xE, 0x1, 0x9,0x5, 0xD, 0x3,0xB,0x7,0xF };
	char reversed[]  = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xA , 0x6, 0xE, 0x1, 0x9,0x5, 0xD, 0x3,0xB,0x7,0xF };
char ff;
for (int i=0; i < 16; i++) {
	Serial.print(" ");
	Serial.print(reversed[i], HEX);
	}
	Serial.println(" ");
for (int i=0; i < 16; i++) {
	Serial.print(" ");
	ff = pgm_read_byte(&charSet[i]);
	Serial.print(ff);
	}

I would expect the two for loops to produce same output. But the output is:

      0 8 4 C 2 A 6 E 1 9 5 D 3 B 7 F
      FFFFFF94 FFFFFFB0 6 FFFFFF80 FFFFFF91 FFFFFFC7 3 62 FFFFFFE3 E FFFFFF94 5E 29 FFFFFF99 23 39

What am I missing? Thank you

try a different declaration

char charSet[]  PROGMEM = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xA , 0x6, 0xE, 0x1, 0x9,0x5, 0xD, 0x3,0xB,0x7,0xF };

also try below instead of assigning to ff

Serial.print( pgm_read_byte( charSet + i ) );

The AVR microcontrollers are Harvard architecture - code and data are in separate memories (flash and SRAM respectively).

Surely you want Serial.print(ff, HEX); rather than just Serial.print(ff);
if you want the output to look the same.

Having said that, it looks like serial.print is assuming you have passed a long rather than a char, which I don't understand yet.

Paul

Surely you want Serial.print(ff, HEX); rather than just Serial.print(ff);
if you want the output to look the same.

Yes, you're right. Anyway the output is the same. Also

Serial.print( pgm_read_byte( charSet + i ), HEX );

produces same output. I really can't understand

countrypaul:
Having said that, it looks like serial.print is assuming you have passed a long rather than a char, which I don't understand yet.

All of the various print(type, base) overloads are piping through print(long, base) apparently to avoid code duplication. The unfortunate side effect is that Hex values print with too many leading Fs.

The unfortunate side effect is that Hex values print with too many leading Fs.

Only if you're using signed datatypes.

So ff should be an unsigned char as in?

unsigned char ff;

Paul

Or "byte", as in " pgm_read_byte"

I'v tried with unsigned char and byte, but no luck. The output is:

 0 8 4 C 2 A 6 E 1 9 5 D 3 B 7 F 
 91 C8 3 A8 16 B9 6 CA 6 DB 6 A1 F0 E 94 D3

The code I'm using is:

byte charSet[] PROGMEM = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xA , 0x6, 0xE, 0x1, 0x9,0x5, 0xD, 0x3,0xB,0x7,0xF };
	byte reversed[]  		 = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xA , 0x6, 0xE, 0x1, 0x9,0x5, 0xD, 0x3,0xB,0x7,0xF };
	byte ff;
	for (int i=0; i < 16; i++) {
		Serial.print(" ");
		Serial.print(reversed[i], HEX);
	}
	Serial.println(" ");
	for (int i=0; i < 16; i++) {
		Serial.print(" ");
		ff = pgm_read_byte(&charSet[i]);
		Serial.print(ff, HEX);
	}
const byte charSet[] PROGMEM = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xA , 0x6, 0xE, 0x1, 0x9,0x5, 0xD, 0x3,0xB,0x7,0xF };
	byte reversed[]  		 = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xA , 0x6, 0xE, 0x1, 0x9,0x5, 0xD, 0x3,0xB,0x7,0xF };

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

	byte ff;
	for (int i=0; i < 16; i++) {
		Serial.print(" ");
		Serial.print(reversed[i], HEX);
	}
	Serial.println(" ");
	for (int i=0; i < 16; i++) {
		Serial.print(" ");
		ff = pgm_read_byte(&charSet[i]);
		Serial.print(ff, HEX);
	}
}

void loop() {

}

returns

 0 8 4 C 2 A 6 E 1 9 5 D 3 B 7 F 
 0 8 4 C 2 A 6 E 1 9 5 D 3 B 7 F

on my computer/arduino.

$ avr-gcc -v
Using built-in specs.
COLLECT_GCC=avr-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/avr/4.7.1/lto-wrapper
Target: avr
Configured with: /build/src/gcc-4.7.1/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --enable-languages=c,c++ --disable-libssp --disable-nls --target=avr --with-as=/usr/bin/avr-as --with-ld=/usr/bin/avr-ld --with-gnu-as --with-gnu-ld
Thread model: single
gcc version 4.7.1 (GCC)

$ pacman -Qi avr-libc
Name           : avr-libc
Version        : 1.8.0-4
URL            : http://savannah.nongnu.org/projects/avr-libc/
Licenses       : BSD
Groups         : None
Provides       : None
Depends On     : avr-gcc
Optional Deps  : None
Required By    : None
Conflicts With : None
Replaces       : None
Installed Size : 23684.00 KiB
Packager       : schuay <jakob.gruber@gmail.com>
Architecture   : any
Build Date     : Sun Jun 24 01:12:18 2012
Install Date   : Thu Jun 28 08:33:48 2012
Install Reason : Explicitly installed
Install Script : No
Description    : The C runtime library for the AVR family of microcontrollers

arduino uno r2

thank you. I had to add const keyword and to move the declaration outside the function.

Now it works.

That's why we prefer it that you DON'T post code snippets

The snippet is part of a rudimental Air conditioner IR driver, I thought it was too long to be posted for a simple issue.

I'll post it, so maybe someone will find other inconsistencies in my rusty C code. It works, but I'd like to decrease memory usage furthermore.

Thanks to anyone who'll be looking at it.

#include <avr/pgmspace.h>
//These are Samsung MH026FB specific
#define tempmask 0xF//temp e` quarto byte dell'input
#define funmask 0xF0
#define fanmask 0x700
#define swirlmask 0x800

#include "IRremoteInt_light.h"
#include "IRremote_light.h"

const byte charSet[] PROGMEM = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xA , 0x6, 0xE, 0x1, 0x9,0x5, 0xD, 0x3,0xB,0x7,0xF };
	
#define DEBUG 1
/**
 Souliss Aircon MH026FB Driver
 implements IR logic. Commanded by Souliss AirCon protocol
 
 SAMSUNG MH026 series
 
*/
//IR_Remote lite library
IRsend irsend;
/**
Counts number of 1 in a long
*/
int NumberOfSetBits(long i)
{
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
/**
 Reverse the bit in a byte via a lookup table
 Bit reversal lookup table
 0100 --> 0010
 //TODO use progmem
*/
char revbits(int n)
{

	return pgm_read_byte(&charSet[n]); 
	//return reversed[n];
}
/**
	Compute Samsung crappy checksum
	Command is split between data and data2
	
	 Samsung checksum
	 1. Count the number of 1 bits in all the bytes except #2 (checksum)
	 2. Compute count mod 15. If the value is 0, use 15 instead.
	 3. Take the value from 2, flip the 4 bits, and reverse the 4 bits.
	 4. The checksum is Bn where n is the value from the previous step.

	 Note that step 2 is mod 15, not mod 16 (which you might expect). I
	 don't know why 15 is used as a special case instead of 0.

	For step 3, the results in order are F, 7, B, 3, D, 5, 9, 1, E, 6, A,
	2, C, 4, 8, 0. Note that these are exactly the same as the values you
	got for byte 5 (temperature). This suggests that the reversed /
	inverted bits are more "fundamental". 
	
	Thanks to Ken Shirriff
*/
long computeChecksum(long data, long data2){
	char temp;
	temp = NumberOfSetBits(data2);
	temp +=NumberOfSetBits(data);
	temp = temp%15;
	if (temp == 0)
		temp = 15;
	#ifdef DEBUG
	Serial.print("NUMBITS SET:");
	Serial.println(temp, HEX);
	#endif
	temp = ~temp;
	temp -=0xF0;
	#ifdef DEBUG
	Serial.print("INVERTED:");
	Serial.println(temp, HEX);
	Serial.print("REVERSED:");
	Serial.println(revbits(temp), HEX);
	#endif
	
	return 0xB0 + revbits(temp);
}

/**************************************************************************
/*!
	Remap Souliss AirCon data with device specific one (Samsung MH026FB).
	You'll have to re-implement this function to support your
	Air Conditioner. Souliss INPUT Data is:
	 
	 0xABCD
	 A = 4 bit mask (XYZT X= powerON, Y PowerOFF, ionizer (airclean) , pwrsave)
	 B = Fan/Swirl (1bit switch + 3bits for four possible fan values)
	 C = Function (auto,cool,dry,fan,heat)
	 D = Temperature (from 16 to 30, see .h)
	 
	 I comandi sono formati da due o tre burst
	 
	 
 	 ---------------------------------------------------------------
	 OUTPUT (Samsung specific)
	 Codice dati da inviare: 7F       XX       80       71       F    1    7    7    F0
							 const    check    swirl    const    ion  tem  fan  fun  const
	 DEFAULT				 00000000 00000000 00000000 01110001 0000 0000 0000 0000 00000000

*/	
/**************************************************************************/
void sendMH026FB(unsigned long data, U8 *memory_map, U8 slot){

	long part1=0;
	long part2=0;

	#ifdef DEBUG
	Serial.print("Souliss AirCon input:");
	Serial.println(data, HEX);
	#endif
	
	if (data & 0x8000){//if first bit of input=0 TURN ON
	#ifdef DEBUG
		Serial.println("POWERON!");
	#endif
		irsend.sendSamsung(0xBFB20FFF, 0xFFFFF0);
		irsend.sendSamsung(0x7FB40FFF, 0xFFFFFF);
		//*status = Souliss_T_IrCom_AirCon_Pow_On;
		//go on with command
	}
	else if (data<<1 & 0x8000) {//if second bit of input=0 SHUT OFF
	#ifdef DEBUG
		Serial.println("POWEROFF!");
	#endif
		irsend.sendSamsung(0xBFB20FFF, 0xFFFFFC);
		irsend.sendSamsung(0x7FB40FFF, 0xFFFFFF);
		irsend.sendSamsung(0x7FB08A71, 0xF84FFC);
		//*status = Souliss_T_IrCom_AirCon_Pow_Off;

		//memory_map[MaCaco_OUT_s + slot] = *status;
		//memory_map[MaCaco_OUT_s + slot + 1] = *status;			
		memory_map[MaCaco_IN_s + slot] = Souliss_T_IrCom_AirCon_Reset;
		return;
	}
	else {//Comando da due bursts, primo costante
		irsend.sendSamsung(0xBFB20FFFUL ,0xFFFFF0UL );
	}
		//starting values
		part1 =  0x7f000000;
		part2 =  0x0000F0;
		//last part1 const (andrebbe 11 -> turbo) e 01->Powersave
		unsigned int powerMode = Souliss_T_IrCom_AirCon_opt_normal;
		//FAN is bit 2,3,4 of 2nd BYTE of INPUT
		char fan = (data & fanmask)>>8;
		//FUN = 3rd byte of INPUT
		char fun = (data & funmask)>>4;

		//TEMP = 4th byte of INPUT
		long temperat = data & tempmask;
		//swirl OFF by default
		unsigned int swirl = 0x80;
		
		//swirl (if deflector has to swing)
		if (data & swirlmask){
			swirl=0x8A;
		}
		
		//Eco bit Toggle, only in cool mode
		if ((data<<3 & 0x8000) && (fun == Souliss_T_IrCom_AirCon_Fun_Cool))
			powerMode = Souliss_T_IrCom_AirCon_opt_eco;
		
		//PART2 
		
		//OPT if terzo bit = 1 IONIZER
		if (data<<2 & 0x8000)
			part2 += 0x700000;//Ion ON
		else
			part2 += 0xF00000;//F = Ion OFF
		
		
		//Fan has only 24 degrees
		if (fun == Souliss_T_IrCom_AirCon_Fun_Fan)
			temperat = Souliss_T_IrCom_AirCon_temp_24C;
			
		//Dry has only AUTO fan setting
		if (fun == Souliss_T_IrCom_AirCon_Fun_Dry)
			fan = Souliss_T_IrCom_AirCon_Fan_Auto;
			
		//Auto mode adjust: fan and options are fixed
		if (fun == Souliss_T_IrCom_AirCon_Fun_Auto){
			fan = 0x4;
			//swirl=0x8A;
			powerMode = Souliss_T_IrCom_AirCon_opt_normal;
		}
		
		part1 += swirl<<8;
		part1 += powerMode;
		//temperature on sixth byte
		part2 += temperat<<16;
		
		part2 += fan<<12;
		part2 += fun<<8;
		
		//has to be last call
		part1 += computeChecksum(part1, part2)<<16;
		
		//send command
		irsend.sendSamsung(part1, part2);
		#ifdef DEBUG
		Serial.print("Sending 0x");
		Serial.print(part1, HEX);
		Serial.print(" ");
		Serial.println(part2, HEX);
		#endif
		
		//*status = data;
}