// process message "brightness" void process_brightness(MessageTypeEnum messagetype) { // "Set" or "SetGet" -> modify dimmer state and abort any running animation if ((messagetype == MESSAGETYPE_SET) || (messagetype == MESSAGETYPE_SETGET)) { start_brightness = end_brightness = msg_dimmer_brightness_get_brightness(); UART_PUTF("Requested Brightness: %u%%;", start_brightness); animation_length = 0; animation_mode = 0; animation_position = 0; setPWMDutyCyclePercent(start_brightness); /* 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); */ } // remember some values before the packet buffer is destroyed uint32_t acksenderid = pkg_header_get_senderid(); uint32_t ackpacketcounter = pkg_header_get_packetcounter(); inc_packetcounter(); // "Set" -> send "Ack" if (messagetype == MESSAGETYPE_SET) { pkg_header_init_dimmer_brightness_ack(); UART_PUTS("Sending Ack\r\n"); } // "Get" or "SetGet" -> send "AckStatus" else { pkg_header_init_dimmer_brightness_ackstatus(); // set message data msg_dimmer_brightness_set_brightness(start_brightness); UART_PUTS("Sending AckStatus\r\n"); } // set common fields pkg_header_set_senderid(device_id); pkg_header_set_packetcounter(packetcounter); pkg_headerext_common_set_acksenderid(acksenderid); pkg_headerext_common_set_ackpacketcounter(ackpacketcounter); pkg_headerext_common_set_error(false); // FIXME: Move code for the Ack to a function and also return an Ack when errors occur before! pkg_header_calc_crc32(); rfm12_send_bufx(); }
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); } } }
// Assume a request as acknowledged and delete it from the request_buffer and request_queue. void remove_request(uint8_t sender_id, uint8_t request_sender_id, uint32_t packet_counter) { if (request_sender_id != 0) // if acknowledge is not meant for the base station (which has device id 0) { UART_PUTS("Ignoring ack (request not from this device).\r\n"); } else { uint8_t rq_slot; for (rq_slot = 0; rq_slot < REQUEST_QUEUE_RECEIVERS; rq_slot++) { if (request_queue[rq_slot][0] == sender_id) { // Because we use a fifo queue, the first buffered element has to be the one that is acknowledged. // We don't need to check the others. uint8_t rb_slot = request_queue[rq_slot][1]; { if (request_buffer[rb_slot].packet_counter == packet_counter) { uint8_t i; UART_PUTF("Removing request from request buffer slot %u.\r\n", rb_slot); // remove from request buffer request_buffer[rb_slot].command_id = RS_UNUSED; // remove from request queue for (i = 1; i < REQUEST_QUEUE_PACKETS; i++) { request_queue[rq_slot][i] = request_queue[rq_slot][i + 1]; } request_queue[rq_slot][REQUEST_QUEUE_PACKETS] = RS_UNUSED; // delete request queue entry if no more packets in this queue_request if (request_queue[rq_slot][1] == RS_UNUSED) { UART_PUTF("Request queue %u is now empty.\r\n", rq_slot); request_queue[rq_slot][0] = RS_UNUSED; } print_request_queue(); } else { UART_PUTS("Warning: Sender ID from ack found in queue, but Packet Counter does not match.\r\n"); } return; } } } // After the last retry, a packet is immediately removed from the queue, and therefore not found if it is acknowledged. UART_PUTS("Warning: Acknowledged request not found in queue (could have been the last retry).\r\n"); } }
// Only for debugging... void print_request_queue(void) { uint8_t i, j; bool empty = true; for (i = 0; i < REQUEST_BUFFER_SIZE; i++) { if (request_buffer[i].message_type != MESSAGETYPE_UNUSED) { UART_PUTF("Request Buffer %u: ", i); UART_PUTF4("MessageType %u, PacketCounter %lu, Timeout %u, Retry %u, Data", request_buffer[i].message_type, request_buffer[i].packet_counter, request_buffer[i].timeout, request_buffer[i].retry_count); // TODO: only show bytes of real data length for (j = 0; j < request_buffer[i].data_bytes; j++) { UART_PUTF(" %02x", request_buffer[i].data[j]); } UART_PUTS("\r\n"); } } for (i = 0; i < REQUEST_QUEUE_RECEIVERS; i++) { if (request_queue[i][0] != SLOT_UNUSED) { empty = false; UART_PUTF("Request Queue %u: ", i); UART_PUTF("ReceiverID %u, Buffer slots", request_queue[i][0]); for (j = 0; j < REQUEST_QUEUE_PACKETS; j++) { if (request_queue[i][j + 1] == SLOT_UNUSED) { UART_PUTS(" -"); } else { UART_PUTF(" %u", request_queue[i][j + 1]); } } UART_PUTS("\r\n"); } } if (empty) { UART_PUTS("Request Queue empty"); } UART_PUTS("\r\n"); }
// Only for debugging... void print_request_queue(void) { uint8_t i, j; bool empty = true; for (i = 0; i < REQUEST_BUFFER_SIZE; i++) { if (request_buffer[i].command_id != RS_UNUSED) { UART_PUTF("Request buffer %u: ", i); UART_PUTF4("Command ID %u, Packet Counter %lu, Timeout %u, Retry %u, Data", request_buffer[i].command_id, request_buffer[i].packet_counter, request_buffer[i].timeout, request_buffer[i].retry_count); for (j = 0; j < 5; j++) { UART_PUTF(" %02x", request_buffer[i].data[j]); } UART_PUTS("\r\n"); } } for (i = 0; i < REQUEST_QUEUE_RECEIVERS; i++) { if (request_queue[i][0] != RS_UNUSED) { empty = false; UART_PUTF("Request Queue %u: ", i); UART_PUTF("Receiver ID %u, Buffer slots", request_queue[i][0]); for (j = 0; j < REQUEST_QUEUE_PACKETS; j++) { if (request_queue[i][j + 1] == RS_UNUSED) { UART_PUTS(" -"); } else { UART_PUTF(" %u", request_queue[i][j + 1]); } } UART_PUTS("\r\n"); } } if (empty) { UART_PUTS("Request Queue empty"); } UART_PUTS("\r\n"); }
void send_dimmer_status(void) { UART_PUTS("Sending Dimmer Status:\r\n"); // set device ID 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 "Dimmer Status" bufx[5] = 30; // TODO: Move command IDs to global definition file as defines // set current brightness bufx[6] = (uint8_t)(current_brightness * 255 / 100); // set end brightness bufx[7] = end_brightness; // set total animation time setBuf16(8, (uint16_t)(animation_length * ANIMATION_CYCLE_MS / 1000)); // set time until animation finishes setBuf16(10, (uint16_t)((animation_length - animation_position) * ANIMATION_CYCLE_MS / 1000)); // 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"); }
void send_packet(uint8_t aes_key_nr, uint8_t data_len) { // set device ID (base station has ID 0 by definition) bufx[0] = 0; // update packet counter packetcounter++; if (packetcounter % PACKET_COUNTER_WRITE_CYCLE == 0) { eeprom_write_dword((uint32_t*)0, packetcounter); } setBuf32(1, packetcounter); // set CRC32 uint32_t crc = crc32(bufx, data_len + 6); setBuf32(data_len + 6, crc); // load AES key (0 is first AES key) if (aes_key_nr >= AES_KEY_EEPROM_COUNT) { aes_key_nr = AES_KEY_EEPROM_COUNT - 1; } eeprom_read_block (aes_key, (uint8_t *)(EEPROM_POS_AES_KEY + aes_key_nr * 32), 32); // show info decode_data(data_len + 6); UART_PUTF(" AES key: %u\r\n", aes_key_nr); UART_PUTF(" CRC32: %02lx\r\n", crc); UART_PUTS(" Unencrypted: "); printbytearray(bufx, data_len + 10); // encrypt and send uint8_t aes_byte_count = rfm12_sendbuf(data_len + 10); UART_PUTS("Send encrypted: "); printbytearray(bufx, aes_byte_count); UART_PUTS("\r\n"); }
// 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); }
void printbytearray(uint8_t * b, uint8_t len) { uint8_t i; for (i = 0; i < len; i++) { UART_PUTF("%02x ", b[i]); } UART_PUTS ("\r\n"); }
// 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 printbytearray(uint8_t * b, uint8_t len) { #ifdef UART_DEBUG uint8_t i; for (i = 0; i < len; i++) { UART_PUTF("%02x ", b[i]); } UART_PUTS ("\r\n"); #endif }
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"); } }
void send_dimmer_status(void) { UART_PUTS("Sending Dimmer Status:\r\n"); inc_packetcounter(); uint8_t bri = (uint8_t)current_brightness; // Set packet content pkg_header_init_dimmer_brightness_status(); pkg_header_set_senderid(device_id); pkg_header_set_packetcounter(packetcounter); msg_dimmer_brightness_set_brightness(bri); pkg_header_calc_crc32(); UART_PUTF("CRC32 is %lx (added as first 4 bytes)\r\n", getBuf32(0)); UART_PUTF("Brightness: %u%%\r\n", bri); rfm12_send_bufx(); }
// 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); } } }
int main(void) { uint8_t aes_key_nr; uint8_t loop = 0; uint8_t loop2 = 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_BASESTATION); request_queue_init(); // read packetcounter, increase by cycle and write back packetcounter = e2p_generic_get_packetcounter() + PACKET_COUNTER_WRITE_CYCLE; e2p_generic_set_packetcounter(packetcounter); // read device specific config aes_key_count = e2p_basestation_get_aeskeycount(); device_id = e2p_generic_get_deviceid(); uart_init(); UART_PUTS("\r\n"); UART_PUTF4("smarthomatic Base Station v%u.%u.%u (%08lx)\r\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_HASH); UART_PUTS("(c) 2012..2014 Uwe Freese, www.smarthomatic.org\r\n"); UART_PUTF("Device ID: %u\r\n", device_id); UART_PUTF("Packet counter: %lu\r\n", packetcounter); UART_PUTF("AES key count: %u\r\n", aes_key_count); UART_PUTS("Waiting for incoming data. Press h for help.\r\n\r\n"); led_blink(500, 500, 3); rfm12_init(); sei(); // ENCODE TEST (Move to unit test some day...) /* uint8_t testlen = 32; uint8_t aes_key_num = 0; memset(&bufx[0], 0, sizeof(bufx)); bufx[0] = 0xff; bufx[1] = 0xb0; bufx[2] = 0xa0; bufx[3] = 0x3f; bufx[4] = 0x01; bufx[5] = 0x70; bufx[6] = 0x00; bufx[7] = 0x0c; bufx[8] = 0xa8; bufx[9] = 0x00; bufx[10] = 0x20; bufx[20] = 0x20; eeprom_read_block (aes_key, (uint8_t *)(EEPROM_AESKEYS_BYTE + aes_key_num * 32), 32); UART_PUTS("Using AES key "); print_bytearray((uint8_t *)aes_key, 32); UART_PUTS("Before encryption: "); print_bytearray(bufx, testlen); uint8_t aes_byte_count = aes256_encrypt_cbc(bufx, testlen); UART_PUTF("byte count = %u\r\n", aes_byte_count); UART_PUTS("After encryption: "); print_bytearray(bufx, aes_byte_count); aes256_decrypt_cbc(bufx, aes_byte_count); UART_PUTS("After decryption: "); print_bytearray(bufx, testlen); while(1); */ 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 { bool crcok = false; for (aes_key_nr = 0; aes_key_nr < aes_key_count ; aes_key_nr++) { memcpy(bufx, rfm12_rx_buffer(), len); /*if (aes_key_nr == 0) { UART_PUTS("Before decryption: "); print_bytearray(bufx, len); }*/ e2p_basestation_get_aeskey(aes_key_nr, aes_key); //UART_PUTS("Trying AES key 2 "); //print_bytearray((uint8_t *)aes_key, 32); aes256_decrypt_cbc(bufx, len); //UART_PUTS("Decrypted bytes: "); //print_bytearray(bufx, len); crcok = pkg_header_check_crc32(len); if (crcok) { //UART_PUTS("CRC correct, AES key found!\r\n"); UART_PUTF("Received (AES key %u): ", aes_key_nr); print_bytearray(bufx, len); decode_data(len); break; } } if (!crcok) { UART_PUTS("Received garbage (CRC wrong after decryption): "); memcpy(bufx, rfm12_rx_buffer(), len); print_bytearray(bufx, len); } UART_PUTS("\r\n"); } //uart_hexdump((char *)bufcontents, rfm12_rx_len()); //UART_PUTS("\r\n"); // tell the implementation that the buffer can be reused for the next data. rfm12_rx_clear(); } // send data, if waiting in send buffer if (send_data_avail) { uint8_t i; // set AES key nr aes_key_nr = hex_to_uint8((uint8_t *)cmdbuf, 1); //UART_PUTF("AES KEY = %u\r\n", aes_key_nr); // init packet buffer memset(&bufx[0], 0, sizeof(bufx)); // set message type uint8_t message_type = hex_to_uint8((uint8_t *)cmdbuf, 3); pkg_header_set_messagetype(message_type); pkg_header_adjust_offset(); //UART_PUTF("MessageType = %u\r\n", message_type); uint8_t string_offset_data = 0; /* UART_PUTS("sKK00RRRRGGMM.............Get\r\n"); UART_PUTS("sKK01RRRRGGMMDD...........Set\r\n"); UART_PUTS("sKK02RRRRGGMMDD...........SetGet\r\n"); UART_PUTS("sKK08GGMMDD...............Status\r\n"); UART_PUTS("sKK09SSSSPPPPPPEE.........Ack\r\n"); UART_PUTS("sKK0ASSSSPPPPPPEEGGMMDD...AckStatus\r\n"); */ // set header extension fields to the values given as hex string in the user input switch (message_type) { case MESSAGETYPE_GET: case MESSAGETYPE_SET: case MESSAGETYPE_SETGET: pkg_headerext_common_set_receiverid(hex_to_uint16((uint8_t *)cmdbuf, 5)); pkg_headerext_common_set_messagegroupid(hex_to_uint8((uint8_t *)cmdbuf, 9)); pkg_headerext_common_set_messageid(hex_to_uint8((uint8_t *)cmdbuf, 11)); string_offset_data = 12; break; case MESSAGETYPE_STATUS: pkg_headerext_common_set_messagegroupid(hex_to_uint8((uint8_t *)cmdbuf, 5)); pkg_headerext_common_set_messageid(hex_to_uint8((uint8_t *)cmdbuf, 7)); string_offset_data = 8; break; case MESSAGETYPE_ACK: pkg_headerext_common_set_acksenderid(hex_to_uint16((uint8_t *)cmdbuf, 5)); pkg_headerext_common_set_ackpacketcounter(hex_to_uint24((uint8_t *)cmdbuf, 9)); pkg_headerext_common_set_error(hex_to_uint8((uint8_t *)cmdbuf, 15)); // fallthrough! case MESSAGETYPE_ACKSTATUS: pkg_headerext_common_set_messagegroupid(hex_to_uint8((uint8_t *)cmdbuf, 17)); pkg_headerext_common_set_messageid(hex_to_uint8((uint8_t *)cmdbuf, 19)); string_offset_data = 20; break; } uint8_t data_len_raw = 0; // copy message data, which exists in all packets except in Get and Ack packets if ((message_type != MESSAGETYPE_GET) && (message_type != MESSAGETYPE_ACK)) { uint8_t data_len_raw = (strlen(cmdbuf) - 1 - string_offset_data) / 2; //UART_PUTF("Data bytes = %u\r\n", data_len_raw); uint8_t start = __HEADEROFFSETBITS / 8; uint8_t shift = __HEADEROFFSETBITS % 8; // copy message data, using __HEADEROFFSETBITS value and string_offset_data for (i = 0; i < data_len_raw; i++) { uint8_t val = hex_to_uint8((uint8_t *)cmdbuf, string_offset_data + 2 * i + 1); array_write_UIntValue(start + i, shift, 8, val, bufx); } } // round packet length to x * 16 bytes uint8_t packet_len = ((uint16_t)__HEADEROFFSETBITS + (uint16_t)data_len_raw * 8) / 8; packet_len = ((packet_len - 1) / 16 + 1) * 16; // send packet which doesn't require an acknowledge immediately if ((message_type != MESSAGETYPE_GET) && (message_type != MESSAGETYPE_SET) && (message_type != MESSAGETYPE_SETGET)) { send_packet(aes_key_nr, packet_len); } else // enqueue request (don't send immediately) { // header size = 9 bytes! if (queue_request(pkg_headerext_common_get_receiverid(), message_type, aes_key_nr, bufx + 9, packet_len - 9)) { UART_PUTF("Request added to queue (%u bytes packet).\r\n", packet_len); } else { UART_PUTS("Warning! Request queue full. Packet will not be sent.\r\n"); } print_request_queue(); } // clear cmdbuf to receive more input from UART send_data_avail = false; rfm12_tick(); led_blink(200, 0, 1); } // flash LED every second to show the device is alive if (loop == 50) { led_blink(10, 10, 1); loop = 0; request_t* request = find_request_to_repeat(packetcounter + 1); if (request != 0) // if request to repeat was found in queue { UART_PUTS("Repeating request.\r\n"); send_packet((*request).aes_key, (*request).data_bytes + 9); // header size = 9 bytes! print_request_queue(); } // Auto-send something for debugging purposes... if (loop2 == 50) { //strcpy(cmdbuf, "s000102828300"); //send_data_avail = true; loop2 = 0; } else { loop2++; } } else { _delay_ms(20); } rfm12_tick(); loop++; process_rxbuf(); if (uart_timeout > 0) { uart_timeout--; if (uart_timeout == 0) { UART_PUTS("*** UART user timeout. Input was ignored. ***\r\n"); } } } // never called // aes256_done(&aes_ctx); }
// Process all bytes in the UART RX ringbuffer ("rxbuf"). This function should be called in the // main loop. It can be interrupted by a UART RX interrupt, so additional bytes can be added // into the ringbuffer while this function is running. void process_rxbuf(void) { // Only process characters if the cmdbuf is clear to be overwritten. // If not, wait for the main loop to clear it by processing the user "send" command. if (send_data_avail) { return; } while (rxbuf_count > 0) { char input; // get one char from the ringbuffer and reduce its size without interruption through the UART ISR cli(); input = rxbuf[rxbuf_startpos]; rxbuf_startpos = (rxbuf_startpos + 1) % RXBUF_LENGTH; rxbuf_count--; sei(); // process character if (uart_timeout == 0) { bytes_to_read = bytes_pos = 0; } if (bytes_to_read > bytes_pos) { if (input == 13) { bytes_to_read = bytes_pos; } else if (((input >= 48) && (input <= 57)) || ((input >= 65) && (input <= 70)) || ((input >= 97) && (input <= 102))) { cmdbuf[bytes_pos] = input; bytes_pos++; UART_PUTF("*** 0x%x\r\n", hex_to_byte(input)); } else { UART_PUTS("*** Illegal character. Use only 0..9, a..f, A..F. ***\r\n"); } if (bytes_pos == bytes_to_read) { cmdbuf[bytes_pos] = '\0'; //led_dbg(1); process_cmd(); } } else if (input == 'h') { UART_PUTS("*** Help ***\r\n"); UART_PUTS("h..............this help\r\n"); UART_PUTS("rAA............read EEPROM at hex address AA\r\n"); UART_PUTS("wAAXX..........write EEPROM at hex address AA to hex value XX\r\n"); UART_PUTS("x..............enable writing to EEPROM\r\n"); UART_PUTS("z..............disable writing to EEPROM\r\n"); UART_PUTS("sKKTT{D}.......Use AES key KK to send a packet with MessageType TT, followed\r\n"); UART_PUTS(" by all necessary extension header fields and message data D.\r\n"); UART_PUTS(" Fields are: ReceiverID (RRRR), MessageGroup (GG), MessageID (MM)\r\n"); UART_PUTS(" AckSenderID (SSSS), AckPacketCounter (PPPPPP), Error (EE).\r\n"); UART_PUTS(" MessageData (DD) can be 0..17 bytes with bits moved to the left.\r\n"); UART_PUTS(" End data with ENTER. SenderID, PacketCounter and CRC are automatically added.\r\n"); UART_PUTS("sKK00RRRRGGMMDD...........Get\r\n"); UART_PUTS("sKK01RRRRGGMMDD...........Set\r\n"); UART_PUTS("sKK02RRRRGGMMDD...........SetGet\r\n"); UART_PUTS("sKK08GGMMDD...............Status\r\n"); UART_PUTS("sKK09SSSSPPPPPPEE.........Ack\r\n"); UART_PUTS("sKK0ASSSSPPPPPPEEGGMMDD...AckStatus\r\n"); UART_PUTS("cKKTT{D}{CRC}..Same as s..., but a CRC32 checksum of the command has to be appended.\r\n"); UART_PUTS(" If it doesn't match, the command is ignored.\r\n"); } else if (input == 'x') { enable_write_eeprom = true; UART_PUTS("*** Writing to EEPROM is now ENABLED. ***\r\n"); } else if (input == 'z') { enable_write_eeprom = false; UART_PUTS("*** Writing to EEPROM is now DISABLED. ***\r\n"); } else if (input == 'r') { UART_PUTS("*** Read from EEPROM. Enter address (2 characters). ***\r\n"); cmdbuf[0] = 'r'; bytes_to_read = 3; bytes_pos = 1; } else if (input == 'w') { UART_PUTS("*** Write to EEPROM. Enter address and data (4 characters). ***\r\n"); cmdbuf[0] = 'w'; bytes_to_read = 5; bytes_pos = 1; } else if (input == 's') { UART_PUTS("*** Enter data, finish with ENTER. ***\r\n"); cmdbuf[0] = 's'; bytes_to_read = 54; // 2 characters for key nr + 2 characters for MessageType + 16 characters for hdr.ext. + 2*17 characters for data bytes_pos = 1; } else if (input == 'c') { UART_PUTS("*** Enter data, finish with ENTER. ***\r\n"); cmdbuf[0] = 'c'; bytes_to_read = 62; // 2 characters for key nr + 2 characters for MessageType + 16 characters for hdr.ext. + 2*17 characters for data + 8 characters for CRC bytes_pos = 1; } else { UART_PUTS("*** Character ignored. Press h for help. ***\r\n"); } // enable user timeout if waiting for further input uart_timeout = bytes_to_read == bytes_pos ? 0 : 255; } }
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); }
int main ( void ) { uint8_t aes_key_nr; uint8_t loop = 0; uint8_t loop2 = 0; uint8_t data[22]; sbi(LED_DDR, LED_PIN); // delay 1s to avoid further communication with uart or RFM12 when my programmer resets the MC after 500ms... _delay_ms(1000); request_queue_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*)0, packetcounter); uart_init(true); UART_PUTS ("\r\n"); UART_PUTS ("Open Home Control Base Station V1.0\r\n"); UART_PUTS ("(c) 2012 Uwe Freese, www.open-home-control.com\r\n"); UART_PUTF ("Packet counter: %lu\r\n", packetcounter); UART_PUTS ("Waiting for incoming data. Press h for help.\r\n"); rfm12_init(); sei(); // ENCODE TEST /* uint8_t testlen = 64; eeprom_read_block (aes_key, (uint8_t *)EEPROM_POS_AES_KEY, 32); UART_PUTS("Using AES key "); printbytearray((uint8_t *)aes_key, 32); UART_PUTS("Before encryption: "); printbytearray(bufx, testlen); unsigned long crc = crc32(bufx, testlen); UART_PUTF("CRC32 is %lx (added as last 4 bytes)\r\n", crc); UART_PUTS("1\r\n"); crc = crc32(bufx, testlen - 4); UART_PUTS("2\r\n"); setBuf32(testlen - 4, crc); UART_PUTS("Before encryption (CRC added): "); printbytearray(bufx, testlen); UART_PUTS("1\r\n"); uint8_t aes_byte_count = aes256_encrypt_cbc(bufx, testlen); UART_PUTS("2\r\n"); UART_PUTS("After encryption: "); printbytearray(bufx, aes_byte_count); UART_PUTF("String len = %u\r\n", aes_byte_count); UART_PUTS("1\r\n"); aes256_decrypt_cbc(bufx, aes_byte_count); UART_PUTS("2\r\n"); UART_PUTS("After decryption: "); printbytearray(bufx, testlen); crc = getBuf32(testlen - 4); UART_PUTF("CRC32 is %lx (last 4 bytes from decrypted message)\r\n", crc); printbytearray(bufx, testlen); UART_PUTS("After decryption (CRC removed): "); printbytearray(bufx, testlen); UART_PUTF("String len = %u\r\n", testlen); while(1); */ 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 { uint32_t assumed_crc; uint32_t actual_crc; for(aes_key_nr = 0; aes_key_nr < AES_KEY_EEPROM_COUNT ; aes_key_nr++) { //strncpy((char *)bufx, (char *)rfm12_rx_buffer(), len); memcpy(bufx, rfm12_rx_buffer(), len); /*if (aes_key_nr == 0) { UART_PUTS("Before decryption: "); printbytearray(bufx, len); }*/ eeprom_read_block (aes_key, (uint8_t *)(EEPROM_POS_AES_KEY + aes_key_nr * 32), 32); //UART_PUTS("Trying AES key "); //printbytearray((uint8_t *)aes_key, 32); aes256_decrypt_cbc(bufx, len); //UART_PUTS("Decrypted bytes: "); //printbytearray(bufx, len); assumed_crc = getBuf32(len - 4); 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("CRC correct, AES key found!\r\n"); UART_PUTF("Received (AES key %u): ", aes_key_nr); printbytearray(bufx, len - 4); decode_data(len - 4); break; } } if (assumed_crc != actual_crc) { UART_PUTS("Received garbage (CRC wrong after decryption).\r\n"); } UART_PUTS("\r\n"); } //uart_hexdump((char *)bufcontents, rfm12_rx_len()); //UART_PUTS("\r\n"); // tell the implementation that the buffer can be reused for the next data. rfm12_rx_clear(); } // send data, if waiting in send buffer if (send_data_avail) { uint8_t i; uint8_t data_len_raw = strlen(sendbuf) / 2 - 2; // round data length to 6 + 16 bytes (including padding bytes) uint8_t data_len = (((data_len_raw + 9) / 16) + 1) * 16 - 10; // set aes key nr aes_key_nr = hex_to_uint8((uint8_t *)sendbuf, 0); //UART_PUTF("AES KEY = %u\r\n", aes_key_nr); // set command id uint8_t command_id = hex_to_uint8((uint8_t *)sendbuf, 2); // set data for (i = 0; i < data_len_raw; i++) { data[i] = hex_to_uint8((uint8_t *)sendbuf, 4 + 2 * i); } // set padding bytes for (i = data_len_raw; i < data_len; i++) { data[i] = 0; } // send status packet immediately (command IDs are less than 128) if (command_id < 128) { // set command id bufx[5] = command_id; // set data memcpy(bufx + 6, data, data_len); send_packet(aes_key_nr, data_len); } else // enqueue request (don't send immediately) { if (queue_request(data[0], command_id, aes_key_nr, data + 1)) { UART_PUTS("Adding request to queue.\r\n"); } else { UART_PUTS("Warning! Request queue full. Packet will not be sent.\r\n"); } print_request_queue(); } // clear send text buffer send_data_avail = false; rfm12_tick(); led_blink(200, 0, 1); } // flash LED every second to show the device is alive if (loop == 50) { led_blink(10, 10, 1); loop = 0; if (set_repeat_request(packetcounter + 1)) // if request to repeat was found in queue { UART_PUTS("Repeating request.\r\n"); send_packet(0, 6); print_request_queue(); } // Auto-send something for debugging purposes... if (loop2 == 50) { //strcpy(sendbuf, "008c0001003d"); //send_data_avail = true; loop2 = 0; } else { loop2++; } } else { _delay_ms(20); } rfm12_tick(); loop++; process_rxbuf(); if (uart_timeout > 0) { uart_timeout--; if (uart_timeout == 0) { UART_PUTS("*** UART user timeout. Input was ignored. ***\r\n"); } } } // never called // aes256_done(&aes_ctx); }
// Process all bytes in the rxbuffer. This function should be called in the main loop. // It can be interrupted by a UART RX interrupt, so additional bytes can be added into the ringbuffer while this function is running. void process_rxbuf(void) { while (rxbuf_count > 0) { char input; // get one char from the ringbuffer and reduce it's size without interruption through the UART ISR cli(); input = rxbuf[rxbuf_startpos]; rxbuf_startpos = (rxbuf_startpos + 1) % RXBUF_LENGTH; rxbuf_count--; sei(); // process character if (uart_timeout == 0) { bytes_to_read = bytes_pos = 0; } if (bytes_to_read > bytes_pos) { if (input == 13) { bytes_to_read = bytes_pos; } else if (((input >= 48) && (input <= 57)) || ((input >= 65) && (input <= 70)) || ((input >= 97) && (input <= 102))) { cmdbuf[bytes_pos] = input; bytes_pos++; UART_PUTF4("*** Received character %c (ASCII %u) = value 0x%x, %u bytes to go. ***\r\n", input, input, hex_to_byte(input), bytes_to_read - bytes_pos); } else { UART_PUTS("*** Illegal character. Use only 0..9, a..f, A..F. ***\r\n"); } if (bytes_pos == bytes_to_read) { cmdbuf[bytes_pos] = '\0'; process_cmd(); } } else if (input == 'h') { UART_PUTS("*** Help ***\r\n"); UART_PUTS("h.........this help\r\n"); UART_PUTS("rAA.......read EEPROM at hex address AA\r\n"); UART_PUTS("wAAXX.....write EEPROM at hex address AA to hex value XX\r\n"); UART_PUTS("x.........enable writing to EEPROM\r\n"); UART_PUTS("z.........disable writing to EEPROM\r\n"); UART_PUTS("sKKCCXX...Use AES key KK to send a packet with command ID CC and data XX (0..22 bytes).\r\n"); UART_PUTS(" End data with ENTER. Packet number and CRC are automatically added.\r\n"); } else if (input == 'x') { enable_write_eeprom = true; UART_PUTS("*** Writing to EEPROM is now ENABLED. ***\r\n"); } else if (input == 'z') { enable_write_eeprom = false; UART_PUTS("*** Writing to EEPROM is now DISABLED. ***\r\n"); } else if (input == 'r') { UART_PUTS("*** Read from EEPROM. Enter address (2 characters). ***\r\n"); cmdbuf[0] = 'r'; bytes_to_read = 3; bytes_pos = 1; } else if (input == 'w') { UART_PUTS("*** Write to EEPROM. Enter address and data (4 characters). ***\r\n"); cmdbuf[0] = 'w'; bytes_to_read = 5; bytes_pos = 1; } else if (input == 's') { UART_PUTS("*** Enter AES key nr, command ID and data in hex format to send, finish with ENTER. ***\r\n"); cmdbuf[0] = 's'; bytes_to_read = 49; // 's' + 2 characters for key nr + 2 characters for command ID + 2*22 characters for data bytes_pos = 1; } else { UART_PUTS("*** Character ignored. Press h for help. ***\r\n"); } // enable user timeout if waiting for further input uart_timeout = bytes_to_read == bytes_pos ? 0 : 255; } }
// 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"); } }
void process_packet(uint8_t len) { pkg_header_adjust_offset(); UART_PUTS("Received: "); print_bytearray(bufx, len); // check SenderID uint32_t senderID = pkg_header_get_senderid(); UART_PUTF("SenderID:%u;", senderID); if (senderID != 0) { UART_PUTS("\r\nERR: Illegal SenderID.\r\n"); return; } // check PacketCounter // TODO: Reject if packet counter lower than remembered!! uint32_t packcnt = pkg_header_get_packetcounter(); UART_PUTF("PacketCounter:%lu;", packcnt); if (0) // packcnt <= station_packetcounter ?? { UART_PUTF("\r\nERR: Received PacketCounter < %lu.\r\n", station_packetcounter); return; } // write received counter station_packetcounter = packcnt; e2p_dimmer_set_basestationpacketcounter(station_packetcounter); // check MessageType MessageTypeEnum messagetype = pkg_header_get_messagetype(); UART_PUTF("MessageType:%u;", messagetype); if ((messagetype != MESSAGETYPE_GET) && (messagetype != MESSAGETYPE_SET) && (messagetype != MESSAGETYPE_SETGET)) { UART_PUTS("\r\nERR: Unsupported MessageType.\r\n"); return; } // check device id uint8_t rcv_id = pkg_headerext_common_get_receiverid(); UART_PUTF("ReceiverID:%u;", rcv_id); if (rcv_id != device_id) { UART_PUTS("\r\nWRN: DeviceID does not match.\r\n"); return; } // check MessageGroup + MessageID uint32_t messagegroupid = pkg_headerext_common_get_messagegroupid(); uint32_t messageid = pkg_headerext_common_get_messageid(); UART_PUTF("MessageGroupID:%u;", messagegroupid); if (messagegroupid != MESSAGEGROUP_DIMMER) { UART_PUTS("\r\nERR: Unsupported MessageGroupID.\r\n"); return; } UART_PUTF("MessageID:%u;", messageid); switch (messageid) { case MESSAGEID_DIMMER_BRIGHTNESS: process_brightness(messagetype); break; case MESSAGEID_DIMMER_ANIMATION: process_animation(messagetype); break; default: UART_PUTS("\r\nERR: Unsupported MessageID.\r\n"); break; } }
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(); }
// 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"); } }
int main(void) { uint8_t i; uint16_t wakeup_sec; bool send; // 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_SOILMOISTUREMETER); // configure power pin for 74HC14D as output sbi(TRIGGERPWR_DDR, TRIGGERPWR_PIN); // 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(); dry_thr = e2p_soilmoisturemeter_get_drythreshold(); if (dry_thr == 0) // set default value if never initialized { dry_thr = 40000; } counter_min = e2p_soilmoisturemeter_get_minval(); if (counter_min == 0) // set default value if never initialized { counter_min = 30000; } avgIntInit = e2p_soilmoisturemeter_get_averagingintervalinit(); avgInt = e2p_soilmoisturemeter_get_averaginginterval(); smoothing_percentage = e2p_soilmoisturemeter_get_smoothingpercentage(); osccal_init(); uart_init(); UART_PUTS ("\r\n"); UART_PUTF4("smarthomatic Soil Moisture Meter v%u.%u.%u (%08lx)\r\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_HASH); UART_PUTS("(c) 2014..2015 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 ("AveragingInterval for initialization: %u\r\n", avgIntInit); UART_PUTF ("AveragingInterval for normal operation: %u\r\n", avgInt); UART_PUTF ("Dry threshold: %u\r\n", dry_thr); UART_PUTF ("Min value: %u\r\n", counter_min); UART_PUTF ("Smoothing percentage: %u\r\n", smoothing_percentage); adc_init(); // init AES key e2p_generic_get_aeskey(aes_key); // set pull-up for BUTTON_DDR sbi(BUTTON_PORT, BUTTON_PIN); _delay_ms(10); // set DIDR for all ADC channels and AINs, switch off digital input buffers to reduce ADC noise and to save power DIDR0 = 63; DIDR1 = 3; // If button pressed at start up, go to sleep for idle power consumption test. // Don't communicate with RFM12, which may not have been connected yet. if (BUTTON) { led_blink(50, 50, 20); power_down(true); } led_blink(500, 500, 3); rfm12_init(); wakeup_sec = init_wakeup(); // init interrupt for button (falling edge) sbi(EICRA, ISC11); sbi(EIMSK, INT1); sei(); for (i = 0; i < SEND_STATUS_TIMES_AT_STARTUP; i++) { prepare_deviceinfo_status(); send_prepared_message(); _delay_ms(800); prepare_battery_status(); send_prepared_message(); _delay_ms(800); } while (42) { if (BUTTON) { led_blink(100, 0, 1); UART_PUTS("Button pressed!\r\n"); uint8_t cnt = 0; while (BUTTON && (cnt < 250)) { _delay_ms(10); cnt++; } if (cnt == 250) { UART_PUTS("Long press -> initiate measure mode!\r\n"); while (BUTTON) { led_blink(100, 100, 1); } init_mode = true; wupCnt = 0; counter_meas = 0; init_wakeup(); // to usually shorter value UART_PUTS("Button released!\r\n"); _delay_ms(10); } } else { send = true; //UART_PUTF("version_status_cycle = %u\r\n", version_status_cycle); if (!measure_humidity()) { if (battery_status_cycle > 0) battery_status_cycle--; if (version_status_cycle > 0) version_status_cycle--; if (version_status_cycle == 0) { version_status_cycle = SEND_VERSION_STATUS_CYCLE; prepare_deviceinfo_status(); } else if (battery_status_cycle == 0) { battery_status_cycle = SEND_BATTERY_STATUS_CYCLE; prepare_battery_status(); } else { send = false; } } if (send) { send_prepared_message(); } } power_down(true); } // never called // aes256_done(&aes_ctx); }