/* * Read in the HMTL config, returning the EEProm address following * what was read. */ int hmtl_read_config(config_hdr_t *hdr, config_max_t outputs[], int max_outputs) { int addr; /* Zero the outputs */ for (int i = 0; i < max_outputs; i++) { outputs[i].hdr.type = HMTL_OUTPUT_NONE; } addr = EEPROM_safe_read(HMTL_CONFIG_ADDR, (uint8_t *)hdr, sizeof (config_hdr_t)); if (addr < 0) { DEBUG_ERR("hmtl_read_config: error reading config from eeprom"); return -1; } if (hdr->magic != HMTL_CONFIG_MAGIC) { DEBUG_ERR("hmtl_read_config: read config with invalid magic"); return -2; } if (hdr->protocol_version != HMTL_CONFIG_VERSION) { DEBUG1_VALUELN("hmtl_read_config: hdr has wrong protocol version:", hdr->protocol_version); return -3; } if ((hdr->num_outputs > 0) && (max_outputs != 0)) { /* Read in the outputs if any were indicated and a buffer was provided */ if (max_outputs < hdr->num_outputs) { DEBUG_ERR("hmtl_read_config: not enough outputs"); return -4; } for (int i = 0; i < hdr->num_outputs; i++) { addr = EEPROM_safe_read(addr, (uint8_t *)&outputs[i], sizeof (config_max_t)); if (addr <= 0) { DEBUG_ERR("hmtl_read_config: error reading outputs"); return -5; } } } DEBUG2_VALUE("hmtl_read_config: size=", addr - HMTL_CONFIG_ADDR); DEBUG2_VALUE(" end=", addr); DEBUG2_VALUELN(" module address=", hdr->address); return addr; }
/* Process a message if it is for this module */ boolean MessageHandler::process_msg(msg_hdr_t *msg_hdr, Socket *src, Socket *serial_socket, config_hdr_t *config) { if (msg_hdr->version != HMTL_MSG_VERSION) { DEBUG_ERR("Invalid message version"); return false; } /* Test if the message is for this device */ if ((msg_hdr->address == address) || (msg_hdr->address == SOCKET_ADDR_ANY)) { if ((msg_hdr->flags & MSG_FLAG_ACK) && (msg_hdr->address != SOCKET_ADDR_ANY)) { /* * This is an ack message that is not for us, resend it over serial in * case that was the original source. * TODO: Maybe this should check address as well, and serial needs to be * assigned an address? */ DEBUG4_PRINTLN("Forwarding ack to serial"); Serial.write((byte *)msg_hdr, msg_hdr->length); if (msg_hdr->type != MSG_TYPE_SENSOR) { // Sensor broadcasts are for everyone return false; } } switch (msg_hdr->type) { case MSG_TYPE_OUTPUT: { output_hdr_t *out_hdr = (output_hdr_t *)(msg_hdr + 1); if (out_hdr->type == HMTL_OUTPUT_PROGRAM) { manager->handle_msg((msg_program_t *)out_hdr); } else { hmtl_handle_output_msg(msg_hdr, manager->num_outputs, manager->outputs, manager->objects); } return true; } case MSG_TYPE_POLL: { // Generate a response to a poll message uint16_t source_address = 0; Socket *sock; if (src != NULL) { // The response will be going over a socket, get the source address source_address = src->sourceFromData(msg_hdr); sock = src; } else { // The data will be sent back to the indicated Serial device. A // socket still needs to be specified in order to have a buffer to // fill. sock = serial_socket; } DEBUG3_VALUELN("Poll req src:", source_address); // Format the poll response uint16_t len = hmtl_poll_fmt(sock->send_buffer, sock->send_data_size, source_address, msg_hdr->flags, OBJECT_TYPE, config, manager->outputs, sock->recvLimit); // Respond to the appropriate source if (src != NULL) { if (msg_hdr->address == SOCKET_ADDR_ANY) { // If this was a broadcast address then do not respond immediately, // delay for time based on our address. int delayMs = address * 2; DEBUG3_VALUELN("Delay resp: ", delayMs) delay(delayMs); // TODO: This blocks any running program? Use a timer } src->sendMsgTo(source_address, sock->send_buffer, len); } else { // Send the response on the serial device Serial.write(sock->send_buffer, len); } break; } case MSG_TYPE_SET_ADDR: { /* Handle an address change message */ msg_set_addr_t *set_addr = (msg_set_addr_t *)(msg_hdr + 1); if ((set_addr->device_id == 0) || (set_addr->device_id == config->device_id)) { address = set_addr->address; src->sourceAddress = address; DEBUG2_VALUELN("Address changed to ", address); } break; } case MSG_TYPE_SENSOR: { if (msg_hdr->flags & MSG_FLAG_ACK) { /* * This is a sensor response, record relevant values for usage * elsewhere. */ msg_sensor_data_t *sensor = NULL; while ((sensor = hmtl_next_sensor(msg_hdr, sensor))) { // Call the ProgramManager's handler for the sensor function manager->run_program(PROGRAM_SENSOR_DATA, sensor); } DEBUG_PRINT_END(); } break; } case MSG_TYPE_TIMESYNC: { /* * This is a time synchronization message, send to the ProgramManager's * TimeSync object. */ timesync.synchronize(src, SOCKET_ADDR_INVALID, msg_hdr); break; } case MSG_TYPE_DUMP_CONFIG: { /* * This is a request to dump the EEPROM objects to the serial device. * * TODO: This could be made to work remotely rather than only to requests * from the serial device. */ uint16_t source_address = 0; DEBUG3_VALUELN("Dump req src:", source_address); /* * The response will be a typical message header followed by the raw data * from EEPROM. */ msg_dumpconfig_response_t *resp = (msg_dumpconfig_response_t *)(serial_socket->send_buffer + sizeof (msg_hdr_t)); int location = HMTL_CONFIG_ADDR; // Starting location in EEPROM int next_addr = 0; uint8_t flags; do { uint16_t len; flags = msg_hdr->flags; next_addr = EEPROM_safe_read(location, resp->data, serial_socket->send_data_size - HMTL_MSG_DUMPCONFIG_MIN_LEN); if (next_addr > 0) { uint16_t datalen = (uint16_t)EEPROM_DATA_SIZE(next_addr - location); DEBUG4_VALUELN("Dump config:", location); /* * Check if the next address is a valid structure and if so indicate * that there will be additional messages. */ if (EEPROM_check_address(next_addr)) { flags |= MSG_FLAG_MORE_DATA; } else { DEBUG4_PRINTLN("Dump final message") } // Now that the length of the data is known construct the message len = hmtl_dumpconfig_fmt(serial_socket->send_buffer, serial_socket->send_data_size, source_address, flags, datalen); location = next_addr; } else { /* * There was an error, respond with an error flag */ flags |= MSG_FLAG_ERROR; len = hmtl_dumpconfig_fmt(serial_socket->send_buffer, serial_socket->send_data_size, source_address, flags, 0); } Serial.write(serial_socket->send_buffer, len); } while (flags & MSG_FLAG_MORE_DATA); break; } } }