Skip to content

Commit

Permalink
OTA updates, code signing, NVS stored credentials, new partition scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
ma-lwa-re committed May 30, 2022
1 parent 9f0119a commit 31289d9
Show file tree
Hide file tree
Showing 18 changed files with 397 additions and 67 deletions.
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ set(SENSORS ON)
# OPTIONAL: Set the temperature scale (C | F | K)
set(SENSORS_SCALE "C")

# OPTIONAL: Enable Over The Air (OTA) updates (ON | OFF)
set(OTA_UPDATES ON)

# OPTIONAL: Set the project version
set(PROJECT_VER "2.4.0.1")

if(HOME_AUTOMATION STREQUAL "HOMEKIT")
set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_LIST_DIR}/../esp-apple-homekit-adk/)
elseif(HOME_AUTOMATION STREQUAL "NEST")
Expand Down
52 changes: 37 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,33 @@ It has been tested and currently works with any desk relying on the Dynamic Moti

The code was developped in C for the ESP32 microcontrollers family. A detailed write-up about the project is available at

- https://ma.lwa.re/dreamdesk
- [`https://ma.lwa.re/dreamdesk`](https://ma.lwa.re/dreamdesk)

## Config
### Select features
### Select Features
Edit the [`CMakeLists.txt`](CMakeLists.txt) file to select what kind of desk you want to control and other optional features.
The project version will be used to check if a newer version is available during the OTA update process.
```
# REQUIRED: Choose your desk type (LOGICDATA | IKEA)
set(DESK_TYPE "LOGICDATA")
# OPTIONAL: Choose your home automation ecosystem (HOMEKIT | NEST | ALEXA | NONE)
set(HOME_AUTOMATION "HOMEKIT")
# OPTIONAL: Use the sensors (ON | OFF)
# OPTIONAL: Enable sensors (ON | OFF)
set(SENSORS ON)
# OPTIONAL: Set the temperature scale (C | F | K)
set(SENSORS_SCALE "C")
```
### Wifi
Rename [`wifi.h.defaults`](main/wifi.h.defaults) to `wifi.h` and set your Wifi SSID and password. You can leave those default values if you're not using any home automation ecosystem.
```
mv main/wifi.h.default main/wifi.h
# OPTIONAL: Enable Over The Air (OTA) updates (ON | OFF)
set(OTA_UPDATES ON)
#define WIFI_SSID "DEFAULT_WIFI_SSID"
#define WIFI_PASS "DEFAULT_WIFI_PASS"
# OPTIONAL: Set the project version
set(PROJECT_VER "2.4.0.1")
```

## Install
## Setup
### Espressif SDK
```
git clone -b v4.4 --recursive https://github.com/espressif/esp-idf.git esp-idf-v4.4
Expand Down Expand Up @@ -61,7 +59,7 @@ cd Tools
cd Dreamdesk
cp ../../../tools/accessory_setup/accessory_setup.csv .
python $IDF_PATH/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py generate accessory_setup.csv accessory_setup.bin 0x6000
esptool.py -p $ESPPORT write_flash 0x340000 accessory_setup.bin
esptool.py -p $ESPPORT write_flash 0x34C000 accessory_setup.bin
```

### Resetting HomeKit Pairing
Expand All @@ -78,11 +76,33 @@ cd $IDF_PATH/../
git clone --recursive https://github.com/ma-lwa-re/dreamdesk.git
cd dreamdesk
idf.py set-target esp32s3
export ESPPORT=/dev/tty.usbserial-130
export ESPPORT=/dev/cu.usbserial-0001
idf.py build flash
```

## Console output
### Wifi
The wifi credentials were previously hardcoded in the [`wifi.h`](main/wifi.h) header, but are now directly written in a standalone partition on the ESP32.

It has the advantage that no secrets are stored in the code section anymore, and thus allows the same code to run on multiple devices, over-the-air (OTA) updates, or modification of wifi credentials without having to reflash the device.

```
cp wifi.csv $IDF_PATH/components/nvs_flash/nvs_partition_generator
cd $IDF_PATH/components/nvs_flash/nvs_partition_generator
# Edit the wifi.csv and replace the DEFAULT_SSID and DEFAULT_PASSWORD strings
python3 nvs_partition_gen.py generate wifi.csv wifi.bin 0x3000
esptool.py -p $ESPPORT write_flash 0x340000 wifi.bin
```

### Code Signing
The integrity of the application can be secure and checked using an RSA signature scheme. The binary is signed after compilation with the private key that can be generated with `espsecure.py` or `openssl`, and the corresponding public key is embedded into the binary for verification.

```
espsecure.py generate_signing_key --version 2 secure_boot_signing_key.pem
```

The bootloader will be compiled with code to verify that an app is signed before booting it. In addition, the signature will be also proofed before updating the firmware and adds significant security against network-based attacks by preventing spoofing of OTA updates.

## Console Output
```
sudo cu -l $ESPPORT -s 115200
```
Expand All @@ -108,7 +128,7 @@ dreamdesk
│   ├── sensors.c
│   ├── sensors.h
│   ├── wifi.c
│   └── wifi.h.default
│   └── wifi.h
├── partitions.csv
├── sdkconfig
└── sdkconfig.defaults
Expand All @@ -118,6 +138,8 @@ dreamdesk
- [ ] IKEA desk testing
- [x] PCB prototype
- [x] PCB assembly
- [x] OTA updates
- [ ] NVS encryption
- [ ] HomeKit memory integration
- [ ] HomeKit sensors integration
- [x] Sensors (humidity + air + temperature)
Expand Down
14 changes: 12 additions & 2 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ elseif(DESK_TYPE STREQUAL "IKEA")
endif()

if(HOME_AUTOMATION STREQUAL "HOMEKIT")
set(WIFI ON)
set(INCLUDE_HOME ./wifi.c ./homekit.c)
elseif(HOME_AUTOMATION STREQUAL "NEST")
set(WIFI ON)
set(INCLUDE_HOME ./wifi.c ./nest.c)
elseif(HOME_AUTOMATION STREQUAL "ALEXA")
set(WIFI ON)
set(INCLUDE_HOME ./wifi.c ./alexa.c)
endif()

Expand All @@ -17,6 +20,13 @@ if(SENSORS)
add_definitions(-DSENSORS_SCALE_${SENSORS_SCALE})
endif()

idf_component_register(SRCS ./main.c ./dreamdesk.c ./lin.c ${INCLUDE_DESK} ${INCLUDE_HOME} ${INCLUDE_SENSORS} INCLUDE_DIRS ".")
if(OTA_UPDATES)
set(WIFI ON)
set(INCLUDE_OTA_UPDATES ./wifi.c ./ota.c)
endif()

idf_component_register(SRCS ./main.c ./dreamdesk.c ./lin.c ${INCLUDE_DESK} ${INCLUDE_HOME}
${INCLUDE_SENSORS} ${INCLUDE_OTA_UPDATES} INCLUDE_DIRS ".")

add_definitions(-D${DESK_TYPE} -D${HOME_AUTOMATION} -DSENSORS_${SENSORS})
add_definitions(-DPROJECT_NAME="${CMAKE_PROJECT_NAME}" -DPROJECT_VER="${PROJECT_VER}" -D${DESK_TYPE}
-DWIFI_${WIFI} -D${HOME_AUTOMATION} -DSENSORS_${SENSORS} -DOTA_UPDATES_${OTA_UPDATES})
1 change: 0 additions & 1 deletion main/dreamdesk.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "string.h"
#include "esp_log.h"
#include "esp_system.h"
Expand Down
1 change: 1 addition & 0 deletions main/dreamdesk.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#define UART_NUM_2_TXD (GPIO_NUM_4)
#define UART_NUM_2_RXD (GPIO_NUM_5)
#define UART_STACK_SIZE (4096)
#define OTA_STACK_SIZE (UART_STACK_SIZE * 2)
#define CONSOLE_BAUD_RATE (115200)
#define ARROW_KEY_UP (0x41)
#define ARROW_KEY_DOWN (0x42)
Expand Down
6 changes: 0 additions & 6 deletions main/homekit.c
Original file line number Diff line number Diff line change
Expand Up @@ -933,9 +933,6 @@ static void InitializePlatform() {
&accessorySetup, &(const HAPPlatformAccessorySetupOptions) { .keyValueStore = &platform.factoryKeyValueStore });
platform.hapPlatform.accessorySetup = &accessorySetup;

// Initialise Wi-Fi
app_wifi_init();

// TCP stream manager.
HAPPlatformTCPStreamManagerCreate(&platform.tcpStreamManager, &(const HAPPlatformTCPStreamManagerOptions) {
/* Listen on all available network interfaces. */
Expand Down Expand Up @@ -1068,9 +1065,6 @@ static void InitializeIP() {
platform.hapAccessoryServerOptions.ip.accessoryServerStorage = &ipAccessoryServerStorage;

platform.hapPlatform.ip.tcpStreamManager = &platform.tcpStreamManager;

// Connect to Wi-Fi
app_wifi_connect();
}

void home_task(void *arg) {
Expand Down
5 changes: 2 additions & 3 deletions main/ikea.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ void desk_handle_lin_frame(lin_frame_t *lin_frame, uint8_t *event_data, uint8_t
ESP_LOG_BUFFER_HEX_LEVEL(IKEA_TAG, &keep_alive_frame, sizeof(keep_alive_frame), ESP_LOG_DEBUG);
} else if(protected_id == LIN_PROTECTED_ID_MOVE) {

if(status_frame_left != NULL && status_frame_right != NULL) {
if(status_frame_right != NULL && status_frame_left != NULL) {
response_frame.height0 = msb0;
response_frame.height1 = lsb0;
response_frame.checksum = checksum((uint8_t*) &response_frame, lin_frame->protected_id);
Expand All @@ -143,8 +143,7 @@ void desk_handle_lin_frame(lin_frame_t *lin_frame, uint8_t *event_data, uint8_t
uart_write_bytes(UART_PORT, &response_frame, sizeof(response_frame));

ESP_LOG_BUFFER_HEX_LEVEL(IKEA_TAG, &response_frame, sizeof(response_frame), ESP_LOG_DEBUG);
status_frame_right = NULL;
status_frame_left = NULL;
status_frame_right = status_frame_left = NULL;
}
} else if(protected_id == LIN_PROTECTED_ID_STATUS_RIGHT || protected_id == LIN_PROTECTED_ID_STATUS_LEFT) {

Expand Down
5 changes: 3 additions & 2 deletions main/ikea.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@
#define DESK_STOP (0x87)
#define DESK_BEFORE_IDLE (0x84)
#define DESK_IDLE (0xFC)
#define DESK_STATUS_READY (0x60)
#define DESK_STATUS_BUSY (0x02) // Frist value after init ??
#define DESK_STATUS_READY (0x00)
#define DESK_STATUS_START_MOVING (0x02)
#define DESK_STATUS_MOVING (0x03)

typedef struct height {
uint8_t msb;
Expand Down
31 changes: 29 additions & 2 deletions main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@
* SOFTWARE.
*/
#include "dreamdesk.h"
#if defined(HOMEKIT)
#include "homekit.h"
#if defined(WIFI_ON)
#include "wifi.h"
#endif
#if defined(SENSORS_ON)
#include "sensors.h"
#endif
#if defined(OTA_UPDATES_ON)
#include "ota.h"
#endif
#if defined(HOMEKIT)
#include "homekit.h"
#endif
#include "esp_log.h"
#include "string.h"
#include "freertos/FreeRTOS.h"
Expand All @@ -42,10 +48,31 @@ void app_main() {
chip_info();
memory_init();

#if defined(WIFI_ON)
app_wifi_credentials();
app_wifi_init();
app_wifi_connect();
#endif

#if defined(SENSORS_ON)
xTaskCreate(sensors_task, "sensors_task", UART_STACK_SIZE, NULL, configMAX_PRIORITIES-9, NULL);
#endif

#if defined(OTA_UPDATES_ON)
const esp_partition_t *running_partition = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;

if(esp_ota_get_state_partition(running_partition, &ota_state) == ESP_OK) {

if(ota_state == ESP_OTA_IMG_PENDING_VERIFY && esp_ota_mark_app_valid_cancel_rollback() == ESP_OK) {
ESP_LOGI(DREAMDESK_TAG, "App is valid, rollback cancelled successfully!");
} else {
ESP_LOGE(DREAMDESK_TAG, "Failed to cancel rollback");
}
}
xTaskCreate(ota_task, "ota_task", OTA_STACK_SIZE, NULL, configMAX_PRIORITIES-8, NULL);
#endif

#if defined(HOMEKIT) || defined(NEST) || defined(ALEXA)
xTaskCreate(home_task, "home_task", HOMEKIT_STACK_SIZE, NULL, configMAX_PRIORITIES-7, NULL);
#endif
Expand Down
Loading

0 comments on commit 31289d9

Please sign in to comment.