void setPWMDutyCyclePercent(float percent) { uint8_t index, index2; float modulo; if (percent > 100) { percent = 100; } current_brightness = percent; // used for status packet #ifdef UART_DEBUG_CALCULATIONS UART_PUTF2 ("Percent requested: %d.%02d\r\n", (uint16_t)percent, (uint16_t)(percent * 100) % 100); #endif // My OSRAM CFL lamp does not react to the 1..10V input in a linear way. // So translate promille to the translated promille value first (if enabled). if (use_pwm_translation) { index = (uint8_t)percent; index2 = index >= 100 ? 100 : index + 1; modulo = percent - index; // decimal places only, e.g. 0.12 when percent is 73.12 uint8_t t1 = eeprom_read_UIntValue8(EEPROM_BRIGHTNESSTRANSLATIONTABLE_BYTE + index, EEPROM_BRIGHTNESSTRANSLATIONTABLE_BIT, 8, 0, 0xFF); uint8_t t2 = eeprom_read_UIntValue8(EEPROM_BRIGHTNESSTRANSLATIONTABLE_BYTE + index2, EEPROM_BRIGHTNESSTRANSLATIONTABLE_BIT, 8, 0, 0xFF); percent = linear_interpolate_f(modulo, 0.0, 1.0, t1, t2) * 100 / 255; #ifdef UART_DEBUG_CALCULATIONS UART_PUTF (" Index 1: %u\r\n", index); UART_PUTF (" Index 2: %u\r\n", index2); UART_PUTF (" Translation value 1: %u\r\n", t1); UART_PUTF (" Translation value 2: %u\r\n", t2); UART_PUTF (" Modulo: 0.%003u\r\n", (uint16_t)(modulo * 1000)); UART_PUTF2 (" Percent corrected: %d.%02d\r\n", (uint16_t)percent, (uint16_t)(percent * 100) % 100); #endif } // convert percentage to corrected percentage index = (uint8_t)percent; index2 = index >= 100 ? 100 : index + 1; modulo = percent - index; // decimal places only, e.g. 0.12 when percent is 73.12 uint32_t pwm = (uint32_t)linear_interpolate_f(modulo, 0.0, 1.0, pwm_lookup[index], pwm_lookup[index2]); #ifdef UART_DEBUG_CALCULATIONS UART_PUTF (" PWM value 1: %u\r\n", pwm_lookup[index]); UART_PUTF (" PWM value 2: %u\r\n", pwm_lookup[index2]); UART_PUTF (" Modulo: 0.%003u\r\n", (uint16_t)(modulo * 1000)); UART_PUTF (" PWM value interpolated: %u\r\n", pwm); #else UART_PUTF ("PWM=%u\r\n", pwm); #endif OCR1A = pwm; }
void print_switch_state(uint8_t max) { uint8_t i; uint16_t u16; for (i = 1; i <= max; i++) { u16 = getBuf16(4 + i * 2); UART_PUTF(";Switch %u=", i); if (u16 & 0b1) { UART_PUTS("ON"); } else { UART_PUTS("OFF"); } u16 = u16 >> 1; if (u16) { UART_PUTF2(";Timeout %u=%us", i, u16); } } }
// Prepare message for humidity void prepare_humidity_status(uint16_t hum) { UART_PUTF2("Sending humidity: %u.%u%%\r\n", hum / 10, hum % 10); // Set packet content pkg_header_init_weather_humidity_status(); msg_weather_humidity_set_humidity(hum); }
// printf for floating point numbers takes ~1500 bytes program size. // Therefore, we use a smaller special function instead // as long it is used so rarely. void printSigned(int16_t i) { if (i < 0) { UART_PUTS("-"); i = -i; } UART_PUTF2("%d.%02d;", i / 100, i % 100); }
// Prepare message for humidity and raw value as temperature (only for debugging!) void prepare_humidity_status_RAW_DBG(uint16_t hum, int16_t raw) { UART_PUTF2("Sending humidity: %u.%u%%\r\n", hum / 10, hum % 10); UART_PUTF ("Sending temperature: %d\r\n", raw); // Set packet content pkg_header_init_weather_humiditytemperature_status(); msg_weather_humiditytemperature_set_humidity(hum); msg_weather_humiditytemperature_set_temperature(raw); }
// printf for floating point numbers takes ~1500 bytes program size. // Therefore, we use a smaller special function instead // as long it is used so rarely. void print_signed(int16_t i) { #ifdef UART_DEBUG if (i < 0) { UART_PUTS("-"); i = -i; } UART_PUTF2("%d.%02d", i / 100, i % 100); #endif // UART_DEBUG }
void process_cmd(void) { uart_putstr("Processing command: "); uart_putstr(cmdbuf); UART_PUTS("\r\n"); if ((cmdbuf[0] == 'w') && (strlen(cmdbuf) == 5)) { if (enable_write_eeprom) { uint16_t adr = hex_to_byte((uint8_t)cmdbuf[1]) * 16 + hex_to_byte((uint8_t)cmdbuf[2]); uint8_t val = hex_to_uint8((uint8_t *)cmdbuf, 3); UART_PUTF2("Writing data 0x%x to EEPROM pos 0x%x.\r\n", val, adr); eeprom_write_byte((uint8_t *)adr, val); } else { UART_PUTS("Ignoring EEPROM write, since write mode is DISABLED.\r\n"); } } else if ((cmdbuf[0] == 'r') && (strlen(cmdbuf) == 3)) { uint16_t adr = hex_to_byte((uint8_t)cmdbuf[1]) * 16 + hex_to_byte((uint8_t)cmdbuf[2]); uint8_t val = eeprom_read_byte((uint8_t *)adr); UART_PUTF2("EEPROM value at position 0x%x is 0x%x.\r\n", adr, val); } else if ((cmdbuf[0] == 's') && (strlen(cmdbuf) > 4)) { strcpy(sendbuf, cmdbuf + 1); send_data_avail = true; } else { UART_PUTS("Unknown command.\r\n"); } }
// Show info about the received packets. // This is only for debugging and only few messages are supported. The definition // of all packets must be known at the PC program that's processing the data. void decode_data(uint8_t len) { uint32_t u32, messagegroupid, messageid; uint16_t u16; pkg_header_adjust_offset(); uint16_t senderid = pkg_header_get_senderid(); uint32_t packetcounter = pkg_header_get_packetcounter(); MessageTypeEnum messagetype = pkg_header_get_messagetype(); UART_PUTF("Packet Data: SenderID=%u;", senderid); UART_PUTF("PacketCounter=%lu;", packetcounter); UART_PUTF("MessageType=%u;", messagetype); // show ReceiverID for all requests if ((messagetype == MESSAGETYPE_GET) || (messagetype == MESSAGETYPE_SET) || (messagetype == MESSAGETYPE_SETGET)) { uint16_t receiverid = pkg_headerext_common_get_receiverid(); UART_PUTF("ReceiverID=%u;", receiverid); } uint16_t acksenderid = 65000; uint32_t ackpacketcounter = 0; // show AckSenderID, AckPacketCounter and Error for "Ack" and "AckStatus" if ((messagetype == MESSAGETYPE_ACK) || (messagetype == MESSAGETYPE_ACKSTATUS)) { acksenderid = pkg_headerext_common_get_acksenderid(); ackpacketcounter = pkg_headerext_common_get_ackpacketcounter(); uint8_t error = pkg_headerext_common_get_error(); UART_PUTF("AckSenderID=%u;", acksenderid); UART_PUTF("AckPacketCounter=%lu;", ackpacketcounter); UART_PUTF("Error=%u;", error); } // show MessageGroupID and MessageID for all MessageTypes except "Ack" if (messagetype != MESSAGETYPE_ACK) { messagegroupid = pkg_headerext_common_get_messagegroupid(); messageid = pkg_headerext_common_get_messageid(); UART_PUTF("MessageGroupID=%u;", messagegroupid); UART_PUTF("MessageID=%u;", messageid); } // show raw message data for all MessageTypes with data (= all except "Get" and "Ack") if ((messagetype != MESSAGETYPE_GET) && (messagetype != MESSAGETYPE_ACK)) { uint8_t i; uint8_t start = __HEADEROFFSETBITS / 8; uint8_t shift = __HEADEROFFSETBITS % 8; uint16_t count = (((uint16_t)len * 8) - __HEADEROFFSETBITS + 7) / 8; //UART_PUTF4("\r\n\r\nLEN=%u, START=%u, SHIFT=%u, COUNT=%u\r\n\r\n", len, start, shift, count); UART_PUTS("MessageData="); for (i = start; i < start + count; i++) { UART_PUTF("%02x", array_read_UIntValue8(i, shift, 8, 0, 255, bufx)); } UART_PUTS(";"); // additionally decode the message data for a small number of messages switch (messagegroupid) { case MESSAGEGROUP_GENERIC: switch (messageid) { case MESSAGEID_GENERIC_VERSION: UART_PUTF("Major=%u;", msg_generic_version_get_major()); UART_PUTF("Minor=%u;", msg_generic_version_get_minor()); UART_PUTF("Patch=%u;", msg_generic_version_get_patch()); UART_PUTF("Hash=%08lx;", msg_generic_version_get_hash()); break; case MESSAGEID_GENERIC_BATTERYSTATUS: UART_PUTF("Percentage=%u;", msg_generic_batterystatus_get_percentage()); break; /*DateTime Status: UART_PUTS("Command Name=DateTime Status;"); UART_PUTF3("Date=%u-%02u-%02u;", bufx[6] + 2000, bufx[7], bufx[8]); UART_PUTF3("Time=%02u:%02u:%02u", bufx[9], bufx[10], bufx[11]);*/ default: break; } break; case MESSAGEGROUP_WEATHER: switch (messageid) { case MESSAGEID_WEATHER_TEMPERATURE: UART_PUTS("Temperature="); print_signed(msg_weather_temperature_get_temperature()); UART_PUTS(";"); break; case MESSAGEID_WEATHER_HUMIDITYTEMPERATURE: u16 = msg_weather_humiditytemperature_get_humidity(); UART_PUTF2("Humidity=%u.%u;Temperature=", u16 / 10, u16 % 10); print_signed(msg_weather_humiditytemperature_get_temperature()); UART_PUTS(";"); break; case MESSAGEID_WEATHER_BAROMETRICPRESSURETEMPERATURE: u32 = msg_weather_barometricpressuretemperature_get_barometricpressure(); UART_PUTF("Pressure=%ld;Temperature=", u32); print_signed(msg_weather_barometricpressuretemperature_get_temperature()); UART_PUTS(";"); break; default: break; } break; case MESSAGEGROUP_POWERSWITCH: switch (messageid) { case MESSAGEID_POWERSWITCH_SWITCHSTATE: UART_PUTF("On=%u;", msg_powerswitch_switchstate_get_on()); UART_PUTF("TimeoutSec=%u;", msg_powerswitch_switchstate_get_timeoutsec()); break; default: break; } break; default: break; } } UART_PUTS("\r\n"); // Detect and process Acknowledges to base station, whose requests have to be removed from the request queue if ((messagetype == MESSAGETYPE_ACK) || (messagetype == MESSAGETYPE_ACKSTATUS)) { if (acksenderid == device_id) // request sent from base station { remove_request(senderid, device_id, ackpacketcounter); } } }
// Show info about the received packets. // This is only for debugging. The definition of all packets must be known at the PC program // that's processing the data. void decode_data(uint8_t len) { uint16_t u16; int16_t s16; uint32_t u32; UART_PUTF("Sender ID=%u;", bufx[0]); UART_PUTF("Packet Counter=%lu;", getBuf32(1)); uint8_t cmd = bufx[5]; UART_PUTF("Command ID=%u;", cmd); switch (cmd) { case 1: // Generic Acknowledge u32 = getBuf32(7); UART_PUTS("Command Name=Generic Acknowledge;"); UART_PUTF("Request Sender ID=%u;", bufx[6]); UART_PUTF("Request Packet Counter=%lu\r\n", u32); remove_request(bufx[0], bufx[6], u32); break; case 10: // Temperature Sensor Status UART_PUTS("Command Name=Temperature Sensor Status;"); s16 = (int16_t)getBuf16(7); UART_PUTF("Battery=%u;", bufx[6]); UART_PUTS("Temperature="); printSigned(s16); u16 = getBuf16(9); UART_PUTF2("Humidity=%u.%02u;", u16 / 100, u16 % 100); UART_PUTF("Brightness=%u", bufx[11]); break; case 20: // Power Switch Status UART_PUTS("Command Name=Power Switch Status"); print_switch_state(3); break; case 21: // Power Switch Status Extended UART_PUTS("Command Name=Power Switch Status Extended"); print_switch_state(8); break; case 30: // DateTime Status UART_PUTS("Command Name=DateTime Status;"); UART_PUTF3("Date=%u-%02u-%02u;", bufx[6] + 2000, bufx[7], bufx[8]); UART_PUTF3("Time=%02u:%02u:%02u", bufx[9], bufx[10], bufx[11]); break; case 140: // Power Switch Request UART_PUTS("Command Name=Power Switch Request;"); uint16_t u16 = getBuf16(8); UART_PUTF("Receiver ID=%u;", bufx[6]); UART_PUTF("Switch Bitmask=%u;", bufx[7]); // TODO: Set binary mode like 00010110 UART_PUTF("Requested State=%u;", u16 & 0b1); UART_PUTF("Timeout=%u", u16 >> 1); break; case 141: // Dimmer Request UART_PUTS("Command Name=Dimmer Request;"); UART_PUTF("Receiver ID=%u;", bufx[6]); UART_PUTF("Animation Mode=%u;", bufx[7] >> 5); UART_PUTF("Dimmer Bitmask=%u;", bufx[7] & 0b111); UART_PUTF("Timeout=%u;", getBuf16(8)); UART_PUTF("Start Brightness=%u;", bufx[10]); UART_PUTF("End Brightness=%u", bufx[11]); break; default: UART_PUTS("Command Name=Unknown;"); UART_PUTS("Data="); printbytearray(bufx + 6, len - 6); } if (cmd != 1) { UART_PUTS("\r\n"); } }
int main(void) { uint16_t send_status_timeout = 25; uint32_t station_packetcounter; uint32_t pos; uint8_t button_state = 0; uint8_t manual_dim_direction = 0; // delay 1s to avoid further communication with uart or RFM12 when my programmer resets the MC after 500ms... _delay_ms(1000); util_init(); check_eeprom_compatibility(DEVICE_TYPE_DIMMER); osccal_init(); // read packetcounter, increase by cycle and write back packetcounter = eeprom_read_dword((uint32_t*)EEPROM_POS_PACKET_COUNTER) + PACKET_COUNTER_WRITE_CYCLE; eeprom_write_dword((uint32_t*)EEPROM_POS_PACKET_COUNTER, packetcounter); // read device id and write to send buffer device_id = eeprom_read_byte((uint8_t*)EEPROM_POS_DEVICE_ID); use_pwm_translation = 1; //eeprom_read_byte((uint8_t*)EEPROM_POS_USE_PWM_TRANSLATION); // TODO: read (saved) dimmer state from before the eventual powerloss /*for (i = 0; i < SWITCH_COUNT; i++) { uint16_t u16 = eeprom_read_word((uint16_t*)EEPROM_POS_SWITCH_STATE + i * 2); switch_state[i] = (uint8_t)(u16 & 0b1); switch_timeout[i] = u16 >> 1; }*/ // read last received station packetcounter station_packetcounter = eeprom_read_dword((uint32_t*)EEPROM_POS_STATION_PACKET_COUNTER); led_blink(200, 200, 5); #ifdef UART_DEBUG uart_init(false); UART_PUTS ("\r\n"); UART_PUTS ("smarthomatic Dimmer V1.0 (c) 2013 Uwe Freese, www.smarthomatic.org\r\n"); osccal_info(); UART_PUTF ("Device ID: %u\r\n", device_id); UART_PUTF ("Packet counter: %lu\r\n", packetcounter); UART_PUTF ("Use PWM translation table: %u\r\n", use_pwm_translation); UART_PUTF ("Last received station packet counter: %u\r\n\r\n", station_packetcounter); #endif // init AES key eeprom_read_block (aes_key, (uint8_t *)EEPROM_POS_AES_KEY, 32); rfm12_init(); PWM_init(); io_init(); setPWMDutyCycle(0); timer0_init(); // DEMO to measure the voltages of different PWM settings to calculate the pwm_lookup table /*while (42) { uint16_t i; for (i = 0; i <= 1024; i = i + 100) { UART_PUTF ("PWM value OCR1A: %u\r\n", i); OCR1A = i; led_blink(500, 6500, 1); } }*/ // DEMO 0..100..0%, using the pwm_lookup table and the translation table in EEPROM. /*while (42) { float i; for (i = 0; i <= 100; i = i + 0.05) { led_blink(10, 10, 1); setPWMDutyCycle(i); } for (i = 99.95; i > 0; i = i - 0.05) { led_blink(10, 10, 1); setPWMDutyCycle(i); } }*/ // set initial switch state /*for (i = 0; i < SWITCH_COUNT; i++) { switchRelais(i, switch_state[i]); }*/ sei(); // DEMO 30s /*animation_length = 30; animation_length = (uint32_t)((float)animation_length * 1000 / ANIMATION_CYCLE_MS); start_brightness = 0; end_brightness = 255; animation_position = 0;*/ while (42) { if (rfm12_rx_status() == STATUS_COMPLETE) { uint8_t len = rfm12_rx_len(); if ((len == 0) || (len % 16 != 0)) { UART_PUTF("Received garbage (%u bytes not multiple of 16): ", len); printbytearray(bufx, len); } else // try to decrypt with all keys stored in EEPROM { memcpy(bufx, rfm12_rx_buffer(), len); //UART_PUTS("Before decryption: "); //printbytearray(bufx, len); aes256_decrypt_cbc(bufx, len); //UART_PUTS("Decrypted bytes: "); //printbytearray(bufx, len); uint32_t assumed_crc = getBuf32(len - 4); uint32_t actual_crc = crc32(bufx, len - 4); //UART_PUTF("Received CRC32 would be %lx\r\n", assumed_crc); //UART_PUTF("Re-calculated CRC32 is %lx\r\n", actual_crc); if (assumed_crc != actual_crc) { UART_PUTS("Received garbage (CRC wrong after decryption).\r\n"); } else { //UART_PUTS("CRC correct, AES key found!\r\n"); UART_PUTS(" Received: "); printbytearray(bufx, len - 4); // decode command and react uint8_t sender = bufx[0]; UART_PUTF(" Sender: %u\r\n", sender); if (sender != 0) { UART_PUTF("Packet not from base station. Ignoring (Sender ID was: %u).\r\n", sender); } else { uint32_t packcnt = getBuf32(1); UART_PUTF(" Packet Counter: %lu\r\n", packcnt); // check received counter if (0) //packcnt <= station_packetcounter) { UART_PUTF2("Received packet counter %lu is lower than last received counter %lu. Ignoring packet.\r\n", packcnt, station_packetcounter); } else { // write received counter station_packetcounter = packcnt; eeprom_write_dword((uint32_t*)EEPROM_POS_STATION_PACKET_COUNTER, station_packetcounter); // check command ID uint8_t cmd = bufx[5]; UART_PUTF(" Command ID: %u\r\n", cmd); if (cmd != 141) // ID 141 == Dimmer Request { UART_PUTF("Received unknown command ID %u. Ignoring packet.\r\n", cmd); } else { // check device id uint8_t rcv_id = bufx[6]; UART_PUTF(" Receiver ID: %u\r\n", rcv_id); if (rcv_id != device_id) { UART_PUTF("Device ID %u does not match. Ignoring packet.\r\n", rcv_id); } else { // read animation mode and parameters uint8_t animation_mode = bufx[7] >> 5; // TODO: Implement support for multiple dimmers (e.g. RGB) // uint8_t dimmer_bitmask = bufx[7] & 0b111; animation_length = getBuf16(8); start_brightness = bufx[10]; end_brightness = bufx[11]; UART_PUTF(" Animation Mode: %u\r\n", animation_mode); // TODO: Set binary mode like 00010110 //UART_PUTF(" Dimmer Bitmask: %u\r\n", dimmer_bitmask); UART_PUTF(" Animation Time: %us\r\n", animation_length); UART_PUTF(" Start Brightness: %u\r\n", start_brightness); UART_PUTF(" End Brightness: %u\r\n", end_brightness); animation_length = (uint32_t)((float)animation_length * 1000 / ANIMATION_CYCLE_MS); animation_position = 0; /* TODO: Write to EEPROM (?) // write back switch state to EEPROM switch_state[i] = req_state; switch_timeout[i] = req_timeout; eeprom_write_word((uint16_t*)EEPROM_POS_SWITCH_STATE + i * 2, u16); */ // send acknowledge UART_PUTS("Sending ACK:\r\n"); // set device ID (base station has ID 0 by definition) bufx[0] = device_id; // update packet counter packetcounter++; if (packetcounter % PACKET_COUNTER_WRITE_CYCLE == 0) { eeprom_write_dword((uint32_t*)0, packetcounter); } setBuf32(1, packetcounter); // set command ID "Generic Acknowledge" bufx[5] = 1; // set sender ID of request bufx[6] = sender; // set Packet counter of request setBuf32(7, station_packetcounter); // zero unused bytes bufx[11] = 0; // set CRC32 uint32_t crc = crc32(bufx, 12); setBuf32(12, crc); // show info UART_PUTF(" CRC32: %lx\r\n", crc); uart_putstr(" Unencrypted: "); printbytearray(bufx, 16); rfm12_sendbuf(); UART_PUTS(" Send encrypted: "); printbytearray(bufx, 16); UART_PUTS("\r\n"); rfm12_tick(); led_blink(200, 0, 1); send_status_timeout = 25; } } } } } } // tell the implementation that the buffer can be reused for the next data. rfm12_rx_clear(); } _delay_ms(ANIMATION_UPDATE_MS); // React on button press. // - abort animation // - switch off, when brightness > 0 // - switch on otherwise if (!(BUTTON_PORT & (1 << BUTTON_PIN))) // button press { if (button_state == 0) { UART_PUTS("Button pressed\r\n"); animation_length = 0; animation_position = 0; } if (button_state < 5) { button_state++; } else // manual dimming { if (manual_dim_direction) // UP { if (current_brightness < 100) { current_brightness = (uint8_t)current_brightness / 2 * 2 + 2; setPWMDutyCycle(current_brightness); } else { UART_PUTS("manual dimming DOWN\r\n"); manual_dim_direction = 0; } } else // DOWN { if (current_brightness > 0) { current_brightness = (((uint8_t)current_brightness - 1) / 2) * 2; setPWMDutyCycle(current_brightness); } else { UART_PUTS("manual dimming UP\r\n"); manual_dim_direction = 1; } } } } else if (button_state && (BUTTON_PORT & (1 << BUTTON_PIN))) // button release { UART_PUTS("Button released\r\n"); if (button_state < 5) // short button press { if (current_brightness > 0) { UART_PUTS(" -> 0%\r\n"); setPWMDutyCycle(0); } else { UART_PUTS(" -> 100%\r\n"); setPWMDutyCycle(100); } } else { // reverse dim direction manual_dim_direction = !manual_dim_direction; } button_state = 0; } // update brightness according animation_position, updated by timer0 if (animation_length > 0) { pos = animation_position; // copy value to avoid that it changes in between by timer interrupt UART_PUTF2("%lu/%lu, ", pos, animation_length); if (pos == animation_length) { UART_PUTF("END Brightness %u%%, ", end_brightness * 100 / 255); setPWMDutyCycle((float)end_brightness * 100 / 255); animation_length = 0; animation_position = 0; } else { float brightness = (start_brightness + ((float)end_brightness - start_brightness) * pos / animation_length) * 100 / 255; UART_PUTF("Br.%u%%, ", (uint32_t)(brightness)); setPWMDutyCycle(brightness); } } // send status from time to time if (send_status_timeout == 0) { send_status_timeout = SEND_STATUS_EVERY_SEC * (1000 / ANIMATION_UPDATE_MS); send_dimmer_status(); led_blink(200, 0, 1); } rfm12_tick(); send_status_timeout--; checkSwitchOff(); }
int main(void) { uint16_t send_status_timeout = 25; uint32_t pos; uint8_t button_state = 0; uint8_t manual_dim_direction = 0; // delay 1s to avoid further communication with uart or RFM12 when my programmer resets the MC after 500ms... _delay_ms(1000); util_init(); check_eeprom_compatibility(DEVICETYPE_DIMMER); // read packetcounter, increase by cycle and write back packetcounter = e2p_generic_get_packetcounter() + PACKET_COUNTER_WRITE_CYCLE; e2p_generic_set_packetcounter(packetcounter); // read device id device_id = e2p_generic_get_deviceid(); // pwm translation table is not used if first byte is 0xFF use_pwm_translation = (0xFF != eeprom_read_UIntValue8(EEPROM_BRIGHTNESSTRANSLATIONTABLE_BYTE, EEPROM_BRIGHTNESSTRANSLATIONTABLE_BIT, 8, 0, 0xFF)); // TODO: read (saved) dimmer state from before the eventual powerloss /*for (i = 0; i < SWITCH_COUNT; i++) { uint16_t u16 = eeprom_read_word((uint16_t*)EEPROM_POS_SWITCH_STATE + i * 2); switch_state[i] = (uint8_t)(u16 & 0b1); switch_timeout[i] = u16 >> 1; }*/ // read last received station packetcounter station_packetcounter = e2p_dimmer_get_basestationpacketcounter(); led_blink(500, 500, 3); osccal_init(); uart_init(); UART_PUTS ("\r\n"); UART_PUTF4("smarthomatic Dimmer v%u.%u.%u (%08lx)\r\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_HASH); UART_PUTS("(c) 2013..2014 Uwe Freese, www.smarthomatic.org\r\n"); osccal_info(); UART_PUTF ("DeviceID: %u\r\n", device_id); UART_PUTF ("PacketCounter: %lu\r\n", packetcounter); UART_PUTF ("Use PWM translation table: %u\r\n", use_pwm_translation); UART_PUTF ("Last received base station PacketCounter: %u\r\n\r\n", station_packetcounter); // init AES key eeprom_read_block (aes_key, (uint8_t *)EEPROM_AESKEY_BYTE, 32); rfm12_init(); PWM_init(); io_init(); setPWMDutyCyclePercent(0); timer0_init(); // DEMO to measure the voltages of different PWM settings to calculate the pwm_lookup table /*while (42) { uint16_t i; for (i = 0; i <= 1024; i = i + 100) { UART_PUTF ("PWM value OCR1A: %u\r\n", i); OCR1A = i; led_blink(500, 6500, 1); } }*/ // DEMO 0..100..0%, using the pwm_lookup table and the translation table in EEPROM. /*while (42) { float i; for (i = 0; i <= 100; i = i + 0.05) { led_blink(10, 10, 1); setPWMDutyCycle(i); } for (i = 99.95; i > 0; i = i - 0.05) { led_blink(10, 10, 1); setPWMDutyCycle(i); } }*/ // set initial switch state /*for (i = 0; i < SWITCH_COUNT; i++) { switchRelais(i, switch_state[i]); }*/ sei(); // DEMO 30s /*animation_length = 30; animation_length = (uint32_t)((float)animation_length * 1000 / ANIMATION_CYCLE_MS); start_brightness = 0; end_brightness = 255; animation_position = 0;*/ while (42) { if (rfm12_rx_status() == STATUS_COMPLETE) { uint8_t len = rfm12_rx_len(); if ((len == 0) || (len % 16 != 0)) { UART_PUTF("Received garbage (%u bytes not multiple of 16): ", len); print_bytearray(bufx, len); } else // try to decrypt with all keys stored in EEPROM { memcpy(bufx, rfm12_rx_buffer(), len); UART_PUTS("Before decryption: "); print_bytearray(bufx, len); aes256_decrypt_cbc(bufx, len); UART_PUTS("Decrypted bytes: "); print_bytearray(bufx, len); if (!pkg_header_check_crc32(len)) { UART_PUTS("Received garbage (CRC wrong after decryption).\r\n"); } else { process_packet(len); } } // tell the implementation that the buffer can be reused for the next data. rfm12_rx_clear(); } _delay_ms(ANIMATION_UPDATE_MS); // React on button press. // - abort animation // - switch off, when brightness > 0 // - switch on otherwise if (!(BUTTON_PORT & (1 << BUTTON_PIN))) // button press { if (button_state == 0) { UART_PUTS("Button pressed\r\n"); animation_length = 0; animation_position = 0; } if (button_state < 5) { button_state++; } else // manual dimming { if (manual_dim_direction) // UP { if (current_brightness < 100) { current_brightness = (uint8_t)current_brightness / 2 * 2 + 2; setPWMDutyCyclePercent(current_brightness); } else { UART_PUTS("manual dimming DOWN\r\n"); manual_dim_direction = 0; } } else // DOWN { if (current_brightness > 0) { current_brightness = (((uint8_t)current_brightness - 1) / 2) * 2; setPWMDutyCyclePercent(current_brightness); } else { UART_PUTS("manual dimming UP\r\n"); manual_dim_direction = 1; } } } } else if (button_state && (BUTTON_PORT & (1 << BUTTON_PIN))) // button release { UART_PUTS("Button released\r\n"); if (button_state < 5) // short button press { if (current_brightness > 0) { UART_PUTS(" -> 0%\r\n"); setPWMDutyCyclePercent(0); } else { UART_PUTS(" -> 100%\r\n"); setPWMDutyCyclePercent(100); } } else { // reverse dim direction manual_dim_direction = !manual_dim_direction; } button_state = 0; } // update brightness according animation_position, updated by timer0 if (animation_length > 0) { pos = animation_position; // copy value to avoid that it changes in between by timer interrupt UART_PUTF2("%lu/%lu, ", pos, animation_length); if (pos == animation_length) { UART_PUTF("END Brightness %u%%, ", end_brightness); setPWMDutyCyclePercent((float)end_brightness); animation_length = 0; animation_position = 0; } else { float brightness = (start_brightness + ((float)end_brightness - start_brightness) * pos / animation_length); UART_PUTF("Br.%u%%, ", (uint32_t)(brightness)); setPWMDutyCyclePercent(brightness); } } // send status from time to time if (send_status_timeout == 0) { send_status_timeout = SEND_STATUS_EVERY_SEC * (1000 / ANIMATION_UPDATE_MS); send_dimmer_status(); led_blink(200, 0, 1); } else if (version_status_cycle >= SEND_VERSION_STATUS_CYCLE) { version_status_cycle = 0; send_version_status(); led_blink(200, 0, 1); } rfm12_tick(); send_status_timeout--; checkSwitchOff(); } // never called // aes256_done(&aes_ctx); }
// Process the user command now contained in the cmdbuf array. void process_cmd(void) { uart_putstr("Processing command: "); uart_putstr(cmdbuf); UART_PUTS("\r\n"); if ((cmdbuf[0] == 'w') && (strlen(cmdbuf) == 5)) // E2P write command { if (enable_write_eeprom) { uint16_t adr = hex_to_uint8((uint8_t *)cmdbuf, 1); uint8_t val = hex_to_uint8((uint8_t *)cmdbuf, 3); UART_PUTF2("Writing data 0x%x to EEPROM pos 0x%x.\r\n", val, adr); eeprom_write_byte((uint8_t *)adr, val); } else { UART_PUTS("Ignoring EEPROM write, since write mode is DISABLED.\r\n"); } } else if ((cmdbuf[0] == 'r') && (strlen(cmdbuf) == 3)) // E2P read command { uint16_t adr = hex_to_uint8((uint8_t *)cmdbuf, 1); uint8_t val = eeprom_read_byte((uint8_t *)adr); UART_PUTF2("EEPROM value at position 0x%x is 0x%x.\r\n", adr, val); } else if ((cmdbuf[0] == 's') && (strlen(cmdbuf) > 6)) // "send" command { send_data_avail = true; } else if ((cmdbuf[0] == 'c') && (strlen(cmdbuf) > 14)) // "send" command with CRC { uint8_t len = strlen(cmdbuf); // Warning! The following two lines were originally in the reverse order. // This obviously should not change anything. // But the "avr-gcc (GCC) 4.7.2" on my linux machine produced a firmware // which resulted in a wrong "calculated_crc" (whereas the // "avr-gcc (WinAVR 20100110) 4.3.3" on my Windows machine didn't). // I could recalculate the calculated_crc a 2nd time, call a UART_PUTS // or some other commands, which all produced a firmware which did not // have this issue. It's unknown why the GCC 4.7.2 produced this // wrong output. // Current workaround: I reversed the following two lines. uint32_t given_crc = hex_to_uint32((uint8_t *)cmdbuf, len - 8); uint32_t calculated_crc = crc32((uint8_t *)cmdbuf, len - 8); if (calculated_crc != given_crc) { UART_PUTF("CRC Error! %08lx does not match. Ignoring command.\r\n", calculated_crc); } else { cmdbuf[len - 8] = 0; // strip CRC from command send_data_avail = true; } } else { UART_PUTS("Unknown command.\r\n"); } }