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;
}