diff --git a/TNC/bm78.cpp b/TNC/bm78.cpp
index 3d28159..4569a12 100644
--- a/TNC/bm78.cpp
+++ b/TNC/bm78.cpp
@@ -2,9 +2,11 @@
// All rights reserved.
#include "bm78.h"
+#include "bm78_eeprom.hpp"
#include "GPIO.hpp"
#include "Log.h"
#include "main.h"
+#include "HdlcFrame.hpp"
#include "stm32l4xx_hal.h"
@@ -15,6 +17,7 @@
extern RTC_HandleTypeDef hrtc;
extern UART_HandleTypeDef huart3;
+extern CRC_HandleTypeDef hcrc;
namespace mobilinkd { namespace tnc { namespace bm78 {
@@ -23,60 +26,47 @@ namespace mobilinkd { namespace tnc { namespace bm78 {
* transparent data transfer in one or both modes. The module documentation
* only clearly describes how to configure the modules using a (horrible)
* Windows application. Microchip publishes a library for use with their
- * PIC chips that we use as a guide for implementing our own configuration
- * code.
+ * PIC chips that we initially used as a guide for implementing our own
+ * configuration code. We now use a hybrid mechanism, writing out the
+ * entire configuration and tweaking a few settings.
*
* The module must be booted into EEPROM programming mode in order to make
- * changes. This mode uses 115200 baud with no hardware flow control
+ * changes. This mode uses 115200 baud with no hardware flow control.
*
* We program the module for the following features:
*
* - The module is set for 115200 baud with hardware flow control.
* - The name is changed to TNC3.
* - The BT3.0 pairing PIN is set to 1234
- * - The BLE5.0 pairing PIN is set to "625653" (MBLNKD on a phone pad).
+ * - The BLE5.0 pairing PIN is set to "123456".
* - The module power setting is set as low as possible for BLE.
*/
-const uint32_t BT_INIT_MAGIC = 0xc0a2;
-
void bm78_reset()
{
// Must use HAL_Delay() here as osDelay() may not be available.
mobilinkd::tnc::gpio::BT_RESET::off();
- HAL_Delay(1200);
+ HAL_Delay(1);
mobilinkd::tnc::gpio::BT_RESET::on();
- HAL_Delay(800);
-
}
-void exit_command_mode()
-{
- gpio::BT_CMD::on();
- bm78_reset();
- INFO("BM78 in PASSTHROUGH mode");
-}
-
-HAL_StatusTypeDef read_response(uint8_t* buffer, uint16_t size, uint32_t timeout)
-{
- memset(buffer, 0, size);
-
- auto result = HAL_UART_Receive(&huart3, buffer, size - 1, timeout);
-
- if (result == HAL_TIMEOUT) result = HAL_OK;
-
- return result;
-}
-
-bool enter_program_mode()
+/**
+ * Enter BM78 EEPROM programming mode.
+ *
+ * This pulls EAN low on the BM78 and resets the module to enter EEPROM
+ * programming mode and disable HW flow control on the UART.
+ *
+ * @note The timing of this process is not well documented.
+ */
+void enter_program_mode()
{
// Ensure we start out disconnected.
gpio::BT_CMD::off();
HAL_Delay(10);
gpio::BT_RESET::off();
- HAL_Delay(10); // Spec says minimum 63ns
+ HAL_Delay(1); // Spec says minimum 63ns.
gpio::BT_RESET::on();
- HAL_Delay(200); // I could not find timing specifications for this.
+ HAL_Delay(200); // Timing for EEPROM programming is not specified.
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.BaudRate = 115200;
@@ -84,95 +74,22 @@ bool enter_program_mode()
{
CxxErrorHandler();
}
-
- // Read (cmd = 0x29) all 1K bytes, starting at address 0, 128 bytes at a time.
- uint8_t cmd[] = {0x01, 0x29, 0xfc, 0x03, 0x00, 0x00, 0x80};
-
- constexpr const uint16_t BLOCK_SIZE = 128;
-
- for (uint16_t addr = 0; addr != 0x500; addr += BLOCK_SIZE)
- {
- cmd[5] = addr & 0xFF;
- cmd[4] = (addr >> 8) & 0xFF;
- if (HAL_UART_Transmit(&huart3, cmd, sizeof(cmd), 10) != HAL_OK)
- {
- ERROR("Read EEPROM transmit failed");
- return false;
- }
-
- uint8_t buffer[BLOCK_SIZE + 10];
-
- if (HAL_UART_Receive(&huart3, buffer, BLOCK_SIZE + 10, 1000) != HAL_OK)
- {
- ERROR("Read EEPROM receive failed");
- return false;
- }
-
- for (size_t i = 0; i != BLOCK_SIZE; i += 16) {
- printf("%04X: ", addr + i);
- for (size_t j = 0; j != 16; j++) {
- printf("%02X ", buffer[i + j + 10]);
- }
- printf("\r\n");
- HAL_Delay(10);
- }
- }
- return true;
}
-bool exit_program_mode()
+/**
+ * Exit BM78 EEPROM programming mode and return to pass-through mode.
+ *
+ * This asserts EAN on the BM78 and resets the module to exit EEPROM
+ * programming mode, reconfigures the UART for HW flow control, then
+ * waits for the module to become ready.
+ */
+void exit_program_mode()
{
- auto result = true;
-
- // Read (cmd = 0x29) all 1K bytes, starting at address 0, 128 bytes at a time.
- uint8_t cmd[] = {0x01, 0x29, 0xfc, 0x03, 0x00, 0x00, 0x80};
-
- constexpr const uint16_t BLOCK_SIZE = 128;
-
- for (uint16_t addr = 0; addr != 0x500; addr += BLOCK_SIZE)
- {
- cmd[5] = addr & 0xFF;
- cmd[4] = (addr >> 8) & 0xFF;
- if (HAL_UART_Transmit(&huart3, cmd, sizeof(cmd), 10) != HAL_OK)
- {
- ERROR("Read EEPROM transmit failed");
- result = false;
- break;
- }
-
- uint8_t buffer[BLOCK_SIZE + 10];
-
- if (HAL_UART_Receive(&huart3, buffer, BLOCK_SIZE + 10, 1000) != HAL_OK)
- {
- ERROR("Read EEPROM receive failed");
- result = false;
- break;
- }
-
- for (size_t i = 0; i != BLOCK_SIZE; i += 16) {
- printf("%04X: ", addr + i);
- for (size_t j = 0; j != 16; j++) {
- int c = buffer[i + j + 10];
- printf(" %c ", isprint(c) ? (char) c : '.');
- }
- printf("\r\n");
- printf("%04X: ", addr + i);
- for (size_t j = 0; j != 16; j++) {
- printf("%02X ", buffer[i + j + 10]);
- }
- printf("\r\n");
- HAL_Delay(10);
- }
- }
-
-
- // Ensure we start out disconnected.
gpio::BT_CMD::on();
HAL_Delay(10);
gpio::BT_RESET::off();
HAL_Delay(1); // Spec says minimum 63ns.
gpio::BT_RESET::on();
- HAL_Delay(400); // Spec says 354ms.
huart3.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
huart3.Init.BaudRate = 115200;
@@ -181,21 +98,32 @@ bool exit_program_mode()
CxxErrorHandler();
}
- return result;
-}
-
-bool set_reset()
-{
- return true;
+ bm78_wait_until_ready();
}
+/**
+ * Parse the result of the previous write. Return true if the write was
+ * successful and false if either the response could not be read or the
+ * write failed.
+ *
+ * @pre A block of EEPROM data was written to the BM78.
+ * @post The write response has been written.
+ *
+ * The result of the write is 7 bytes in length. The write was successful
+ * if the last byte returned is a 0.
+ *
+ * @param function is the name of the calling function and is used when
+ * logging to indicate which write operation failed.
+ * @return true when the write was successful, otherwise false.
+ */
bool parse_write_result(const char* function)
{
uint8_t result[7];
- if (HAL_UART_Receive(&huart3, result, sizeof(result), 1000) != HAL_OK)
+ HAL_StatusTypeDef status;
+ if ((status = HAL_UART_Receive(&huart3, result, sizeof(result), 1000)) != HAL_OK)
{
- ERROR("%s receive failed", function);
+ ERROR("%s receive failed (%d)", function, status);
return false;
}
@@ -207,7 +135,79 @@ bool parse_write_result(const char* function)
}
return result[6] == 0;
+}
+/**
+ * Parse the EEPROM data and write the segments to the BM78 EEPROM.
+ *
+ * @pre The BM78 is in programming mode.
+ * @post The BM78 is programmed.
+ *
+ * The data from the EEPROM programming UI is a sparsely populated memory
+ * map. That data has been converted into
format
+ * in the eeprom_data block. The last block has a zero length, indicating
+ * EOF. The address is two bytes, big endian, followed by a one byte
+ * length and *length* bytes of data.
+ *
+ * It is important that memory areas that are not populated by the UI are
+ * not overwritten as they may contain device-specific data elements. For
+ * example, the first 6 bytes of data are the Bluetooth device MAC address.
+ *
+ * The write command is 7 bytes long. The first three bytes are static:
+ * {0x01, 0x27, 0xfc}. The 4th byte is *length* + 3. The 5th and 6th
+ * are the address, and the 7th is *length*. The data to be written
+ * follows.
+ *
+ * Once the data has been written, the BM78 module sends a response. This
+ * must be read and parse before the next block is written.
+ *
+ * @return true if the BM78 is programmed, otherwise false.
+ */
+bool write_eeprom()
+{
+ const uint8_t* data = eeprom_data;
+
+ uint8_t cmd[] = {0x01, 0x27, 0xfc, 0x00, 0x00, 0x00, 0x00};
+
+ bool result = true;
+ while (result)
+ {
+ auto len = data[2];
+
+ cmd[0] = 0x01;
+ cmd[1] = 0x27;
+ cmd[2] = 0xfc;
+ cmd[3] = len + 3;
+ cmd[4] = data[0];
+ cmd[5] = data[1];
+ cmd[6] = len;
+
+ if (len == 0)
+ break;
+
+ data += 3;
+
+ if (HAL_UART_Transmit(&huart3, cmd, sizeof(cmd), 1000) != HAL_OK)
+ {
+ ERROR("%s transmit header failed", __PRETTY_FUNCTION__);
+ return false;
+ }
+
+ if (HAL_UART_Transmit(&huart3, const_cast(data), len, 1000) != HAL_OK)
+ {
+ ERROR("%s transmit data failed", __PRETTY_FUNCTION__);
+ return false;
+ } else {
+ DEBUG("%s transmit succeeded %d bytes at 0x%02X%02X",
+ __FUNCTION__, cmd[6], cmd[4], cmd[5]);
+ }
+
+ result = parse_write_result(__PRETTY_FUNCTION__);
+
+ data += len;
+ }
+
+ return result;
}
bool set_name()
@@ -331,7 +331,7 @@ bool set_le_attribute_properties()
if (HAL_UART_Transmit(&huart3, cmd, sizeof(cmd), 10) != HAL_OK)
{
- ERROR("set_le_attribute_properties transmit failed");
+ ERROR("%s transmit failed", __PRETTY_FUNCTION__);
return false;
}
@@ -429,12 +429,47 @@ bool set_reliable()
}}} // mobilinkd::tnc::bm78
+void bm78_wait_until_ready()
+{
+ auto start = HAL_GetTick();
+ // Must wait until P1_5 (BT_STATE2) is high and P0_4 (BT_STATE1) is low.
+ GPIO_PinState bt_state1, bt_state2;
+ do {
+ if (HAL_GetTick() > start + 2000) CxxErrorHandler(); // Timed out.
+
+ HAL_Delay(100);
+ bt_state2 = HAL_GPIO_ReadPin(BT_STATE2_GPIO_Port, BT_STATE2_Pin);
+ bt_state1 = HAL_GPIO_ReadPin(BT_STATE1_GPIO_Port, BT_STATE1_Pin);
+ DEBUG("bt_state2=%d, bt_state1=%d", bt_state2, bt_state1);
+ } while (!((bt_state2 == GPIO_PIN_SET) and (bt_state1 == GPIO_PIN_RESET)));
+}
+
+uint32_t eeprom_crc()
+{
+ const uint8_t* data = eeprom_data;
+
+ while (true)
+ {
+ data++;
+ data++;
+ auto len = *data++;
+ if (!len) break;
+ data += len;
+ }
+
+ uint32_t size = data - eeprom_data;
+
+ return HAL_CRC_Calculate(&hcrc, (uint32_t*)eeprom_data, size);
+}
+
int bm78_initialized()
{
using namespace mobilinkd::tnc;
using namespace mobilinkd::tnc::bm78;
- return HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) == BT_INIT_MAGIC;
+ auto crc = eeprom_crc();
+
+ return HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) == crc;
}
int bm78_initialize()
@@ -444,34 +479,18 @@ int bm78_initialize()
int result = 0;
- if (!enter_program_mode()) result = 1;
- else if (!set_name()) result = 2;
- else if (!set_pin()) result = 3;
- else if (!set_misc()) result = 4;
- else if (!configure_le_service()) result = 5;
+ enter_program_mode();
+ if (!write_eeprom()) result = 1;
exit_program_mode();
-#if 0
- bt_status_init();
-
- if (not enter_command_mode()) result = 1;
- else if (not set_uart()) result = 4;
- else if (not set_work()) result = 7;
- else if (not set_power()) result = 8;
- else if (not set_secure()) result = 9;
- else if (not set_gpio()) result = 3;
- else if (not set_name()) result = 2;
- else if (not set_reset()) result = 10;
- exit_command_mode();
#if 1
if (result == 0) {
- /* Write BT_INIT_MAGIC to RTC back-up register RTC_BKP_DR1 to indicate
- that the HC-05 module has been initialized. */
+ /* Write CRC to RTC back-up register RTC_BKP_DR1 to indicate
+ that the BM78 module has been initialized. */
HAL_PWR_EnableBkUpAccess();
- HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, BT_INIT_MAGIC);
+ HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, eeprom_crc());
HAL_PWR_DisableBkUpAccess();
}
-#endif
#endif
return result;
}
diff --git a/TNC/bm78.h b/TNC/bm78.h
index a7367b3..c55a43c 100644
--- a/TNC/bm78.h
+++ b/TNC/bm78.h
@@ -14,6 +14,19 @@ extern "C" {
#include
#endif
+/**
+ * The BM78 module says that the module is ready about 475ms after start,
+ * but that the proper thing to do is wait until P1_5 (BT_STATE2) is high
+ * and P0_4 (BT_STATE1) is low.
+ *
+ * This waits for the module to reach this state. If it fails to become
+ * ready after 2 seconds, it enters the error handler.
+ *
+ * @note This does not work to detect whether the module is ready for
+ * EEPROM programming, only for transparent data mode. The time required
+ * and state indication for EEPROM programming mode is not specified.
+ */
+void bm78_wait_until_ready(void);
void bm78_state_change(void);
int bm78_disable(void);
int bm78_enable(void);
diff --git a/TNC/bm78_eeprom.cpp b/TNC/bm78_eeprom.cpp
new file mode 100644
index 0000000..d9c39d5
--- /dev/null
+++ b/TNC/bm78_eeprom.cpp
@@ -0,0 +1,106 @@
+
+#include "bm78_eeprom.hpp"
+
+const uint8_t eeprom_data[] = {
+ 0x00, 0x07, 0x03, 0x04, 0x04, 0x24, 0x00, 0x0b,
+ 0x10, 0x54, 0x4e, 0x43, 0x33, 0x20, 0x4d, 0x6f,
+ 0x62, 0x69, 0x6c, 0x69, 0x6e, 0x6b, 0x64, 0x00,
+ 0x00, 0x00, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x01, 0x10,
+ 0x00, 0x31, 0x01, 0x03, 0x00, 0x3b, 0x01, 0x02,
+ 0x00, 0x56, 0x02, 0x11, 0x11, 0x00, 0x5b, 0x10,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0xad, 0x06, 0xc1, 0x0c, 0x08, 0x1b, 0x21,
+ 0x00, 0x01, 0xb6, 0x02, 0x03, 0x20, 0x01, 0xba,
+ 0x07, 0x19, 0x00, 0x0c, 0xff, 0xff, 0x01, 0x01,
+ 0x01, 0xc2, 0x02, 0x00, 0x00, 0x01, 0xc5, 0x0b,
+ 0x03, 0x0c, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
+ 0x02, 0x1f, 0x40, 0x01, 0xd4, 0x10, 0xba, 0xb0,
+ 0x00, 0x08, 0x01, 0x90, 0x00, 0x02, 0x02, 0x00,
+ 0x01, 0x00, 0x06, 0x01, 0x03, 0x00, 0x01, 0xe4,
+ 0x02, 0x00, 0x00, 0x01, 0xea, 0x08, 0x1f, 0x18,
+ 0x10, 0x0b, 0x22, 0x05, 0x05, 0x05, 0x01, 0xf3,
+ 0x02, 0x05, 0x05, 0x01, 0xf6, 0x04, 0x00, 0x01,
+ 0x01, 0x01, 0x01, 0xfb, 0x02, 0x02, 0x01, 0x01,
+ 0xfe, 0x04, 0x00, 0x03, 0x02, 0x03, 0x02, 0x03,
+ 0x02, 0x04, 0x02, 0x02, 0x06, 0x04, 0x00, 0x01,
+ 0x03, 0x02, 0x02, 0x0b, 0x02, 0x03, 0x01, 0x02,
+ 0x0e, 0x04, 0x00, 0x3c, 0x1e, 0x1e, 0x02, 0x13,
+ 0x02, 0x1e, 0x1e, 0x02, 0x17, 0x10, 0x0e, 0x4d,
+ 0x6f, 0x62, 0x69, 0x6c, 0x69, 0x6e, 0x6b, 0x64,
+ 0x20, 0x54, 0x4e, 0x43, 0x33, 0x00, 0x02, 0x27,
+ 0x10, 0x00, 0x43, 0x36, 0x50, 0x36, 0x34, 0x4a,
+ 0x32, 0x4d, 0x5a, 0x58, 0x54, 0x4e, 0x43, 0x33,
+ 0x00, 0x02, 0x37, 0x0b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x42, 0x10, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42,
+ 0x42, 0x41, 0x41, 0x42, 0x42, 0x41, 0x41, 0x42,
+ 0x42, 0x41, 0x02, 0x52, 0x10, 0x00, 0x39, 0x50,
+ 0x56, 0x01, 0x00, 0x00, 0x01, 0x43, 0x68, 0x61,
+ 0x74, 0x62, 0x6f, 0x61, 0x72, 0x02, 0x62, 0x10,
+ 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x49, 0x53,
+ 0x02, 0x72, 0x10, 0x53, 0x43, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x42, 0x4d, 0x02, 0x82, 0x0e, 0x37, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x10, 0x63,
+ 0x6f, 0x6d, 0x2e, 0x69, 0x73, 0x73, 0x63, 0x2e,
+ 0x64, 0x61, 0x74, 0x61, 0x70, 0x61, 0x74, 0x02,
+ 0xb0, 0x10, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0xc0, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x10,
+ 0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x73, 0x73, 0x63,
+ 0x2d, 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x00,
+ 0x02, 0xe0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0xf0, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x10, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x64, 0x20, 0x4c, 0x4c, 0x43, 0x00, 0x00,
+ 0x00, 0x03, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x38, 0x36, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x20, 0x10, 0x54,
+ 0x4e, 0x43, 0x33, 0x00, 0x00, 0x01, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x30, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x40, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0xbb, 0x68, 0x1f, 0x96,
+ 0xb0, 0x01, 0x49, 0xae, 0xc9, 0x46, 0x2a, 0xba,
+ 0x03, 0x60, 0x10, 0x01, 0x00, 0x00, 0x00, 0xbb,
+ 0x68, 0x1f, 0x96, 0xb0, 0x01, 0x49, 0xae, 0xc9,
+ 0x46, 0x2a, 0xba, 0x03, 0x70, 0x10, 0x03, 0x00,
+ 0x00, 0x00, 0xbb, 0x68, 0x1f, 0x96, 0xb0, 0x01,
+ 0x49, 0xae, 0xc9, 0x46, 0x2a, 0xba, 0x03, 0x80,
+ 0x10, 0x02, 0x00, 0x00, 0x00, 0x10, 0x0c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x04, 0x03, 0x90, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0xa0, 0x10, 0x1b,
+ 0x05, 0x08, 0x54, 0x4e, 0x43, 0x33, 0x11, 0x06,
+ 0xbb, 0x68, 0x1f, 0x96, 0xb0, 0x01, 0x49, 0x03,
+ 0xb0, 0x10, 0xae, 0xc9, 0x46, 0x2a, 0xba, 0x01,
+ 0x00, 0x00, 0x00, 0x02, 0x01, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0xc0, 0x10, 0x03, 0x02, 0x0a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd0, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0xe0, 0x10, 0x03, 0x02, 0x01, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0xf0, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0xf0, 0x10, 0x54, 0x4e, 0x43,
+ 0x33, 0x20, 0x32, 0x2e, 0x31, 0x2e, 0x31, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/TNC/bm78_eeprom.hpp b/TNC/bm78_eeprom.hpp
new file mode 100644
index 0000000..f130102
--- /dev/null
+++ b/TNC/bm78_eeprom.hpp
@@ -0,0 +1,8 @@
+// Copyright 2018 Rob Riggs
+// All rights reserved.
+
+#include
+
+extern "C" {
+extern const uint8_t eeprom_data[];
+}
diff --git a/bm78_eeprom b/bm78_eeprom
new file mode 100644
index 0000000..cceb328
--- /dev/null
+++ b/bm78_eeprom
@@ -0,0 +1,59 @@
+# ADDR1 ADDR2 LEN DATA...
+00 07 03 04 04 24 # Class of Device
+00 0B 10 54 4E 43 33 20 4D 6F 62 69 6C 69 6E 6B 64 00 00
+00 1B 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 2B 01 10
+00 31 01 03
+00 3B 01 02
+00 56 02 11 11
+00 5B 10 31 32 33 34 35 36 00 00 00 00 00 00 00 00 00 00
+01 AD 06 C1 0C 08 1B 21 00
+01 B6 02 03 20
+01 BA 07 19 00 0C FF FF 01 01
+01 C2 02 00 00
+01 C5 0B 03 0C 00 00 08 00 08 00 02 1F 40
+01 D4 10 BA B0 00 08 01 90 00 02 02 00 01 00 06 01 03 00
+01 E4 02 00 00
+01 EA 08 1F 18 10 0B 22 05 05 05
+01 F3 02 05 05
+01 F6 04 00 01 01 01
+01 FB 02 02 01
+01 FE 04 00 03 02 03
+02 03 02 04 02
+02 06 04 00 01 03 02
+02 0B 02 03 01
+02 0E 04 00 3C 1E 1E
+02 13 02 1E 1E
+02 17 10 0E 4D 6F 62 69 6C 69 6E 6B 64 20 54 4E 43 33 00
+02 27 10 00 43 36 50 36 34 4A 32 4D 5A 58 54 4E 43 33 00
+02 37 0B 00 00 00 00 00 00 00 00 00 00 00
+02 42 10 41 42 42 41 41 42 42 41 41 42 42 41 41 42 42 41 # Serial Number (can base64 encode the CPUID)
+02 52 10 00 39 50 56 01 00 00 01 43 68 61 74 62 6F 61 72
+02 62 10 64 00 00 00 00 00 00 00 01 00 00 01 00 00 49 53
+02 72 10 53 43 00 00 00 00 00 00 00 00 00 00 00 00 42 4D
+02 82 0E 37 38 00 00 00 00 00 00 00 00 00 00 00 00
+02 A0 10 63 6F 6D 2E 69 73 73 63 2E 64 61 74 61 70 61 74
+02 B0 10 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+02 C0 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+02 D0 10 63 6F 6D 2E 69 73 73 63 2D 74 65 63 68 00 00 00
+02 E0 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+02 F0 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+03 00 10 4D 6F 62 69 6C 69 6E 6B 64 20 4C 4C 43 00 00 00
+03 10 10 00 00 00 00 30 38 36 30 00 00 00 00 00 00 00 00
+03 20 10 54 4E 43 33 00 00 01 00 04 00 00 00 00 00 00 00
+03 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+03 40 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+03 50 10 00 00 00 00 BB 68 1F 96 B0 01 49 AE C9 46 2A BA
+03 60 10 01 00 00 00 BB 68 1F 96 B0 01 49 AE C9 46 2A BA
+03 70 10 03 00 00 00 BB 68 1F 96 B0 01 49 AE C9 46 2A BA
+03 80 10 02 00 00 00 10 0C 00 00 00 00 00 00 00 01 00 04
+03 90 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+03 A0 10 1B 05 08 54 4E 43 33 11 06 BB 68 1F 96 B0 01 49
+03 B0 10 AE C9 46 2A BA 01 00 00 00 02 01 02 00 00 00 00
+03 C0 10 03 02 0A 00 00 00 00 00 00 00 00 00 00 00 00 00
+03 D0 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+03 E0 10 03 02 01 06 00 00 00 00 00 00 00 00 00 00 00 00
+03 F0 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+04 00 09 01 00 00 00 00 00 00 00 00
+04 F0 10 54 4E 43 33 20 32 2E 31 2E 31 00 00 00 00 00 00
+00 00 00
diff --git a/make_bm78_eeprom_c.py b/make_bm78_eeprom_c.py
new file mode 100755
index 0000000..8bbe21f
--- /dev/null
+++ b/make_bm78_eeprom_c.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+import sys
+import re
+
+regex = re.compile("^([0-9A-Fa-f]{2}\s){3}([0-9A-Fa-f]{2}\s?)*(#.*)?$")
+
+bytes = []
+
+for line in sys.stdin:
+ atoms = re.split("\s+", line)
+ for atom in atoms:
+ if len(atom) == 0 or atom == '#':
+ break
+ bytes.append(int(atom, 16))
+
+chunks = [bytes[i:i+8] for i in range(0, len(bytes), 8)]
+
+print("#include ")
+print("const uint8_t eeprom_data[] = {")
+for chunk in chunks:
+ s = ", ".join(['0x{:02x}'.format(x) for x in chunk])
+ print(" {},".format(s))
+print("};")
+
+