Created
December 14, 2019 21:15
-
-
Save harelabb/c714b7bc6a8d1ae70fda10d97f7812ab to your computer and use it in GitHub Desktop.
Arduino Mega sketch to program EEPROM for RC switch code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
constexpr bool dry_run {false}; | |
constexpr bool do_program {true}; | |
// codes | |
constexpr uint32_t on_code {38909585}; | |
constexpr uint32_t off_code {39916949}; | |
// protocol | |
constexpr int pulse_length {450}; //RCswitch reports too high value | |
constexpr uint8_t on_bit[] {2, 1}; | |
constexpr uint8_t off_bit[] {1, 2}; | |
constexpr uint8_t sync_bit[] {1, 10}; | |
constexpr int code_length {28}; | |
constexpr uint8_t off_address {0x00}; | |
constexpr uint8_t on_address {0x40}; | |
constexpr int transmit_pin {5}; | |
constexpr int serial_speed {9600}; | |
constexpr int on_off_delay {1000}; | |
// ------- most configuration above here ----------- | |
constexpr uint8_t ones(int n) { | |
return n <= 1 ? 1 : (1 << (n-1)) | ones(n-1); | |
} | |
template <typename T> | |
constexpr T diff(T a, T b) { | |
return a > b ? a - b : b - a; | |
} | |
constexpr int npulse {on_bit[0] + on_bit[1]}; | |
constexpr int nsync {sync_bit[0] + sync_bit[1]}; | |
constexpr int code_size {nsync + code_length * npulse}; | |
constexpr int code_bytes {(code_size + 7) / 8}; | |
constexpr int repeat {diff(on_address, off_address) / code_bytes}; | |
constexpr uint8_t off {ones(off_bit[0])}; | |
constexpr uint8_t on {ones(on_bit[0])}; | |
constexpr uint8_t sync {ones(sync_bit[0])}; | |
// ------------------------------------------------------------------------ | |
// Arduino Mega parallel EEPROM programmer | |
// can program 256 entries (8 bit addresses) | |
// PORTA for address pins 22-29 | |
// PORTC for data pins 37-30 | |
// DRY_RUN can be used for storing data in Arduino memory | |
// instead of an actual EEPROM. | |
template <bool DRY_RUN = false> | |
class Eeprom { | |
static constexpr int CE{2}; | |
static constexpr int OE{3}; | |
static constexpr int WE{4}; | |
uint8_t eeprom[DRY_RUN ? 256 : 1]; | |
#ifndef ARDUINO_AVR_MEGA2560 | |
static_assert(DRY_RUN, "Arduino Mega needed if not dry_run"); | |
int DDRA, PORTA; | |
#endif | |
public: | |
void begin() { | |
pinMode(CE, OUTPUT); | |
pinMode(WE, OUTPUT); | |
pinMode(OE, OUTPUT); | |
digitalWrite(OE, HIGH); | |
digitalWrite(WE, HIGH); | |
digitalWrite(CE, HIGH); | |
DDRC = 0x00; | |
DDRA = 0xff; | |
PORTA = 0x00; | |
delay(1); | |
} | |
void end() { | |
DDRA = 0xff; | |
DDRC = 0xff; | |
PORTA = 0; | |
PORTC = 0; | |
} | |
void writeByte(uint8_t data, | |
uint8_t address) { | |
digitalWrite(OE, HIGH); | |
digitalWrite(WE, HIGH); | |
digitalWrite(CE, LOW); | |
DDRC = 0xff; | |
PORTA = address; | |
PORTC = data; | |
digitalWrite(WE, LOW); | |
delayMicroseconds(10); | |
digitalWrite(WE, HIGH); | |
digitalWrite(CE, HIGH); | |
} | |
uint8_t readByte(uint8_t address) { | |
digitalWrite(OE, HIGH); | |
digitalWrite(WE, HIGH); | |
digitalWrite(CE, LOW); | |
DDRC= 0x00; | |
PORTA = address; | |
digitalWrite(OE, LOW); | |
delayMicroseconds(1); | |
uint8_t data {PINC}; | |
digitalWrite(OE, HIGH); | |
digitalWrite(CE, HIGH); | |
return data; | |
} | |
}; | |
// Alternative versions for dry run | |
template<> | |
void Eeprom<true>::begin() {} | |
template<> | |
void Eeprom<true>::end() {} | |
template<> | |
void Eeprom<true>::writeByte(uint8_t data, | |
uint8_t address) { | |
eeprom[address] = data; | |
} | |
template <> | |
uint8_t Eeprom<true>::readByte(uint8_t address) { | |
return eeprom[address]; | |
} | |
Eeprom<dry_run> eeprom; | |
// ------------------------------------------------------------------------ | |
class Reader { | |
int32_t bit_count {0}; | |
uint8_t bits {0}; | |
uint8_t address; | |
public: | |
explicit Reader(uint8_t start_address) | |
: address(start_address) {} | |
uint8_t getAddress() const { | |
return address; | |
} | |
bool readBit() { | |
if (bit_count++ % 8 == 0) { | |
bits = eeprom.readByte(address++); | |
bit_count = 1; | |
delay(20); | |
} | |
bool bit = bits & 1; | |
bits >>= 1; | |
return bit; | |
} | |
void skipSync() { | |
for (int b {0}; b < nsync; ++b) { | |
(void) readBit(); | |
} | |
} | |
bool readCodeBit() { | |
uint8_t code_bit {0}; | |
for (int b {0}; b < npulse; ++b) { | |
if (readBit()) { | |
code_bit |= (1 << b); | |
} | |
} | |
if (code_bit != on && code_bit != off) { | |
Serial.print("Illegal code bit: "); | |
Serial.println(code_bit, HEX); | |
} | |
return code_bit == on; | |
} | |
uint32_t readCode() { | |
skipSync(); | |
uint32_t code {0}; | |
for (auto c {code_length}; c--;) { | |
if (readCodeBit()) { | |
code |= (1L << c); | |
} | |
} | |
return code; | |
} | |
}; | |
class Writer { | |
int32_t bit_count {0}; | |
uint8_t bits {0}; | |
uint8_t address; | |
public: | |
explicit Writer(uint8_t start_address) | |
: address(start_address) {} | |
~Writer() { | |
if (bit_count != 0) { | |
eeprom.writeByte(bits, address); | |
} | |
} | |
uint8_t getAddress() const { | |
return bit_count == 0 ? address : address + 1; | |
} | |
void writeBit(bool bit) { | |
if (bit) { | |
bits |= (1 << bit_count); | |
} | |
if (++bit_count == 8) { | |
eeprom.writeByte(bits, address++); | |
delay(20); | |
bits = bit_count = 0; | |
} | |
} | |
void writeCodeBit(bool bit) { | |
uint8_t code_bit {bit ? on : off}; | |
for (int b {0}; b < npulse; ++b) { | |
writeBit(code_bit & (1 << b)); | |
} | |
} | |
void writeSync() { | |
for (int b {0}; b < nsync; ++b) { | |
writeBit(sync & (1 << b)); | |
} | |
} | |
void writeCode(uint32_t code) { | |
writeSync(); | |
for (auto c {code_length}; c--;) { | |
writeCodeBit(code & (1L << c)); | |
} | |
} | |
}; | |
// ------------------------------------------------------------------------ | |
bool verifyEeprom(uint32_t expected, | |
uint8_t address) { | |
Reader reader(address); | |
for (int r {0}; r < repeat; ++r) { | |
uint32_t code {reader.readCode()}; | |
Serial.println(code, HEX); | |
if (code != expected) { | |
Serial.println("Code verification failed"); | |
return false; | |
} | |
} | |
Serial.println(reader.getAddress(), HEX); | |
return true; | |
} | |
void writeEeprom(uint32_t code, | |
uint8_t address) { | |
Writer writer(address); | |
Serial.println(code, HEX); | |
for (int r {0}; r < repeat; ++r) { | |
writer.writeCode(code); | |
} | |
Serial.println(writer.getAddress(), HEX); | |
} | |
void setup() { | |
Serial.begin(serial_speed); | |
pinMode(transmit_pin, OUTPUT); | |
eeprom.begin(); | |
if (!dry_run && !do_program) { | |
return; | |
} | |
// Write the codes to the EEPROM | |
Serial.println("clearing"); | |
for (int i {0}; i <= 0xff; ++i) { | |
eeprom.writeByte(0, i); | |
} | |
Serial.println("writing"); | |
writeEeprom(off_code, off_address); | |
writeEeprom(on_code, on_address); | |
Serial.println("verifying"); | |
if (!verifyEeprom(off_code, off_address) || | |
!verifyEeprom(on_code, on_address)) { | |
eeprom.end(); | |
for (;;) {} | |
} | |
} | |
void sendCode(uint8_t address, | |
uint8_t nbytes) { | |
// Sends the code stored in the EEPROM to the transmitter | |
for (uint8_t n {0}; n < nbytes; ++n) { | |
uint8_t bits {eeprom.readByte(address++)}; | |
for (uint8_t b {0}; b < 8; ++b) { | |
digitalWrite(transmit_pin, bits & 1 ? HIGH : LOW); | |
delayMicroseconds(pulse_length); | |
bits >>= 1; | |
} | |
} | |
} | |
void loop() { | |
constexpr uint8_t addresses[] { | |
off_address, on_address, off_address, on_address}; | |
for (auto adr : addresses) { | |
Serial.println(adr == on_address ? "on" : "off"); | |
sendCode(adr, diff(on_address, off_address)); | |
delay(on_off_delay); | |
} | |
eeprom.end(); | |
for(;;) {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment