diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..d0558179 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,62 @@ +name: CI build + +on: [push, workflow_dispatch] + +env: + PP: AD2IoT-Release + SHELL: /bin/bash + TERM: 'xterm-256color' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Create base Artifact release package structure + run: | + mkdir $PP + cp README.md CHANGELOG.md LICENSE $PP/ + mkdir $PP/esp32 + cp contrib/README-FLASH-ESP32.md contrib/ESP32-DOWNLOAD-TOOL-UPLOADING-FIRMWARE.png $PP/esp32/ + + - name: Install required packages for st-device-c-ref build + run: | + sudo apt-get install -y gperf cmake ninja-build ccache dfu-util + + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Build esp32-poe-iso + run: pio run -e esp32-poe-iso + + - name: Build spiffs.bin for esp32-poe-iso + run: pio run -t buildfs -e esp32-poe-iso + + - name: Create release package folders for each branch. + run: | + mkdir $PP/esp32/esp32-poe-iso-webui + + - name: Add esp32-poe-iso webui firmware build to release package + run: | + cp .pio/build/esp32-poe-iso/firmware.bin $PP/esp32/esp32-poe-iso-webui/ + + - name: Add common firmware files for each branch to the package. + run: | + cp .pio/build/esp32-poe-iso/spiffs.bin $PP/esp32/esp32-poe-iso-webui/ + cp .pio/build/esp32-poe-iso/bootloader.bin $PP/esp32/esp32-poe-iso-webui/ + cp .pio/build/esp32-poe-iso/partitions.bin $PP/esp32/esp32-poe-iso-webui/ + cp .pio/build/esp32-poe-iso/ota_data_initial.bin $PP/esp32/esp32-poe-iso-webui/ + + - name: Upload firmware package Artifact + uses: actions/upload-artifact@v4 + with: + name: AD2IoT-Release-Package + path: AD2IoT-Release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..31c8de93 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,81 @@ +name: CI build release + +on: + release: + types: [published] # Triggers when a release is published + +env: + REL_PATH: AD2IoT-Release + SHELL: /bin/bash + TERM: 'xterm-256color' + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write # Grants permission to write to the repository content (including releases) + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Create base Artifact release package structure + run: | + mkdir $REL_PATH + cp README.md CHANGELOG.md LICENSE $REL_PATH/ + mkdir $REL_PATH/esp32 + cp contrib/README-FLASH-ESP32.md contrib/ESP32-DOWNLOAD-TOOL-UPLOADING-FIRMWARE.png $REL_PATH/esp32/ + + - name: Install required packages for st-device-c-ref build + run: | + sudo apt-get install -y gperf cmake ninja-build ccache dfu-util + + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Build esp32-poe-iso + run: pio run -e esp32-poe-iso + + - name: Build spiffs.bin for esp32-poe-iso + run: pio run -t buildfs -e esp32-poe-iso + + - name: Create release package folders for each branch. + run: | + mkdir $REL_PATH/esp32/esp32-poe-iso-webui + + - name: Add esp32-poe-iso webui firmware build to release package + run: | + cp .pio/build/esp32-poe-iso/firmware.bin $REL_PATH/esp32/esp32-poe-iso-webui/ + + - name: Add common firmware files for each branch to the package. + run: | + cp .pio/build/esp32-poe-iso/spiffs.bin $REL_PATH/esp32/esp32-poe-iso-webui/ + cp .pio/build/esp32-poe-iso/bootloader.bin $REL_PATH/esp32/esp32-poe-iso-webui/ + cp .pio/build/esp32-poe-iso/partitions.bin $REL_PATH/esp32/esp32-poe-iso-webui/ + cp .pio/build/esp32-poe-iso/ota_data_initial.bin $REL_PATH/esp32/esp32-poe-iso-webui/ + + - name: Zip the release folder + run: zip -r esp32-poe-iso-webui.zip $REL_PATH + + - name: Upload firmware package Artifact + uses: actions/upload-artifact@v4 + with: + name: AD2IoT-Release-Package + path: $REL_PATH + + - name: Upload Release Asset + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # The default token provided by GitHub Actions + run: | + # The 'gh release upload' command needs the tag name of the release + TAG_NAME="${{ github.event.release.tag_name }}" + FILE_PATH="./esp32-poe-iso-webui.zip" + echo "Uploading $FILE_PATH to release $TAG_NAME" + # Use --clobber to overwrite the asset if it already exists + gh release upload "$TAG_NAME" "$FILE_PATH" --clobber + # You can also use wildcards for multiple files, e.g., './dist/*.tgz' diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index afe8ed51..00000000 --- a/.travis.yml +++ /dev/null @@ -1,42 +0,0 @@ -dist: xenial -sudo: required - -language: python - -python: - - "nightly" - -os: - - linux - -cache: - pip: true - directories: - - "~/.platformio" - - $HOME/astyle - -install: - - pip install -U platformio - - pio update - -before_script: - - sh test/install_astyle.sh - -script: - - cd $TRAVIS_BUILD_DIR - - export ARTISTIC_STYLE_OPTIONS=".astylerc" - - astyle *.cpp,*.h > astyle.out - - TEST=$(cat astyle.out | wc -l) - - ASTYLE_HEADER_LINES=4 - - | - if [[ $(($TEST - $ASTYLE_HEADER_LINES)) -ne 0 ]]; then - cat astyle.out - git --no-pager diff - echo "Please fix style issues as shown above" - exit 1 - fi - -notifications: - email: - on_success: change - on_failure: change diff --git a/CHANGELOG.md b/CHANGELOG.md index 017d07c3..914990b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,36 +4,65 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] Open issues +- [ ] CORE: TODO: Add astyle testing in new github action worflow. +- [ ] STSDK: TODO: Successful adopting test. ### SM - Sean Mathews coder at f34r.com +- [ ] API: Add countdown tracking for DSC/Ademco exit mode - [ ] CORE: Needed feature ad2_fw_update() to update AD2* firmware. - [ ] CORE: TODO: Monitor limited sockets look for ways to reduce if possible. - [ ] CORE: Push includes down to lower level. main include has too many component specific includes. -- [ ] TWILIO & PUSHOVER: Add virtual partition qualifier to virtual switch command. Currently on the Twilio notification is hard coded to the default virtual partition in slot 0. The Pushover notification currently has no qualifier and sends messages regardless of the partition as long as it matches. Merge these into a single pattern allowing for the user to define it by its ```vpart``` id. - [ ] CORE: Audit Espressif v3.2 api usage look for more that are soon to be deprecated. -- [ ] STSDK: TODO. FIX Ability to build stsdk component inside of pio build environment. Currently only possible to build with STSDK build.py script. - [ ] CORE: FIXME: Setting HOST NAME when using static IP over ethernet not working. - [ ] CORE: FIXME: reboot of esp32 causes connected ser2sock clients to hang. So far various attempts to fix have been unsuccessful. Will need to do some network captures to determine the problem. - [ ] CORE: HUP/RESTART needs to be centralized so cleanup ex(_fifo_destroy) can happen first. How to connect with STSDK having its own restart calls. -- [ ] STSDK: TODO: Find way to set IOT_PUB_QUEUE_LENGTH & IOT_QUEUE_LENGTH from 10 to 20 during build. - [ ] CORE: Noted coredump when doing oil change check and a twilio message goes out. Both are mbedtls web requests. Will need to investigate and possibly serialize web requests. -- [ ] API: Add countdown tracking for DSC/Ademco exit mode - [ ] CORE: Improve: Finish wiring Virtual Switch A & B and Button A & B. -- [ ] STSDK: Improve: Connect Component OutputA & OutputB with switch capabilities tied to hal_ - [ ] CORE: Wishlist: Compile for bare metal BeagleBone Black and Raspberry Pi. https://forums.freertos.org/t/freertos-porting-to-raspberry-pi-3/6686/5. Alternatively run inside an ESP32 Virtual machine on a Pi? - [ ] CORE: TODO: better hardware abstraction. Need to remove _esp_ specific code to make it easier to port to other hardware. Trying to keep the code as POSIX as possible with the limited resources I have. - [ ] CORE: TODO: ```'ping'``` command could come in handy. Again today needed this with ST MQTT servers seeming to be down. +- [ ] STSDK: TODO. FIX Ability to build stsdk component inside of pio build environment. Currently only possible to build with STSDK build.py script. +- [ ] STSDK: Improve: Connect Component OutputA & OutputB with switch capabilities tied to hal_ - [ ] STSDK: TODO: Add SmartThings Zone devices. +- [ ] STSDK: TODO: Find way to set IOT_PUB_QUEUE_LENGTH & IOT_QUEUE_LENGTH from 10 to 20 during build. +- [ ] TWILIO & PUSHOVER: Add virtual partition qualifier to virtual switch command. Currently on the Twilio notification is hard coded to the default virtual partition in slot 0. The Pushover notification currently has no qualifier and sends messages regardless of the partition as long as it matches. Merge these into a single pattern allowing for the user to define it by its ```vpart``` id. - [ ] webUI: Add REST api compatible with the current webapp including a secret key. This is low priority as this method of connection is not very efficient. -### AJ -- [ ] Add a GitHub Action to run a `pio` build on every PR -- [ ] Migrate `astyle` to GitHub Action -- [ ] Update README.md to reflect `pio` build changes - --- ## Releases -## [1.1.1] - 2022-09-18 Sean Mathews - coder @f34rdotcom +## [1.1.0 P2] - 2026-03-17 Sean Mathews - coder @f34rdotcom +Changes: + - Add CI using github Actions to test building and create an Artifact with a release package with compiled firmware and instructions. + - More info on Artifacts: https://docs.github.com/en/actions/managing-workflow-runs/downloading-workflow-artifacts + - Removed travis-ci support. + - Fix missing settings and organized sdkconfig.defaults file. + - Improve error handling to fix null pointer crashes when processing unexpected response from Twilio rest API. + - TODO: Find more time to audit and cleanup code. + - Fixed some small errors in the default configuration ini file and made sure basic switches have examples in components. + - Remove building broken STSDK during github commit. + - Improved documentation fixing errors and adding config file examples in each section. + - Add new component to load firmware from uSD card. Upload firmware.bin to /sdcard using FileZilla and trigger upgrade with command 'UPGD' and 'VERS'. After upgrade the firmware.bin file will be removed from /sdcard folder. + - Disable OTA firmware update component. +### Change log + - [X] SM OTA: Noticed it was trying too hard to connect at first boot after first check timeout. Set retry check time to 5 minutes when network down and on too many fails skip for 24h. + - [X] SM MQTT,TWILIO,PUSOVER: Fix notify regex filter not applied to test causing a switch to fire even if a filter is set that would exclude it. + - [X] SM CORE: Service startup when network is disabled needs to be delayed waiting for the network to start. + - [X] SM CORE: cleanup some compiler warnings about unused vars. Others I need to add more error handling. + - [X] SM API,MQTT,PUSHOVER,TWILIO: Replace "fault" with "trouble" in error messages and code comments. Improve error reporting when validating and loading switches during init. + - [X] SM CORE: Moving from travis-ci to Github Actions for build testing. I ran out of credits :(. No loss now we can generate a release file something that seemed much more difficult with travis-ci. Add a workflow file named ```CI build``` to test and build a release file with compiled firmware and instructions. See the ```CI build``` build Summary page for the Artifacts file ```AD2IoT-Release-Package``` + - [X] SM CORE: Docs improvements. + - [X] SM FTPD: Does not build with stsdk. Had to wrap in extern 'C' because stsdk already imports as extern. Testing did not show any issues and none were expected. + - [X] SM STSDK: Build and uploaded set keys but was not able to adopt. '''Error 81-001 Something went wrong''' + - [X] SM CORE: Testing stsdk and webui modules build. Currently not able to add ```top``` or ```mqtt``` modules with ```stsdk``` build. + - [X] SM CORE: Update README.md docs on building project including stsdk and platformio. + - [X] SM CORE: Add fixes for stsdk code that I have been sitting on. Mostly just CDECL stuff. + - [X] SM CORE: Fix CMakeList.txt to fetch SimpleIni from github and include it for stsdk building. + - [X] SM CORE: Travis build platformio project test. + - [X] SM CORE: Fix sdkconfig.defaults was missing critical bits. Better organized now and "should" build same exact same firmware as in release. + - [X] SM CORE: Fix missing compile flag ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH + - [X] SM TWILIO: Improve error handling and response data validation. + - [X] SM CORE: Small changes to default ad2iot.ini to fix a few switch numbers and add testing for all default switches in components. +## [1.1.0 P1] - 2022-09-18 Sean Mathews - coder @f34rdotcom Changes: - Partition change add new coredump partition. - This change will require manual flash OTA will not work. Hopefully this is the last partition change. diff --git a/README.md b/README.md index a0dc4551..089dafaf 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ * 1. [Overview](#overview) * 2. [Tested and recommended hardware](#tested-and-recommended-hardware) * 3. [Firmware](#firmware) - * 3.1. [webUI build (webui) - alarmdecoder_webui_esp32.bin](#webui-build-(webui)---alarmdecoder_webui_esp32.bin) - * 3.2. [SmartThings build (stsdk) - alarmdecoder_stsdk_esp32.bin](#smartthings-build-(stsdk)---alarmdecoder_stsdk_esp32.bin) + * 3.1. [webUI build (webui) - esp32/esp32-poe-iso-webui](#webui-build-(webui)---esp32/esp32-poe-iso-webui) + * 3.2. [SmartThings build (stsdk) - esp32/esp32-poe-iso-stsdk](#smartthings-build-(stsdk)---esp32/esp32-poe-iso-stsdk) * 4. [Configuring the AD2IoT device](#configuring-the-ad2iot-device) * 5. [AD2Iot CLI - command line interface](#ad2iot-cli---command-line-interface) * 5.1. [Basic commands](#basic-commands) @@ -23,7 +23,7 @@ * 5.8.1. [Configuration for FTP server](#configuration-for-ftp-server) * 6. [Building firmware](#building-firmware) * 6.1. [PlatformIO](#platformio) - * 6.1.1. [TODO: Setup notes](#todo:-setup-notes) + * 6.1.1. [TODO: Setup notes](#platformio-setup-notes) * 6.2. [SmartThings device SDK build environment](#smartthings-device-sdk-build-environment) * 6.2.1. [Setup build environment](#setup-build-environment) * 6.2.2. [Configure the project](#configure-the-project) @@ -37,9 +37,9 @@ /vscode-markdown-toc-config --> # AlarmDecoder IoT Network Appliance - [Latest stable release ![Release Version](https://img.shields.io/github/release/nutechsoftware/AlarmDecoder-IoT.svg?style=plastic) ![Release Date](https://img.shields.io/github/release-date/nutechsoftware/AlarmDecoder-IoT.svg?style=plastic)](https://github.com/nutechsoftware/AlarmDecoder-IoT/releases/latest/) [![Travis (.org) branch](https://img.shields.io/travis/nutechsoftware/AlarmDecoder-IoT/master?style=plastic)](https://travis-ci.org/nutechsoftware/AlarmDecoder-IoT) + [Latest stable release ![Release Version](https://img.shields.io/github/release/nutechsoftware/AlarmDecoder-IoT.svg?style=plastic) ![Release Date](https://img.shields.io/github/release-date/nutechsoftware/AlarmDecoder-IoT.svg?style=plastic)](https://github.com/nutechsoftware/AlarmDecoder-IoT/releases/latest/) [![.github/workflows/build.yml](https://github.com/nutechsoftware/AlarmDecoder-IoT/actions/workflows/build.yml/badge.svg?branch=master&event=push)](https://github.com/nutechsoftware/AlarmDecoder-IoT/actions/workflows/build.yml) - [Latest development branch ![Development branch](https://img.shields.io/badge/dev-yellow?style=plastic) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/nutechsoftware/AlarmDecoder-IoT/dev?style=plastic)](https://github.com/nutechsoftware/AlarmDecoder-IoT/tree/dev) [![Travis (.org) branch](https://img.shields.io/travis/nutechsoftware/AlarmDecoder-IoT/dev?style=plastic)](https://travis-ci.org/nutechsoftware/AlarmDecoder-IoT) + [Latest development branch ![Development branch](https://img.shields.io/badge/dev-yellow?style=plastic) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/nutechsoftware/AlarmDecoder-IoT/dev?style=plastic)](https://github.com/nutechsoftware/AlarmDecoder-IoT/tree/dev) [![.github/workflows/build.yml](https://github.com/nutechsoftware/AlarmDecoder-IoT/actions/workflows/build.yml/badge.svg?branch=dev&event=push)](https://github.com/nutechsoftware/AlarmDecoder-IoT/actions/workflows/build.yml) ## 1. Overview @@ -54,27 +54,25 @@ The device firmware is stored in the onboard non-volatile flash making the devic * ESP32-DevKitC-32E. WiFi only applications. ## 3. Firmware -The firmware is already compiled for the ESP32-POE-ISO board and is available in the release page or via OTA(over-the-air) update. Currently the firmware is built with the following build flags 'stsdk' and 'webui'. +Pre compiled firmware for the ESP32-POE-ISO board is available in the release package to flash to a new device or via OTA(over-the-air) update for devices that are already programmed with a current AD2IoT firmware. -To switch to a specific build over the internet using OTA include the buildflag in the upgrade command. -- ```upgrade webui``` +Currently two builds are available ```stsdk``` and ```webui```. To switch to a specific build over the internet using OTA include the buildflag in the upgrade command. +- ```upgradeota webui``` If the upgrade fails it may be the result of low memory on the device. Try disabling features restart the device and try again. Example. ```webui disable Y```. If all else fails install the latest release of the AD2IoT firmware over USB. -See the README-FLASH.MD inside the release file for instructions on flashing the firmware over the ESP32-POE-ISO USB port. +See the README-FLASH-ESP32.md inside the release file for instructions on flashing the firmware to a ESP32-POE-ISO board over USB. -### 3.1. webUI build (webui) - alarmdecoder_webui_esp32.bin -- Enabled components: Pushover, Twilio, Sendgrid, ser2sock, webUI, MQTT, ftpd. - -This build uses the latest ESP-IDF v4.3 with the SmartThings driver is disabled. +### 3.1. webUI build (webui) - esp32/esp32-poe-iso-webui +- Enabled components: Pushover, Twilio, Sendgrid, ser2sock, webUI, MQTT, ftpd, top. - Optional uSD card with a FAT32 root partition is required for webUI. - Copy the contents of contrib/webUI/flash-drive folder into the root directory of the card. - Optionally place the sample configuration file [data/ad2iot.ini](data/ad2iot.ini) on the root directory to override the default config storage on the internal /spiffs partition. - Reboot the device after inserting the card for changes to take effect. -### 3.2. SmartThings build (stsdk) - alarmdecoder_stsdk_esp32.bin -- Enabled components: Pushover, Twilio, Sendgrid, ser2sock, SmartThings. +### 3.2. SmartThings build (stsdk) - esp32/esp32-poe-iso-stsdk +- Enabled components: SmartThings, Pushover, Twilio, Sendgrid, ser2sock, webUI, MQTT, ftpd, top. This build is compiled using the [st-device-sdk-c-ref](https://github.com/SmartThingsCommunity/st-device-sdk-c-ref) from the SmartThings github repo and has the webUI component disabled. @@ -99,7 +97,7 @@ Configuration of the AD2IoT is done directly over the USB serial port using a co - To save settings to the [ad2iot.ini](data/ad2iot.ini) use the ```restart``` command. This will save any settings changed in memory to the active configuration file before restarting to load the new settings. - Configuration using the configuration file. - The ad2iot will first attempt to load the [ad2iot.ini](data/ad2iot.ini) config file from the first fat32 partition on a uSD card if attached. If this fails it will attempt to load the same file from the internal spiffs partition. If this fails the system will use defaults and save any changes on ```restart``` command to the internal spiffs partition in the file [ad2iot.ini](data/ad2iot.ini). - - To access /sdcard/ad2iot.ini and /spiffs/ad2iot.ini files over the network enable the [FTPD component](#ftp-daemon-component). Use the custom FTP command ```REST``` to restart the ad2iot and force it to load the new configuration. + - To access /sdcard/ad2iot.ini and /spiffs/ad2iot.ini files over the network enable the [FTPD component](#ftp-daemon-component). With FileZilla edit the configuration upload and send a custom FTP command using the "Server" menu and the "Enter custom command.." sub menu. Enter ```REST``` to restart the ad2iot and force it to load the configuration. - Sample config file with internal documentation can be found here [data/ad2iot.ini](data/ad2iot.ini) - Be sure to set the ftpd acl to only allow trusted systems to manage the files on the uSD card. @@ -110,9 +108,9 @@ Configuration of the AD2IoT is done directly over the USB serial port using a co - ```ad2source COM 4:36``` - Network shared AD2* device over ser2sock - ```ad2source SOCK 192.168.0.121:10000``` - - Configure the AlarmDecoder firmware settings for the the attached alarm system. For Ademco mode a free keypad address needs to be assigned to each partition to control. DSC mode is ZeroConf and only requires the mode 'D' and the partition # from 1-8. + - Configure the AlarmDecoder firmware settings for the the attached alarm system. For Ademco mode a free keypad address needs to be assigned to each partition to control. DSC mode is ZeroConf and only requires the mode 'D' and the partition # from 1-8 followed by the Slot 1-8 So address=11 is Partition #1 Slot #1. - Typical Ademco Vista setting: ```ad2config mode=A&address=18``` - - Typical DSC Power Series setting: ```ad2config mode=D&address=1``` + - Typical DSC Power Series setting: ```ad2config mode=D&address=11``` - Configure the default partition address and optional zones in partition 1. - ```partition 1 18 2,3,4,5``` - Define any additional partitions and optional zones. @@ -216,20 +214,40 @@ ftp daemon 12 B 1 7376 0 615 0.00 0.00 Stack: Minimum stack free bytes, CPU#: CPU affinity TBusy: % busy total, Busy: % busy now ``` -- upgrade +- upgradeota ```console -Usage: upgrade [buildflag] +Usage: upgradeota [buildflag] - Preform an OTA upgrade now download and install new flash + Preform an OTA firmware upgrade now download and install new flash Options: buildflag Specify a different build or use current if omitted See release page for details on available builds ``` -- version +- versionota +```console +Usage: versionota + + Report the current and available version and build flag +``` +```console +AD2IOT # versionota +Installed version(AD2IOT-1103) build flag (webui) available version(AD2IOT-1103). +``` +- upgradeusd ```console -Usage: version +Usage: upgradeusd - Report the current and available version + Preform an firmware upgrade reading firmware.bin from uSD disk +``` +- versionusd +```console +Usage: versionusd + + Report the current firmware version and build flag +``` +```console +AD2IOT # versionusd +Installed version(AD2IOT-1103) build flag (webui). ``` - netmode ```console @@ -253,6 +271,11 @@ Examples: Ethernet Static IPv4 address. ```netmode E mode=s&ip=192.168.1.111&mask=255.255.255.0&gw=192.168.1.1&dns1=4.2.2.2&dns2=8.8.8.8``` ``` +```console +# Example config file ini setting +# Enable ethernet driver and use DHCP for setting address +netmode = E mode=d +``` - switch ```console Usage: switch [command] [] @@ -274,23 +297,61 @@ Commands: close IDX REGEX CLOSE event REGEX filter for IDX 1-8 trouble IDX REGEX TROUBLE event REGEX filter for IDX 1-8 Options: - switchId ad2iot virtual switch ID 1-255 + swid ad2iot virtual switch ID 1-255 IDX REGEX index 1-8 for multiple tests REGEX Regular expression or exact match string. TYPE Message types [ALPHA,LRR,REL,EXP,RFX,AUI,KPM,KPE, CRC,VER,ERR,EVENT] -Common search verbs for type EVENT - arm {STAY|AWAY} - disarm - power {AC|BATTERY} - ready {ON|OFF} - alarm {ON/OFF} - fire {ON|OFF} - chime {ON|OFF} - exit {ON|OFF} - programming {ON|OFF} - zone {OPEN,CLOSE,TROUBLE} ZONE_NUMBER +Common message verbs and arguments received for type EVENT + ARM {STAY|AWAY} + DISARM + POWER {AC|BATTERY} + READY {ON|OFF} + ALARM {ON/OFF} + FIRE {ON|OFF} + CHIME {ON|OFF} + EXIT {ON|OFF} + PROGRAMMING {ON|OFF} + ZONE {OPEN,CLOSE,TROUBLE} {zero padded 3 digit zone number} +``` +```console +# Example config file ini section [switch N] +[switch 10] +# Test ZONE tracking event for zone 3 +default = 0 +reset = 0 +types = EVENT +filter = ZONE.* +open 1 = ZONE OPEN 003 +close 1 = ZONE CLOSE 003 +trouble 1 = ZONE TROUBLE 003 + +[switch 60] +# RFX serial 0123456 +default = -1 +reset = 0 +types = RFX +filter = !RFX:0123456,.* +open 1 = !RFX:0123456,1....... +close 1 = !RFX:0123456,0....... +trouble 1 = !RFX:0123456,......1. + +[switch 91] +# AC switch +default = -1 +reset = 0 +types = EVENT +open 1 = POWER BATTERY +close 1 = POWER AC + +[switch 95] +# Fire switch +default = -1 +reset = 0 +types = EVENT +open 1 = FIRE ON +close 1 = FIRE OFF ``` - code ```console @@ -302,6 +363,12 @@ Options: - Delete entry value Code string ``` +```console +# Example config file ini setting +[code] +1 = 4112 +2 = 1234 +``` - ad2term ```console Usage: ad2term [reset] @@ -325,6 +392,14 @@ Options: device. Can be partial config. Example set mode Ademco with default address 18. ```ad2config mode=A&address=18``` + Example set mode DSC with default Partiion 1 Slot 2. + ```ad2config mode=D&address=12``` +``` +```console +# Example config file ini setting +# This will push these settings to the AD2* board. +# Avoid multiple systems trying to enforce this setting. That would be bad. +ad2config = address=18&mode=A ``` - partition ```console @@ -362,6 +437,13 @@ Examples: ```partition 2 -``` Note: address - will remove an entry. ``` +```console +# Example config file ini section [partition N] +# Note: Must add [zone N] sections for each zone for zone details. +[partition 1] +address = 18 +zones = 2,3,4,5,6,24,25,26 +``` - zone ```console Usage: zone [- | ] @@ -373,6 +455,23 @@ Options: value json string with type and alpha attributes {"type": "smoke", "alpha": "TESTING LAB SMOKE"} ``` +```console +# Example config file ini section [zone NN] +# Note: Must add each zone to the [partition N] zones list. +# For MQTT "type" is translated to "device_class" for the device discovery document. +# "alpha" is translated to "name" +# https://www.home-assistant.io/integrations/mqtt/#mqtt-discovery +# https://www.home-assistant.io/integrations/binary_sensor/#device-class +# +[zone 1] +description = {"type": "smoke", "alpha": "Smoke Alarm"} + +[zone 3] +description = {"type": "door", "alpha": "Back Door"} + +[zone 4] +description = {"type": "motion", "alpha": "Entry Motion"} +``` - ad2source ```console Usage: ad2source [( )] @@ -389,6 +488,11 @@ Examples: Set source to local attached uart with TX on GPIO 4 and RX on GPIO 36. ```ad2source COM 4:36``` ``` +```console +# Example config file ini setting +# Use caution changing this setting it can change GPIO pin states. +ad2source = C 4:36 +``` ### 5.2. Ser2sock server component Ser2sock allows sharing of a serial device over a TCP/IP network. It also supports encryption and authentication via OpenSSL. Typically configured for port 10000 several home automation systems are able to use this protocol to talk to the AlarmDecoder device for a raw stream of messages. Please be advised that network scanning of this port can lead to alarm faults. It is best to use the Access Control List feature to only allow specific hosts to communicate directly with the AD2* and the alarm panel. @@ -404,6 +508,12 @@ Examples: ```ser2sockd enable Y``` ```ser2sockd acl 192.168.0.0/28,192.168.1.0-192.168.1.10,192.168.3.4``` ``` +```console +# Example config file ini setting +[ser2sockd] +enable = true +acl = 192.168.0.0/16, 10.10.0.0/16 +``` ### 5.3. Web User Interface webUI component This component provides a simple HTML5+WebSocket user interface with realtime alarm status using push messages over WebSocket. Buttons for Arming, Disarming, Exiting, and sending panic events. Panic buttons require pressing the button three times in 5 seconds to help prevent false alarms.
@@ -422,7 +532,12 @@ Examples: ```webui enable Y``` ```webui acl 192.168.0.0/28,192.168.1.0-192.168.1.10,192.168.3.4``` ``` - +```console +# Example config file ini setting +[webui] +enable = true +acl = 192.168.0.0/16, 10.10.0.0/16 +``` ### 5.4. SmartThings Direct Connected device. ###### ```Only available in stsdk firmware build``` Direct-connected devices connect directly to the SmartThings cloud. The SDK for Direct Connected Devices is equipped to manage all MQTT topics and onboarding requirements, freeing you to focus on the actions and attributes of your device. To facilitate the development of device application in an original chipset SDK, the core device library and the examples were separated into two git repositories. That is, if you want to use the core device library in your original chipset SDK that installed before, you may simply link it to develop a device application in your existing development environment. For more info see https://github.com/SmartThingsCommunity/st-device-sdk-c-ref. @@ -526,7 +641,7 @@ Commands: token acid [hash] Twilio Auth Token from acid [address] Validated Email or Phone # to acid [address] Email or Phone # - type acid [M|C|E] Notification type Mail, Call, EMail + type acid [M|C|E] Notification type SMS, Call, EMail format acid [format] Output format string switch swid SCMD [ARG] Configure switches Sub-Commands: switch @@ -574,28 +689,28 @@ Options: # Twilio config section [twilio] - # Account ID #1 EMail using api.sendgrid.com + # Account storage ID(acid) #1 EMail using api.sendgrid.com sid 1 = NA token 1 = Abcdefg012345.... from 1 = foo@example.com to 1 = bar@example.com - type 1 = email + type 1 = E format 1 = {} - # Account ID #2 Text message using api.twilio.com + # Account storage ID(acid) #2 Text message using api.twilio.com sid 2 Abcdefg012345.... token 2 = Abcdefg012345.... from 2 = 15555551234 to 2 = 15555551234 - type 2 = text + type 2 = M format 2 = {} - # Account ID #3 Voice Twiml call using api.twilio.com + # Account storage ID(acid) #3 Voice Twiml call using api.twilio.com sid 3 = Abcdefg012345.... token 3 = Abcdefg012345.... from 3 = 15555551234 to 3 = 15555551234 - type 3 = call + type 3 = C format 3 = {0}{0}{0} ``` - Send notifications from profile in slot #0 for 5800 RF sensor with SN 0123456 and trigger on OPEN(ON), CLOSE(OFF) and TROUBLE REGEX patterns. In this example the Text or EMail sent would event contain the user defined message. @@ -710,12 +825,15 @@ If enabled the daemon will allow update of files on the attached uSD card. This This daemon only supports one command and control connection at a time. Be sure to disable or limit the client used to a single connection or the client may appear to stall or timeout. +Custom FTPD commands can be sent using FileZilla with the "Server" menu and the "Enter custom command.." sub menu. +Current custom commands are 'REST' to restart the device, 'UPGD' to trigger a firmware upgrade check, and 'VERS' get get the current firmware version. + - Implementation notes: - root folder contains two virtual paths. * /spiffs - The spiffs partition. spiffs does not allow sub folders and has limits on file names. * /sdcard - - The first fat32 partition on a uSD card if available and connected + - The first fat32 partition on a uSD card if available and connected. For firmware updates over uSD place the ```firmware.bin``` file here that use usdupgrade cli command or issue a ```UPGD``` command using FileZilla connected to the ad2iot. - Basic commands to rename, remove, upload, and dowloand files on the spiffs partition and attached uSD. #### 5.8.1. Configuration tool for ftp server @@ -731,27 +849,68 @@ Examples: ```ftpd enable Y``` ```ftpd acl 192.168.0.0/28,192.168.1.0-192.168.1.10,192.168.3.4``` ``` +```console +# Example config file ini section +[ftpd] +## Enable / Disable true or false +enable = true +## Access control list +acl = 192.168.0.0/16, 10.10.0.0/16 +``` ## 6. Building firmware ### 6.1. PlatformIO -#### 6.1.1. TODO: Setup notes +#### 6.1.1. Open the project and use the platformio UI inside of vscode to build and flash. Select esp32dev or esp32-poe-iso tree and select Build to compile. ### 6.2. SmartThings device SDK build environment #### 6.2.1. Setup build environment -- Follow the instructions in the [SmartThings SDK for Direct connected devices for C](https://github.com/SmartThingsCommunity/st-device-sdk-c-ref) project for setting up a build environment. Confirm you can build the switch_example before continuing. -- Select the esp32 build environment. Branch v1.4 seems to be the current active branch and uses espidf v4.0.1-317-g50b3e2c81. +- Based on the instructions in the [SmartThings SDK for Direct connected devices for C](https://github.com/SmartThingsCommunity/st-device-sdk-c-ref) for setting up a build environment and [This community post](https://community.smartthings.com/t/how-to-build-direct-connected-devices/204055) to build the code inside of the stsdk c-ref build environment. ``` +# Make the root esp folder. +cd ~ +mkdir esp + +# Get and install esp-idf toolchain v5.0(AF), v4.3.2(AF), cd ~/esp - git clone https://github.com/SmartThingsCommunity/st-device-sdk-c-ref.git -b release/v1.4 - cd st-device-sdk-c-ref -./setup.py esp32 -``` +git clone -b v5.0 --recursive https://github.com/espressif/esp-idf.git -- Place the contents of this his project in ```st-device-sdk-c-ref/apps/esp32/``` +cd ~/esp/esp-idf +## Added esp32 to the end. Need to test above again with this change. +./install.sh esp32 -#### 6.2.2. Configure the project +## At the end will be prompted to set the environment for building by sourcing the exports.sh file created during setup of stsdk c-ref. You can also follow the espressif docs and set the alias get_idf to '. $HOME/esp/esp-idf/export.sh' + +. ./export.sh +# Install the xtensa esp32 toolchain +## Is this automagic now? During setup of esp-idf above. +### Downloading xtensa-esp32-elf-gcc8_4_0-esp-2021r2-linux-amd64.tar.gz to /home/mathewss/.espressif/dist/xtensa-esp32-elf-gcc8_4_0-esp-2021r2-linux-amd64.tar.gz.tmp +cd ~/esp +wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-97-gc752ad5-5.2.0.tar.gz +tar -xvf xtensa-esp32-elf-linux64-1.22.0-97-gc752ad5-5.2.0.tar.gz + +# Install st-device-sdk-c-ref master branch currently v1.7.5. +cd ~/esp +git clone https://github.com/SmartThingsCommunity/st-device-sdk-c-ref.git +cd st-device-sdk-c-ref +python setup.py esp32 + +# Confirm the switch example can be built before continuing. +python build.py esp32 switch_example + +# Link our external AlarmDecoder-IoT project into the apps folder for st-device-sdk-c-ref. +# or fetch the AlarmDecoder-IoT project directly inside of the st-device-sdk-c-ref apps/esp32/ folder. +ln -s ~/Code/AlarmDecoder-IoT ~/esp/st-device-sdk-c-ref/apps/esp32 +``` + +#### 6.2.2. Configure the project +Run menu config and enable/disable components. Each module will consume code space and memory so test with the ```top``` command to be sure resources are not being exausted. + - Component config: + - Enable ```SmartThings IoT Core``` + - Enable ESP32 support. ```BSP Support(ESP32)``` + - Disable ```AD2Iot * FTP demon``` ``` -./build.sh esp32 AlarmDecoder-IoT menuconfig +cd ~/esp/st-device-sdk-c-ref +python build.py esp32 AlarmDecoder-IoT menuconfig ``` #### 6.2.3. Build, Flash, and Run @@ -759,7 +918,8 @@ cd ~/esp Build the project and flash it to the board, then run monitor tool to view serial output: ``` -./build.sh esp32 AlarmDecoder-IoT build flash monitor -p /dev/ttyUSB0 +cd ~/esp/st-device-sdk-c-ref +python build.py esp32 AlarmDecoder-IoT build flash monitor -p /dev/ttyUSB0 ``` (To exit the serial monitor, type ``Ctrl-]``.) diff --git a/address_info.txt b/address_info.txt index 29f7cf37..5cce8a38 100644 --- a/address_info.txt +++ b/address_info.txt @@ -2,4 +2,4 @@ bootloader.bin : 0x1000 partitions.bin : 0x8000 ota_data_initial.bin : 0xd000 spiffs.bin : 0x11000 -alarmdecoder_ad2iot_esp32.bin : 0x60000 \ No newline at end of file +alarmdecoder_ad2iot_esp32.bin : 0x80000 \ No newline at end of file diff --git a/components/ad2mqtt/ad2mqtt.cpp b/components/ad2mqtt/ad2mqtt.cpp index 2638ea5d..b439b112 100644 --- a/components/ad2mqtt/ad2mqtt.cpp +++ b/components/ad2mqtt/ad2mqtt.cpp @@ -36,6 +36,9 @@ static const char *TAG = "MQTT"; // esp component includes #include "mqtt_client.h" +// enable verbose debug logging +//#define MQTT_DEBUG + /* Constants that aren't configurable in menuconfig */ #define MQTT_COMMAND "mqtt" #define MQTT_ENABLE_SUBCMD "enable" @@ -504,12 +507,15 @@ void mqtt_on_connect(esp_mqtt_client_handle_t client) * @param [in]event_data void * * */ -static esp_err_t ad2_mqtt_event_handler(esp_mqtt_event_handle_t event_data) +static void ad2_mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { - esp_mqtt_client_handle_t client = event_data->client; + esp_mqtt_event_handle_t event = (esp_mqtt_event_t*)event_data; + esp_mqtt_client_handle_t client = event->client; + int msg_id; + // your_context_t *context = event->context; - switch (event_data->event_id) { + switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: #if defined(MQTT_EVENT_LOGGING) ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); @@ -523,24 +529,24 @@ static esp_err_t ad2_mqtt_event_handler(esp_mqtt_event_handle_t event_data) break; case MQTT_EVENT_SUBSCRIBED: #if defined(MQTT_EVENT_LOGGING) - ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event_data->msg_id); + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); #endif break; case MQTT_EVENT_UNSUBSCRIBED: #if defined(MQTT_EVENT_LOGGING) - ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event_data->msg_id); + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); #endif break; case MQTT_EVENT_PUBLISHED: #if defined(MQTT_EVENT_LOGGING) - ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event_data->msg_id); + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); #endif break; case MQTT_EVENT_DATA: #if defined(MQTT_EVENT_LOGGING) ESP_LOGI(TAG, "MQTT_EVENT_DATA"); - printf("TOPIC=%.*s\r\n", event_data->topic_len, event_data->topic); - printf("DATA=%.*s\r\n", event_data->data_len, event_data->data); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); #endif // test if commands subscription is enabled if ( commands_enabled ) { @@ -551,19 +557,19 @@ static esp_err_t ad2_mqtt_event_handler(esp_mqtt_event_handle_t event_data) topic_path += mqttclient_UUID; topic_path += "/" MQTT_COMMANDS_TOPIC; - if ( event_data->topic_len == topic_path.length() ) { - std::string topic( event_data->topic, event_data->topic_len ); + if ( event->topic_len == topic_path.length() ) { + std::string topic( event->topic, event->topic_len ); if ( topic.compare(topic_path) == 0 ) { // We only want fresh messages no recordings. // Check for retain flag skip if true. - if ( event_data->retain ) { + if ( event->retain ) { break; } // sanity check the event_data->data_len - if ( event_data->data_len < MQTT_COMMAND_MAX_DATA_LEN ) { - std::string command( event_data->data, event_data->data_len ); + if ( event->data_len < MQTT_COMMAND_MAX_DATA_LEN ) { + std::string command( event->data, event->data_len ); // grab the json buffer // { // partition: {{ number partition ID see ```partition``` command. }}, @@ -623,7 +629,7 @@ static esp_err_t ad2_mqtt_event_handler(esp_mqtt_event_handle_t event_data) } else if ( action.compare("SEND_RAW") == 0 ) { ad2_send(arg); } else if ( action.compare("FW_UPDATE_IOT") == 0 ) { - hal_ota_do_update(""); + hal_do_fwupdate(""); } else if ( action.compare("FW_UPDATE_AD2") == 0 ) { ad2_fw_update(arg.c_str()); } else if ( action.compare("FW_CONFIG_IOT") == 0 ) { @@ -659,7 +665,6 @@ static esp_err_t ad2_mqtt_event_handler(esp_mqtt_event_handle_t event_data) break; } - return ESP_OK; } /** @@ -819,7 +824,9 @@ void mqtt_free() void on_search_match_cb_mqtt(std::string *msg, AD2PartitionState *s, void *arg) { AD2EventSearch *es = (AD2EventSearch *)arg; - +#if defined(MQTT_DEBUG) + ESP_LOGI(TAG, "ON_SEARCH_MATCH_CB: '%s' -> '%s' [switch %i]", msg->c_str(), es->out_message.c_str(), es->INT_ARG); +#endif std::string message = es->out_message; // Grab the topic using the virtual switch ID pre saved into INT_ARG @@ -843,6 +850,13 @@ void on_search_match_cb_mqtt(std::string *msg, AD2PartitionState *s, void *arg) MQTT_DEF_QOS, MQTT_DEF_RETAIN, MQTT_DEF_STORE); + + if (msg_id) { + ESP_LOGI(TAG,"Switch #%i match message '%s'. Sending '%s'", es->INT_ARG, msg->c_str(), es->out_message.c_str()); + } else { + ESP_LOGE(TAG,"Error adding mqtt message."); + } + cJSON_free(state); cJSON_Delete(root); } @@ -926,7 +940,6 @@ static void _cli_cmd_mqtt_smart_alert_switch(std::string &subcmd, const char *in MQTT_CONFIG_SWITCH_SUFFIX_TROUBLE); std::string sk; - bool command_found = false; ad2_printf_host(false, "## [mqtt] switch %i configuration.\r\n", swID); while (ss >> sk) { tbuf = ""; @@ -1115,40 +1128,23 @@ void mqtt_register_cmds() } /** - * Initialize queue and SSL + * @brief daemon startup task + * + * @param [in]pvParameters currently not used NULL. */ -void mqtt_init() +void mqtt_startup_task(void *pvParameters) { - // if netif not enabled then we can't start. - if (!hal_get_netif_started()) { - ad2_printf_host(true, "%s client disabled. Network interface not enabled.", TAG); - return; - } - - bool en = false; - ad2_get_config_key_bool(MQTT_CONFIG_SECTION, MQTT_ENABLE_SUBCMD, &en); - - // nothing more needs to be done once commands are set if not enabled. - if (!en) { - ad2_printf_host(true, "%s client disabled.", TAG); - return; - } - -#if 0 // debug logging settings. - esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE); - esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE); - esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE); - esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); - esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); - esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE); +#if defined(MQTT_DEBUG) + ESP_LOGI(TAG, "MQTT waiting for network layer to start."); #endif - - // load commands subscription enable/disable setting - ad2_get_config_key_bool(MQTT_CONFIG_SECTION, MQTT_CMDEN_SUBCMD, &commands_enabled); - - // generate our client's unique user id. UUID. - ad2_genUUID(0x10, mqttclient_UUID); - ad2_printf_host(true, "%s: Init UUID: %s", TAG, mqttclient_UUID.c_str()); + while (1) { + if (!hal_get_netif_started()) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } else { + break; + } + } + ESP_LOGI(TAG, "Network layer is OK. %s client starting.", TAG); // configure and start MQTT client esp_err_t err; @@ -1180,26 +1176,59 @@ void mqtt_init() LWT_TOPIC+=mqttclient_UUID; LWT_TOPIC+="/status"; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" - const esp_mqtt_client_config_t mqtt_cfg = { - .event_handle = ad2_mqtt_event_handler, - .uri = brokerURL.c_str(), - .client_id = mqttclient_UUID.c_str(), - .lwt_topic = LWT_TOPIC.c_str(), - .lwt_msg = "offline", - .lwt_qos = 1, // For LWT use QOS1 - .lwt_retain = 1 // FOr LWT use Retain - }; -#pragma GCC diagnostic pop + // Build mqtt client config + esp_mqtt_client_config_t mqtt_cfg = {}; + mqtt_cfg.broker.address.uri = brokerURL.c_str(); + mqtt_cfg.credentials.client_id = mqttclient_UUID.c_str(); + mqtt_cfg.session.last_will.topic = LWT_TOPIC.c_str(); + mqtt_cfg.session.last_will.msg = "offline"; + mqtt_cfg.session.last_will.qos = 1; + mqtt_cfg.session.last_will.retain = 1; // Create and start the client. mqtt_client = esp_mqtt_client_init(&mqtt_cfg); + + // register event callback + esp_mqtt_client_register_event(mqtt_client, (esp_mqtt_event_id_t)ESP_EVENT_ANY_ID, ad2_mqtt_event_handler, NULL); + err = esp_mqtt_client_start(mqtt_client); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_mqtt_client_start return error: %s.", esp_err_to_name(err)); } + vTaskDelete(NULL); +} + +/** + * Initialize queue and SSL + */ +void mqtt_init() +{ + bool en = false; + ad2_get_config_key_bool(MQTT_CONFIG_SECTION, MQTT_ENABLE_SUBCMD, &en); + + // nothing more needs to be done once commands are set if not enabled. + if (!en) { + ad2_printf_host(true, "%s client disabled.", TAG); + return; + } + +#if 0 // debug logging settings. + esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE); + esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); + esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE); +#endif + + // load commands subscription enable/disable setting + ad2_get_config_key_bool(MQTT_CONFIG_SECTION, MQTT_CMDEN_SUBCMD, &commands_enabled); + + // generate our client's unique user id. UUID. + ad2_genUUID(0x10, mqttclient_UUID); + ad2_printf_host(true, "%s: Init UUID: %s", TAG, mqttclient_UUID.c_str()); + // Subscribe standard AlarmDecoder events AD2Parse.subscribeTo(ON_ARM, mqtt_on_state_change, (void *)ON_ARM); AD2Parse.subscribeTo(ON_DISARM, mqtt_on_state_change, (void *)ON_DISARM); @@ -1302,6 +1331,7 @@ void mqtt_init() // load [switch N] required regex match settings std::string prefilter_regex; ad2_get_config_key_string(key.c_str(), AD2SWITCH_SK_FILTER, prefilter_regex); + es1->PRE_FILTER_REGEX = prefilter_regex; // Load all regex search patterns for open, close, and trouble sub keys. std::string regex_sk_list = AD2SWITCH_SK_OPEN " " @@ -1347,18 +1377,18 @@ void mqtt_init() } else { // incomplete switch so delete it. delete es1; - ESP_LOGE(TAG, "Error in config section [switch %i]. Missing required open, close, or fault filter expressions.", swID); + ESP_LOGE(TAG, "Error in config section [switch %i]. Need at least one open, close, or trouble filter expressions.", swID); } } else { if (open_output_format.length() || close_output_format.length() || trouble_output_format.length()) { - ESP_LOGE(TAG, "Error in config for switch [switch %i]. Missing on or more required open,close, or fault output expressions.", swID); + ESP_LOGE(TAG, "Section config error. Need at least one open, close, or trouble output strings for switch %i.", swID); } } } ad2_printf_host(true, "%s: Init done. Found and configured %i virtual switches.", TAG, subscribers); - + xTaskCreate(&mqtt_startup_task, "mqtt startup", 1024*4, NULL, tskIDLE_PRIORITY+1, NULL); } #endif /* CONFIG_AD2IOT_MQTT_CLIENT */ diff --git a/components/alarmdecoder-api/alarmdecoder_api.cpp b/components/alarmdecoder-api/alarmdecoder_api.cpp index ea0ea613..4e86c04f 100644 --- a/components/alarmdecoder-api/alarmdecoder_api.cpp +++ b/components/alarmdecoder-api/alarmdecoder_api.cpp @@ -619,7 +619,7 @@ AD2PartitionState * AlarmDecoderParser::getAD2PState(uint32_t *amask, bool updat ad2ps->partition = AD2PStates.size(); ad2ps->primary_address = 0; #if defined(IDF_VER) - ESP_LOGI(TAG, "AD2PStates[%08x] not found adding partition ID(%i)", *amask, ad2ps->partition); + ESP_LOGI(TAG, "AD2PStates[%08lux] not found adding partition ID(%i)", *amask, ad2ps->partition); #endif } @@ -1249,7 +1249,7 @@ bool AlarmDecoderParser::put(uint8_t *buff, int8_t len) // Debugging / testing output #if defined(IDF_VER) - ESP_LOGD(TAG, "!DBG: SSIZE(%i) PID(%i) MASK(%08X) Ready(%i) Armed[Away(%i) Stay(%i)] Bypassed(%i) Exit(%i)", + ESP_LOGD(TAG, "!DBG: SSIZE(%i) PID(%i) MASK(%08lX) Ready(%i) Armed[Away(%i) Stay(%i)] Bypassed(%i) Exit(%i)", AD2PStates.size(),ad2ps->partition,amask,ad2ps->ready,ad2ps->armed_away,ad2ps->armed_stay,ad2ps->zone_bypassed,ad2ps->exit_now); #endif @@ -1289,7 +1289,7 @@ bool AlarmDecoderParser::put(uint8_t *buff, int8_t len) && !ad2ps->exit_now) { // not exit countdown // get the numeric section and use as a zone #. - // conver base 16 to base 10 if needed. + // convert base 16 to base 10 if needed. bool _ishex = std::count_if(ad2ps->last_numeric_message.begin(), ad2ps->last_numeric_message.end(), [](unsigned char c) { @@ -1319,9 +1319,11 @@ bool AlarmDecoderParser::put(uint8_t *buff, int8_t len) // standard zone fault report. // [00000011000000000A--],002,[f70600ef1002000018020000000000],"FAULT 02 " - // check zone(system_issue) set for zone fault report entry. + // alarm zone(alarm_event_occurred) report. Set for zone alarm report entry. + // [00110001111000010A--],011,[f70200ff101110802b020000000000],"ALARM 11 GARAGE DOOR " + // check zone(system_issue) set for zone trouble report entry. // [00000401000000100A--],009,[f700001f1009040208020000000000],"CHECK 09 " - if (ad2ps->system_issue) { + if (ad2ps->system_issue || ad2ps->alarm_event_occurred) { // Update the zone state object and set timeout if (ad2ps->zone_states[_zone].state() != AD2_STATE_TROUBLE) { _send_event = true; diff --git a/components/ftpd/ftpd.cpp b/components/ftpd/ftpd.cpp index b306cc20..80d628ab 100644 --- a/components/ftpd/ftpd.cpp +++ b/components/ftpd/ftpd.cpp @@ -33,7 +33,14 @@ static const char *TAG = "FTPD"; // module specific includes +/// Problem with dirent included as "C" already by STSDK build. +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif #include #define FTPD_COMMAND "ftpd" @@ -142,6 +149,8 @@ class FTPD void onXmkd(std::istringstream& ss); void onXrmd(std::istringstream& ss); void onRest(std::istringstream& ss); + void onUpgd(std::istringstream& ss); + void onVers(std::istringstream& ss); bool openData(); void receiveFile(std::string fileName); @@ -661,7 +670,7 @@ std::string FTPD::listenPassive() ESP_LOGI(TAG, "m_passiveSocket fd: %i", m_passiveSocket); #endif - unsigned int addrLen = sizeof(serverAddress); + socklen_t addrLen = sizeof(serverAddress); rc = getsockname(m_passiveSocket, (struct sockaddr*)&serverAddress, &addrLen); if (rc == -1) { ESP_LOGE(TAG, "m_passiveSocket getsockname() error %s", strerror(errno)); @@ -669,7 +678,7 @@ std::string FTPD::listenPassive() return "ERROR"; } - unsigned int addrInfoSize = sizeof(clientAddrInfo); + socklen_t addrInfoSize = sizeof(clientAddrInfo); getsockname(m_clientSocket, (struct sockaddr*)&clientAddrInfo, &addrInfoSize); // Convert client address to string for ACL testing. @@ -966,6 +975,28 @@ void FTPD::onRest(std::istringstream& ss) hal_restart(); } +/** + * @brief Process a UPGD operation. + * Upgrade and restart the AD2IoT + */ +void FTPD::onUpgd(std::istringstream& ss) +{ + sendResponse(RESPONSE_200_COMMAND_OK, "Starting ad2iot upgrade now. Reconnect in 1 minute."); // Command okay. + ad2_printf_host(true, "%s: 'UPGD' command received. Starting ad2iot upgrade now.", TAG); + closeConnection(); + hal_do_fwupdate(""); +} + + +/** + * @brief Process a VERS operation. + * Return the version of the AD2IoT firmware + */ +void FTPD::onVers(std::istringstream& ss) +{ + sendResponse(RESPONSE_200_COMMAND_OK, "AD2IoT installed version(" FIRMWARE_VERSION ") build flag (" FIRMWARE_BUILDFLAGS ").\r\n"); // Command okay. +} + /** * @brief Process a NOOP operation. */ @@ -1414,8 +1445,12 @@ void FTPD::processCommand() onRnto(ss); } else if (command.compare("REST")==0) { // Custom verb to restart AD2IoT onRest(ss); + } else if (command.compare("UPGD")==0) { // Custom verb to upgrade the AD2IoT + onUpgd(ss); + } else if (command.compare("VERS")==0) { // Custom verb to get the AD2IoT version string. + onVers(ss); } else { -#if defined(FTPD_DEBUG) + #if defined(FTPD_DEBUG) ESP_LOGI(TAG, "Unknown command verb: '%s'. Teach me more please!", command.c_str()); #endif sendResponse(FTPD::RESPONSE_500_COMMAND_UNRECOGNIZED); // Syntax error, command unrecognized. @@ -1774,9 +1809,17 @@ static void _cli_cmd_ftpd_event(const char *string) void ftp_daemon_task(void *pvParameters) { #if defined(FTPD_DEBUG) - ESP_LOGI(TAG, "ftp daemon task starting."); + ESP_LOGI(TAG, "%s waiting for network layer to start.", TAG); #endif + while (1) { + if (!hal_get_netif_started()) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } else { + break; + } + } if (ad2ftpd != nullptr) { + ESP_LOGI(TAG, "Network layer is OK. %s daemon service starting.", TAG); ad2ftpd->start(); } vTaskDelete(NULL); @@ -1819,12 +1862,6 @@ void ftpd_register_cmds() */ void ftpd_init() { - // if netif not enabled then we can't start. - if (!hal_get_netif_started()) { - ad2_printf_host(true, "%s daemon disabled. Network interface not enabled.", TAG); - return; - } - bool en = false; ad2_get_config_key_bool(FTPD_CONFIG_SECTION, FTPD_SUBCMD_ENABLE, &en); @@ -1850,7 +1887,7 @@ void ftpd_init() } } - ad2_printf_host(true, "%s: Init done. Daemon starting.", TAG); + ad2_printf_host(true, "%s: Init done.", TAG); xTaskCreate(&ftp_daemon_task, "ftp daemon", 1024*8, NULL, tskIDLE_PRIORITY+1, NULL); } diff --git a/components/otaupdate/CMakeLists.txt b/components/otaupdate/CMakeLists.txt index 0d6b7f00..8e5be99b 100644 --- a/components/otaupdate/CMakeLists.txt +++ b/components/otaupdate/CMakeLists.txt @@ -2,8 +2,9 @@ idf_component_register(SRCS "ota_util.cpp" REQUIRES idf::nvs_flash REQUIRES idf::alarmdecoder-api REQUIRES idf::app_update + REQUIRES idf::esp-tls REQUIRES idf::esp_https_ota REQUIRES idf::json INCLUDE_DIRS . ../../main/ EMBED_TXTFILES firmware_signature_public_key.pem ${EXTRA_EMBED}) -project(otaupdate) \ No newline at end of file +project(otaupdate) diff --git a/components/otaupdate/Kconfig b/components/otaupdate/Kconfig new file mode 100644 index 00000000..46e87463 --- /dev/null +++ b/components/otaupdate/Kconfig @@ -0,0 +1,7 @@ +menu "AD2iot * Firmware update support OTA." + config AD2IOT_OTAUPDATE + bool "OTA Firmware Updates" + default n + help + Enable OTA firmware updates. +endmenu \ No newline at end of file diff --git a/components/otaupdate/ota_util.cpp b/components/otaupdate/ota_util.cpp index cb252f58..6022c084 100644 --- a/components/otaupdate/ota_util.cpp +++ b/components/otaupdate/ota_util.cpp @@ -21,15 +21,19 @@ * limitations under the License. * */ -// FreeRTOS includes -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -static const char *TAG = "AD2OTA"; + static const char *TAG = "AD2OTA"; -// AlarmDecoder std includes + // AlarmDecoder std includes #include "alarmdecoder_main.h" + // Disable via config +#if CONFIG_AD2IOT_OTAUPDATE + + // FreeRTOS includes +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + // esp component includes #include #include @@ -52,10 +56,11 @@ static const char *TAG = "AD2OTA"; #define OTA_VERSION_INFO_BUF_SIZE 1024 -#define OTA_UPGRADE_CMD "upgrade" -#define OTA_VERSION_CMD "version" +#define OTA_UPGRADE_CMD "upgradeota" +#define OTA_VERSION_CMD "versionota" #define OTA_FIRST_CHECK_DELAY_MS 30*1000 +#define OTA_RETRY_CHECK_DELAY_MS 300*1000 #define OTA_SOCKET_TIMEOUT 10*1000 // forward decl @@ -289,15 +294,9 @@ static int _crypto_sha256(const unsigned char *src, size_t src_len, unsigned cha #if defined(DEBUG_OTA) ESP_LOGI(TAG, "%s: src: %d@%p, dst: %p", __func__, src_len, src, dst); #endif - ret = mbedtls_sha256_ret(src, src_len, dst, 0); - if (ret) { -#if defined(DEBUG_OTA) - ESP_LOGI(TAG, "%s: mbedtls_sha256_ret = -0x%04X", __func__, -ret); -#endif - return ret; - } + mbedtls_sha256(src, src_len, dst, 0); - return 0; + return 1; } /** @@ -507,11 +506,7 @@ esp_err_t ota_https_update_device(const char *buildflags) mbedtls_sha256_init( &ctx ); b_ctx_init = true; - if (mbedtls_sha256_starts_ret( &ctx, 0) != 0 ) { - ESP_LOGE(TAG, "%s: Failed to initialise api", __func__); - ret = ESP_FAIL; - goto clean_up; - } + mbedtls_sha256_starts( &ctx, 0); while (1) { int data_read = esp_http_client_read(client, upgrade_data_buf, OTA_DEFAULT_BUF_SIZE); @@ -546,9 +541,7 @@ esp_err_t ota_https_update_device(const char *buildflags) } if (data_read > 0) { - if (mbedtls_sha256_update_ret(&ctx, (const unsigned char *)upgrade_data_buf, data_read) != 0) { - ESP_LOGE(TAG, "%s: Failed getting HASH", __func__); - } + mbedtls_sha256_update(&ctx, (const unsigned char *)upgrade_data_buf, data_read); ota_write_err = esp_ota_write( update_handle, (const void *)upgrade_data_buf, data_read); if (ota_write_err != ESP_OK) { @@ -561,11 +554,7 @@ esp_err_t ota_https_update_device(const char *buildflags) #if defined(DEBUG_OTA) ESP_LOGI(TAG, "%s: Total binary data length writen: %d", __func__, total_read_len); #endif - if (mbedtls_sha256_finish_ret( &ctx, md) != 0) { - ESP_LOGE(TAG, "%s: Failed getting HASH", __func__); - ret = ESP_FAIL; - goto clean_up; - } + mbedtls_sha256_finish( &ctx, md); /* Check firmware validation */ if (_check_firmware_validation((const unsigned char *)md, sig, sig_len) != true) { @@ -719,7 +708,14 @@ esp_err_t ota_https_read_version_info(char **version_info, unsigned int *version */ static void ota_polling_task_func(void *arg) { + uint32_t delay = OTA_FIRST_CHECK_DELAY_MS; + static int fail_count = 0; while (1) { + // failed too many times skip for 24h + if (fail_count > 2) { + fail_count--; + delay = 24 * 3600; + } #if defined(AD2_STACK_REPORT) #define EXTRA_INFO_EVERY 1 static int extra_info = EXTRA_INFO_EVERY; @@ -729,7 +725,7 @@ static void ota_polling_task_func(void *arg) } #endif - vTaskDelay(OTA_FIRST_CHECK_DELAY_MS / portTICK_PERIOD_MS); + vTaskDelay(delay / portTICK_PERIOD_MS); ESP_LOGI(TAG, "Starting check new version with current version '%s'-%s", FIRMWARE_VERSION, FIRMWARE_BUILDFLAGS); @@ -738,8 +734,10 @@ static void ota_polling_task_func(void *arg) continue; } - if (!hal_get_network_connected()) { - ESP_LOGI(TAG, "Device update check aborted. No internet connection."); + if (!hal_get_netif_started()) { + ESP_LOGI(TAG, "Device update check aborted. Network interface not started."); + delay = OTA_RETRY_CHECK_DELAY_MS; + fail_count++; continue; } @@ -759,9 +757,13 @@ static void ota_polling_task_func(void *arg) if (available_version) { free(available_version); } + fail_count++; continue; } + // reset fail count upon success + fail_count = 0; + // Update and notify subscribers of a new version if (available_version) { ota_available_version = available_version; @@ -775,7 +777,7 @@ static void ota_polling_task_func(void *arg) } } - /* Set polling period */ + /* Get polling period in days from server response and set it */ unsigned int polling_day = ota_get_polling_period_day(); unsigned int task_delay_sec = polling_day * 24 * 3600; vTaskDelay(task_delay_sec * 1000 / portTICK_PERIOD_MS); @@ -818,15 +820,21 @@ static struct cli_command ota_cmd_list[] = { }; /** - * @brief Start the OTA check task. + * @brief Register component cli commands. */ -void ota_init() +void ota_register_cmds() { - // Register twilio CLI commands + // Register CLI commands for (int i = 0; i < ARRAY_SIZE(ota_cmd_list); i++) { cli_register_command(&ota_cmd_list[i]); } +} +/** + * @brief Start the OTA check task. + */ +void ota_init() +{ xTaskCreate(ota_polling_task_func, "AD2 ota check", 1024*4, NULL, tskIDLE_PRIORITY, NULL); } @@ -837,3 +845,4 @@ void ota_do_version(const char *arg) { ad2_printf_host(false, "Installed version(" FIRMWARE_VERSION ") build flag (" FIRMWARE_BUILDFLAGS ") available version(%s).\r\n", ota_available_version.c_str()); } +#endif /* CONFIG_AD2IOT_OTAUPDATE */ diff --git a/components/otaupdate/ota_util.h b/components/otaupdate/ota_util.h index 2c24ac74..11fe7408 100644 --- a/components/otaupdate/ota_util.h +++ b/components/otaupdate/ota_util.h @@ -23,9 +23,10 @@ #ifndef _OTA_UTIL_H #define _OTA_UTIL_H - +#if CONFIG_AD2IOT_OTAUPDATE void ota_do_update(const char *arg); +void ota_register_cmds(); void ota_init(); - +#endif #endif /* _OTA_UTIL_H */ diff --git a/components/pushover/pushover.cpp b/components/pushover/pushover.cpp index f7c41460..f0d59977 100644 --- a/components/pushover/pushover.cpp +++ b/components/pushover/pushover.cpp @@ -98,7 +98,6 @@ class request_message */ static void _sendQ_ready_handler(esp_http_client_handle_t client, esp_http_client_config_t *config) { - esp_err_t err; // if perform failed this can be NULL if (client) { request_message *r = (request_message*) config->user_data; @@ -109,7 +108,7 @@ static void _sendQ_ready_handler(esp_http_client_handle_t client, esp_http_clien "&message=" + ad2_urlencode(r->message)); // does not copy data just a pointer so we have to maintain memory. - err = esp_http_client_set_post_field(client, r->post.c_str(), r->post.length()); + esp_http_client_set_post_field(client, r->post.c_str(), r->post.length()); } } @@ -182,6 +181,11 @@ esp_err_t _pushover_http_event_handler(esp_http_client_event_t *evt) case HTTP_EVENT_DISCONNECTED: #if defined(DEBUG_PUSHOVER) ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED"); +#endif + break; + case HTTP_EVENT_REDIRECT: +#if defined(DEBUG_PUSHOVER) + ESP_LOGI(TAG, "HTTP_EVENT_REDIRECT"); #endif break; case HTTP_EVENT_ON_DATA: @@ -247,7 +251,9 @@ void on_search_match_cb_pushover(std::string *msg, AD2PartitionState *s, void *a // Add client config to the http_sendQ for processing. bool res = ad2_add_http_sendQ(r->config_client, _sendQ_ready_handler, _sendQ_done_handler); - if (!res) { + if (res) { + ESP_LOGI(TAG,"Switch #%i match message '%s'. Sending '%s' to acid #%i", es->INT_ARG, msg->c_str(), es->out_message.c_str(), notify_slot); + } else { ESP_LOGE(TAG,"Error adding HTTP request to ad2_add_http_sendQ."); // destroy storage class if we fail to add to the sendQ delete r; @@ -293,11 +299,11 @@ static void _cli_cmd_pushover_event_generic(std::string &subcmd, const char *str // if (ad2_copy_nth_arg(buf, string, 3) >= 0) { ad2_set_config_key_string(PUSHOVER_CONFIG_SECTION, subcmd.c_str(), buf.c_str(), accountId); - ad2_printf_host(false, "Setting #%02i '%s' value '%s' finished.\r\n", accountId, subcmd.c_str(), buf.c_str()); + ad2_printf_host(false, "Setting #%i '%s' value '%s' finished.\r\n", accountId, subcmd.c_str(), buf.c_str()); } else { buf = ""; ad2_get_config_key_string(PUSHOVER_CONFIG_SECTION, subcmd.c_str(), buf, accountId); - ad2_printf_host(false, "Current #%02i '%s' value '%s'\r\n", accountId, subcmd.c_str(), buf.length() ? buf.c_str() : "EMPTY"); + ad2_printf_host(false, "Current #%i '%s' value '%s'\r\n", accountId, subcmd.c_str(), buf.length() ? buf.c_str() : "EMPTY"); } } else { ad2_printf_host(false, "Missing or invalid [1-8].\r\n"); @@ -360,7 +366,6 @@ static void _cli_cmd_pushover_smart_alert_switch(std::string &subcmd, const char PUSHOVER_CONFIG_SWITCH_SUFFIX_TROUBLE); std::string sk; - bool command_found = false; ad2_printf_host(false, "## [pushover] switch %i configuration.\r\n", swID); while (ss >> sk) { tbuf = ""; @@ -555,6 +560,7 @@ void pushover_init() // load [switch N] required regex match settings std::string prefilter_regex; ad2_get_config_key_string(key.c_str(), AD2SWITCH_SK_FILTER, prefilter_regex); + es1->PRE_FILTER_REGEX = prefilter_regex; // Load all regex search patterns for open, close, and trouble sub keys. std::string regex_sk_list = AD2SWITCH_SK_OPEN " " @@ -601,12 +607,12 @@ void pushover_init() // incomplete switch so delete it and supporting pointers. delete pslots; delete es1; - ESP_LOGE(TAG, "Error in config section [switch %i]. Missing required open, close, or fault filter expressions.", swID); + ESP_LOGE(TAG, "Error in config section [switch %i]. Need at least one open, close, or trouble filter expressions.", swID); } } else { if (open_output_format.length() || close_output_format.length() || trouble_output_format.length()) { - ESP_LOGE(TAG, "Error in config for switch [switch %i]. Missing on or more required open,close, or fault output expressions.", swID); + ESP_LOGE(TAG, "Section config error. Need at least one open, close, or trouble output strings for switch %i.", swID); } } } diff --git a/components/ser2sock/ser2sock.cpp b/components/ser2sock/ser2sock.cpp index 38dde8d0..383b1bde 100644 --- a/components/ser2sock/ser2sock.cpp +++ b/components/ser2sock/ser2sock.cpp @@ -257,10 +257,9 @@ void ser2sockd_init(void) return; } - ad2_printf_host(true, "%s: Init done, daemon starting.", TAG); - // ser2sockd worker thread // 20210815SM: 1284 bytes stack free after first connection. + ad2_printf_host(true, "%s: Init done, daemon starting.", TAG); xTaskCreate(&ser2sockd_server_task, "AD2 ser2sockd", 1024*4, NULL, tskIDLE_PRIORITY+1, NULL); } @@ -553,7 +552,7 @@ static bool _poll_read_fdset(fd_set *read_fdset) /* clear our state vars */ newsockfd = -1; { - unsigned int addr_len; + socklen_t addr_len; struct sockaddr_storage peer_addr = {}; addr_len = sizeof(peer_addr); newsockfd = accept(listen_sock, (struct sockaddr *) &peer_addr, &addr_len); @@ -698,12 +697,25 @@ void ser2sockd_server_task(void *pvParameters) struct timeval wait; #if defined(S2SD_DEBUG) - ESP_LOGI(TAG, "ser2sock server task starting."); + ESP_LOGI(TAG, "%s waiting for network layer to start.", TAG); +#endif + while (1) { + if (!hal_get_netif_started()) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } else { + break; + } + } + ESP_LOGI(TAG, "Network layer is OK. %s client starting.", TAG); + + +#if defined(S2SD_DEBUG) + ESP_LOGI(TAG, "%s waiting for network IP layer to start.", TAG); #endif for (;;) { if (hal_get_network_connected()) { #if defined(S2SD_DEBUG) - ESP_LOGI(TAG, "network up creating listening socket"); + ESP_LOGI(TAG, "Network IP layer is OK. %s daemon service starting.", TAG); #endif #if CONFIG_LWIP_IPV6 // IPv6 socket will listen on both IPv4 and IPv6 at the same time. diff --git a/components/stsdk/CMakeLists.txt b/components/stsdk/CMakeLists.txt index cc2d5730..0eee8515 100644 --- a/components/stsdk/CMakeLists.txt +++ b/components/stsdk/CMakeLists.txt @@ -12,11 +12,10 @@ idf_component_register(SRCS "stsdk_main.cpp" "caps_smokeDetector.c" "caps_switch.c" "caps_tamperAlert.c" - EMBED_FILES "device_info.json" - "onboarding_config.json" + EMBED_FILES "onboarding_config.json" REQUIRES idf::otaupdate INCLUDE_DIRS . ../../main/ $ENV{STDK_CORE_PATH}/src/include/) project(stsdk) else() message(AUTHOR_WARNING "**** Not currently possible to build 'stsdk' component outside of the STSDK build.py tool.") -endif() \ No newline at end of file +endif() diff --git a/components/stsdk/caps_battery.h b/components/stsdk/caps_battery.h index 36f3fd2b..d252e6b7 100644 --- a/components/stsdk/caps_battery.h +++ b/components/stsdk/caps_battery.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_battery.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_battery_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -36,4 +40,6 @@ typedef struct caps_battery_data { } caps_battery_data_t; caps_battery_data_t *caps_battery_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); - +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_button.h b/components/stsdk/caps_button.h index bbae7062..6b345220 100644 --- a/components/stsdk/caps_button.h +++ b/components/stsdk/caps_button.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_button.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_button_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -43,3 +47,6 @@ typedef struct caps_button_data { } caps_button_data_t; caps_button_data_t *caps_button_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_carbonMonoxideDetector.h b/components/stsdk/caps_carbonMonoxideDetector.h index 03ef2b54..9914a107 100644 --- a/components/stsdk/caps_carbonMonoxideDetector.h +++ b/components/stsdk/caps_carbonMonoxideDetector.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_carbonMonoxideDetector.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_carbonMonoxideDetector_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -34,3 +38,6 @@ typedef struct caps_carbonMonoxideDetector_data { } caps_carbonMonoxideDetector_data_t; caps_carbonMonoxideDetector_data_t *caps_carbonMonoxideDetector_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_contactSensor.h b/components/stsdk/caps_contactSensor.h index 51dcd919..877bf8fb 100644 --- a/components/stsdk/caps_contactSensor.h +++ b/components/stsdk/caps_contactSensor.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_contactSensor.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_contactSensor_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -34,3 +38,6 @@ typedef struct caps_contactSensor_data { } caps_contactSensor_data_t; caps_contactSensor_data_t *caps_contactSensor_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_momentary.h b/components/stsdk/caps_momentary.h index bc553ea4..5606daa1 100644 --- a/components/stsdk/caps_momentary.h +++ b/components/stsdk/caps_momentary.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_momentary.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_momentary_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -31,3 +35,6 @@ typedef struct caps_momentary_data { } caps_momentary_data_t; caps_momentary_data_t *caps_momentary_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_powerSource.h b/components/stsdk/caps_powerSource.h index 4011aabe..8d283a5b 100644 --- a/components/stsdk/caps_powerSource.h +++ b/components/stsdk/caps_powerSource.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_powerSource.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_powerSource_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -34,3 +38,6 @@ typedef struct caps_powerSource_data { } caps_powerSource_data_t; caps_powerSource_data_t *caps_powerSource_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_refresh.h b/components/stsdk/caps_refresh.h index 08025cd3..0a3eca3c 100644 --- a/components/stsdk/caps_refresh.h +++ b/components/stsdk/caps_refresh.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_refresh.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_refresh_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -31,3 +35,6 @@ typedef struct caps_refresh_data { } caps_refresh_data_t; caps_refresh_data_t *caps_refresh_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_securitySystem.h b/components/stsdk/caps_securitySystem.h index b99c6784..a9d2697c 100644 --- a/components/stsdk/caps_securitySystem.h +++ b/components/stsdk/caps_securitySystem.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_securitySystem.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_securitySystem_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -42,3 +46,6 @@ typedef struct caps_securitySystem_data { } caps_securitySystem_data_t; caps_securitySystem_data_t *caps_securitySystem_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_smokeDetector.h b/components/stsdk/caps_smokeDetector.h index 83b130e6..5b141004 100644 --- a/components/stsdk/caps_smokeDetector.h +++ b/components/stsdk/caps_smokeDetector.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_smokeDetector.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_smokeDetector_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -34,3 +38,6 @@ typedef struct caps_smokeDetector_data { } caps_smokeDetector_data_t; caps_smokeDetector_data_t *caps_smokeDetector_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/caps_switch.h b/components/stsdk/caps_switch.h index 6454c5d2..19b71b19 100644 --- a/components/stsdk/caps_switch.h +++ b/components/stsdk/caps_switch.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_switch.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_switch_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -37,3 +41,6 @@ typedef struct caps_switch_data { } caps_switch_data_t; caps_switch_data_t *caps_switch_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/components/stsdk/caps_tamperAlert.h b/components/stsdk/caps_tamperAlert.h index 92341fa3..daddf00a 100644 --- a/components/stsdk/caps_tamperAlert.h +++ b/components/stsdk/caps_tamperAlert.h @@ -18,6 +18,10 @@ #include "caps/iot_caps_helper_tamperAlert.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct caps_tamperAlert_data { IOT_CAP_HANDLE* handle; void *usr_data; @@ -34,3 +38,6 @@ typedef struct caps_tamperAlert_data { } caps_tamperAlert_data_t; caps_tamperAlert_data_t *caps_tamperAlert_initialize(IOT_CTX *ctx, const char *component, void *init_usr_cb, void *usr_data); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/components/stsdk/device_info.json b/components/stsdk/device_info.json deleted file mode 100644 index 5d9b000c..00000000 --- a/components/stsdk/device_info.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "deviceInfo": { - "firmwareVersion": "firmwareVersion_here", - "privateKey": "privateKey_here", - "publicKey": "publicKey_here", - "serialNumber": "serialNumber_here" - } -} diff --git a/components/stsdk/stsdk_main.cpp b/components/stsdk/stsdk_main.cpp index 616a5898..a9e3abf4 100644 --- a/components/stsdk/stsdk_main.cpp +++ b/components/stsdk/stsdk_main.cpp @@ -35,15 +35,18 @@ static const char *TAG = "STSDK"; // esp component includes #include "driver/uart.h" #include "nvs_flash.h" -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) -#include "tcpip_adapter.h" -#else #include "esp_netif.h" -#endif + +static bool _enabled = false; // specific includes #include "stsdk_main.h" +#if CONFIG_AD2IOT_OTAUPDATE #include "ota_util.h" +#endif +#ifdef __cplusplus +extern "C" { +#endif // @brief global ST status tracking iot_status_t g_iot_status = IOT_STATUS_IDLE; @@ -58,10 +61,6 @@ IOT_CTX* ctx = NULL; extern const uint8_t onboarding_config_start[] asm("_binary_onboarding_config_json_start"); extern const uint8_t onboarding_config_end[] asm("_binary_onboarding_config_json_end"); -// device_info_start is null-terminated string -extern const uint8_t device_info_start[] asm("_binary_device_info_json_start"); -extern const uint8_t device_info_end[] asm("_binary_device_info_json_end"); - /** * @brief component: main */ @@ -82,6 +81,22 @@ caps_powerSource_data_t *cap_powerSource_data; // @brief Battery [% int 0 - 100] caps_battery_data_t *cap_battery_data; +char * STSDK_SUBCMD [] = { + (char*)STSDK_SUBCMD_ENABLE, + (char*)STSDK_SUBCMD_CLEANUP, + (char*)STSDK_SUBCMD_SERIAL, + (char*)STSDK_SUBCMD_PUBKEY, + (char*)STSDK_SUBCMD_PRIVKEY, + 0 // EOF +}; + +enum { + STSDK_SUBCMD_ENABLE_ID = 0, + STSDK_SUBCMD_CLEANUP_ID, + STSDK_SUBCMD_SERIAL_ID, + STSDK_SUBCMD_PUBKEY_ID, + STSDK_SUBCMD_PRIVKEY_ID +}; /** * @brief component: chime @@ -184,175 +199,131 @@ caps_momentary_data_t *cap_momentary_data_disarm; /** - * Enable/Disable STSDK component. + * @brief SmartThings generic command event processing + * command: [COMMAND] + * ex. + * [COMMAND] 0 arg... */ -static void _cli_cmd_enable_event(char *string) +static void _cli_cmd_stsdk_event(const char *string) { - ESP_LOGI(TAG, "%s: enable/disable STSDK", __func__); - std::string arg; - if (ad2_copy_nth_arg(arg, string, 1) >= 0) { - ad2_set_config_key_bool(STSDK_ENABLE, nullptr, (arg[0] == 'Y' || arg[0] == 'y')); - ad2_printf_host(false, "Success setting value. Restart required to take effect.\r\n"); - } - - // show contents of this slot - bool en = false; - ad2_get_config_key_bool(STSDK_ENABLE, nullptr, &en); - ad2_printf_host(false, "SmartThings SDK driver is '%s'.\r\n", (en ? "Enabled" : "Disabled")); -} - -/** - * Forget all STSDK state revert back to adoption mode. - */ -static void _cli_cmd_cleanup_event(char *string) -{ - ESP_LOGI(TAG, "%s: clean-up data with restart option", __func__); - ad2_printf_host(false, "Calling clean-up for STSDK settings.\r\n"); - st_conn_cleanup(ctx, true); -} + // key value validation + std::string cmd; + ad2_copy_nth_arg(cmd, string, 0); + ad2_lcase(cmd); -/** - * Configure the AD2IoT SmartThings device_info.json - * field stored in NV - * - * command: stserial - * ex. - * stserial AaBbCcDdEeFf... - */ -static void _cli_cmd_stserial_event(char *string) -{ - std::string arg; - if (ad2_copy_nth_arg(arg, string, 1) >= 0) { - nvs_handle my_handle; - esp_err_t err; - err = nvs_open_from_partition("stnv", "stdk", NVS_READWRITE, &my_handle); - if ( err != ESP_OK) { - ESP_LOGE(TAG, "%s: nvs_open_from_partition err %d", __func__, err); - } - err = nvs_set_str(my_handle, "SerialNum", arg.c_str()); - if ( err != ESP_OK) { - ESP_LOGE(TAG, "%s: nvs_set_str err %d", __func__, err); - ad2_printf_host(false, "Failed setting value.\r\n"); - } else { - ad2_printf_host(false, "Success setting value. Restart required to take effect.\r\n"); - } - err = nvs_commit(my_handle); - nvs_close(my_handle); - } else { - ad2_printf_host(false, "Missing \r\n"); + if(cmd.compare(STSDK_COMMAND) != 0) { + ad2_printf_host(false, "What?\r\n"); + return;; } -} -/** - * Configure the AD2IoT SmartThings device_info.json - * field stored in NV - * - * command: stpublickey - * ex. - * stpublickey AaBbCcDdEeFf... - */ -static void _cli_cmd_stpublickey_event(char *string) -{ - std::string arg; - if (ad2_copy_nth_arg(arg, string, 1) >= 0) { - nvs_handle my_handle; - esp_err_t err; - err = nvs_open_from_partition("stnv", "stdk", NVS_READWRITE, &my_handle); - if ( err != ESP_OK) { - ESP_LOGE(TAG, "%s: nvs_open_from_partition err %d", __func__, err); - } - err = nvs_set_str(my_handle, "PublicKey", arg.c_str()); - if ( err != ESP_OK) { - ESP_LOGE(TAG, "%s: nvs_set_str err %d", __func__, err); - ad2_printf_host(false, "Failed setting value.\r\n"); - } else { - ad2_printf_host(false, "Success setting value. Restart required to take effect.\r\n"); - } - err = nvs_commit(my_handle); - nvs_close(my_handle); - } else { - ad2_printf_host(false, "Missing \r\n"); - } -} + // key value validation + std::string subcmd; + ad2_copy_nth_arg(subcmd, string, 1); + ad2_lcase(subcmd); -/** - * Configure the AD2IoT SmartThings device_info.json - * field stored in NV - * - * command: stprivatekey - * ex. - * stprivatekey AaBbCcDdEeFf... - */ -static void _cli_cmd_stprivatekey_event(char *string) -{ - std::string arg; - if (ad2_copy_nth_arg(arg, string, 1) >= 0) { - nvs_handle my_handle; - esp_err_t err; - err = nvs_open_from_partition("stnv", "stdk", NVS_READWRITE, &my_handle); - if ( err != ESP_OK) { - ESP_LOGE(TAG, "%s: nvs_open_from_partition err %d", __func__, err); + int i; + for(i = 0;; ++i) { + if (STSDK_SUBCMD[i] == 0) { + ad2_printf_host(false, "What?\r\n"); + break; } - err = nvs_set_str(my_handle, "PrivateKey", arg.c_str()); - if ( err != ESP_OK) { - ESP_LOGE(TAG, "%s: nvs_set_str err %d", __func__, err); - ad2_printf_host(false, "Failed setting value.\r\n"); - } else { - ad2_printf_host(false, "Success setting value. Restart required to take effect.\r\n"); + if(subcmd.compare(STSDK_SUBCMD[i]) == 0) { + std::string arg; + std::string key; + switch(i) { + /** + * Enable/Disable STSDK client. + */ + case STSDK_SUBCMD_ENABLE_ID: + if (ad2_copy_nth_arg(arg, string, 2) >= 0) { + ad2_set_config_key_bool(STSDK_CONFIG_SECTION, STSDK_SUBCMD_ENABLE, (arg[0] == 'Y' || arg[0] == 'y')); + ad2_printf_host(true, "Success setting value. Restart required to take effect.\r\n"); + } + + // load the current and show contents of this slot. + ad2_get_config_key_bool(STSDK_CONFIG_SECTION, STSDK_SUBCMD_ENABLE, &_enabled); + ad2_printf_host(true, "SmartThings Direct Connected Devices SDK client is '%s'.\r\n", (_enabled ? "Enabled" : "Disabled")); + break; + /** + * Forget all STSDK state revert back to adoption mode. + */ + case STSDK_SUBCMD_CLEANUP_ID: + ESP_LOGI(TAG, "%s: clean-up data with restart option", __func__); + ad2_printf_host(true, "Calling clean-up for STSDK settings.\r\n"); + st_conn_cleanup(ctx, true); + break; + /** + * SmartThings Serial. + */ + case STSDK_SUBCMD_SERIAL_ID: + // arg found save + if (ad2_copy_nth_arg(arg, string, 2, true) >= 0) { + ad2_set_config_key_string(STSDK_CONFIG_SECTION, STSDK_SUBCMD_SERIAL, arg.c_str()); + ad2_printf_host(true, "Success setting value. Restart required to take effect.\r\n"); + break; + } + // If no arg then return current show contents of this slot + ad2_printf_host(true, "SmartThings '" STSDK_SUBCMD_SERIAL "' arg missing.\r\n"); + break; + /** + * SmartThings PrivateKey. + */ + case STSDK_SUBCMD_PRIVKEY_ID: + // arg found save + if (ad2_copy_nth_arg(arg, string, 2, true) >= 0) { + ad2_set_config_key_string(STSDK_CONFIG_SECTION, STSDK_SUBCMD_PRIVKEY, arg.c_str()); + ad2_printf_host(true, "Success setting value. Restart required to take effect.\r\n"); + break; + } + // If no arg then return current show contents of this slot + ad2_printf_host(true, "SmartThings '" STSDK_SUBCMD_PRIVKEY "' arg missing.\r\n"); + break; + /** + * SmartThings PublicKey. + */ + case STSDK_SUBCMD_PUBKEY_ID: + // arg found save + if (ad2_copy_nth_arg(arg, string, 2, true) >= 0) { + ad2_set_config_key_string(STSDK_CONFIG_SECTION, STSDK_SUBCMD_PUBKEY, arg.c_str()); + ad2_printf_host(true, "Success setting value. Restart required to take effect.\r\n"); + break; + } + // If no arg then return current show contents of this slot + ad2_printf_host(true, "SmartThings '" STSDK_SUBCMD_PUBKEY "' arg missing.\r\n"); + break; + + default: + break; + } + break; } - err = nvs_commit(my_handle); - nvs_close(my_handle); - } else { - ad2_printf_host(false, "Missing \r\n"); } } -char * STSDK_SETTINGS [] = { - (char*)STSDK_ENABLE, - (char*)STSDK_CLEANUP, - (char*)STSDK_SERIAL, - (char*)STSDK_PUBKEY, - (char*)STSDK_PRIVKEY, - 0 // EOF -}; - /** * @brief command list for module */ static struct cli_command stsdk_cmd_list[] = { { - (char*)STSDK_ENABLE,(char*) - "Usage: stenable [arg]\r\n" + (char*)STSDK_COMMAND,(char*) + "Usage: smartthings [arg]\r\n" "\r\n" - " Configuration tool for STSDK client\r\n" - "Options:\r\n" - " arg [Y | N] Enable or Disable STSDK client\r\n" - , _cli_cmd_enable_event - }, - { - (char*)STSDK_CLEANUP,(char*) - "- Cleanup NV data with restart option\r\n" - " - ```" STSDK_CLEANUP "```\r\n\r\n", _cli_cmd_cleanup_event - }, - { - (char*)STSDK_SERIAL,(char*) - "- Sets the SmartThings device_info serialNumber.\r\n" - " - ```" STSDK_SERIAL " {serialNumber}```\r\n" - " - Example: ```" STSDK_SERIAL " AaBbCcDdEeFfGg...```\r\n\r\n", _cli_cmd_stserial_event - }, - { - (char*)STSDK_PUBKEY,(char*) - "- Sets the SmartThings device_info publicKey.\r\n" - " - ```" STSDK_PUBKEY " {publicKey}```\r\n" - " - Example: ```" STSDK_PUBKEY " AaBbCcDdEeFfGg...```\r\n\r\n", _cli_cmd_stpublickey_event - }, - { - (char*)STSDK_PRIVKEY,(char*) - "- Sets the SmartThings device_info privateKey.\r\n" - " - ```" STSDK_PRIVKEY " {privateKey}```\r\n" - " - Example: ```" STSDK_PRIVKEY " AaBbCcDdEeFfGg...```\r\n\r\n", _cli_cmd_stprivatekey_event - }, + " Configuration tool for SmartThings Direct Connected Devices SDK\r\n" + " NOTE: Only the enable setting is kept in the ad2iot.ini config.\r\n" + " The remaining settings are kept in the ```stnv``` partition and\r\n" + " managed by the SmartThings Direct Device SDK.\r\n" + "Commands:\r\n" + " enable [Y|N] Set or get enable flag\r\n" + " cleanup Reset the NVS partition\r\n" + " serial Set the device serial\r\n" + " publickey Set the device public key\r\n" + " privatekey Set the device private key\r\n" + "Examples:\r\n" + " ```smartthings enable Y```\r\n" + " ```smartthings publickey aabbccddeeffAABBCCDEEFF```\r\n" + , _cli_cmd_stsdk_event + } }; #if 0 // TODO/FIXME @@ -996,23 +967,46 @@ void stsdk_init(void) { unsigned char *onboarding_config = (unsigned char *) onboarding_config_start; unsigned int onboarding_config_len = onboarding_config_end - onboarding_config_start; - unsigned char *device_info = (unsigned char *) device_info_start; - unsigned int device_info_len = device_info_end - device_info_start; int iot_err; - bool en = false; - ad2_get_config_key_bool(STSDK_ENABLE, 0, nullptr, &en); + ad2_get_config_key_bool(STSDK_CONFIG_SECTION, STSDK_SUBCMD_ENABLE, &_enabled); // nothing more needs to be done once commands are set if not enabled. - if (!en) { + if (!_enabled) { ESP_LOGI(TAG, "STSDK disabled"); return; } ESP_LOGI(TAG, "Starting STSDK"); - // create a iot context - ctx = st_conn_init(onboarding_config, onboarding_config_len, device_info, device_info_len); + // Create a device info json string for the st_conn_init from internal NV keys + cJSON *root = cJSON_CreateObject(); + cJSON *deviceInfo = cJSON_CreateObject(); + + std::string privateKey; + std::string publicKey; + std::string serialNumber; + ad2_get_config_key_string(STSDK_CONFIG_SECTION, STSDK_SUBCMD_PRIVKEY, privateKey); + ad2_get_config_key_string(STSDK_CONFIG_SECTION, STSDK_SUBCMD_PUBKEY, publicKey); + ad2_get_config_key_string(STSDK_CONFIG_SECTION, STSDK_SUBCMD_SERIAL, serialNumber); + + cJSON_AddStringToObject(deviceInfo, "firmwareVersion", FIRMWARE_VERSION); + cJSON_AddStringToObject(deviceInfo, "privateKey", privateKey.c_str()); + cJSON_AddStringToObject(deviceInfo, "publicKey", publicKey.c_str()); + cJSON_AddStringToObject(deviceInfo, "serialNumber", serialNumber.c_str()); + cJSON_AddItemToObject(root, "deviceInfo", deviceInfo); + static char *device_info_json = NULL; + device_info_json = cJSON_Print(root); + ctx = st_conn_init(onboarding_config, onboarding_config_len, (unsigned char*)device_info_json, strlen(device_info_json)); + + // if ctx is good then the network layer should be up. + if (ctx != NULL) { + hal_set_netif_started(true); + } + + //cJSON_free(device_info_json); + cJSON_Delete(root); + if (ctx != NULL) { iot_err = st_conn_set_noti_cb(ctx, iot_noti_cb, NULL); if (iot_err) { @@ -1330,6 +1324,8 @@ void stsdk_connection_start(void) iot_pin_t *pin_num = NULL; int err; + if (!_enabled) return; + #if defined(SET_PIN_NUMBER_CONFRIM) pin_num = (iot_pin_t *) malloc(sizeof(iot_pin_t)); if (!pin_num) { @@ -1465,6 +1461,7 @@ void update_firmware_cmd_cb(IOT_CAP_HANDLE *handle, { hal_ota_do_update(nullptr); } - +#ifdef __cplusplus +} // extern "C" +#endif #endif /* CONFIG_STDK_IOT_CORE */ - diff --git a/components/stsdk/stsdk_main.h b/components/stsdk/stsdk_main.h index 8655ca02..e9721b09 100644 --- a/components/stsdk/stsdk_main.h +++ b/components/stsdk/stsdk_main.h @@ -41,14 +41,20 @@ #include "caps_powerSource.h" #include "caps_battery.h" -#define STSDK_ENABLE "stenable" -#define STSDK_CLEANUP "stcleanup" -#define STSDK_SERIAL "stserial" -#define STSDK_PUBKEY "stpublickey" -#define STSDK_PRIVKEY "stprivatekey" +#define STSDK_COMMAND "smartthings" +#define STSDK_SUBCMD_ENABLE "enable" +#define STSDK_SUBCMD_CLEANUP "cleanup" +#define STSDK_SUBCMD_SERIAL "serial" +#define STSDK_SUBCMD_PUBKEY "publickey" +#define STSDK_SUBCMD_PRIVKEY "privatekey" + +#define STSDK_CONFIG_SECTION "smartthings" -//#define SET_PIN_NUMBER_CONFRIM +//#define SET_PIN_NUMBER_CONFRIM +#ifdef __cplusplus +extern "C" { +#endif void stsdk_register_cmds(void); void stsdk_init(void); void button_event(IOT_CAP_HANDLE *handle, int type, int count); @@ -102,6 +108,7 @@ extern IOT_CAP_HANDLE *healthCheck_cap_handle; extern IOT_CAP_HANDLE *refresh_cap_handle; extern int noti_led_mode; - +#ifdef __cplusplus +} // extern "C" +#endif #endif /* _STSDK_MAIN_H */ - diff --git a/components/twilio/twilio.cpp b/components/twilio/twilio.cpp index 65019f5f..fd34484a 100644 --- a/components/twilio/twilio.cpp +++ b/components/twilio/twilio.cpp @@ -183,7 +183,7 @@ static void _build_twilio_call_post(esp_http_client_handle_t client, tw_request_ std::string toString; ad2_get_config_key_string(TWILIO_CONFIG_SECTION, TWILIO_TO_SUBCMD, toString, r->notify_slot); - std::string twiml = fmt::format(formatString, r->message); + std::string twiml = fmt::format(fmt::runtime(formatString), r->message); #if defined(DEBUG_TWILIO) ESP_LOGI(TAG, "Sending Twiml message: %s", twiml.c_str()); #endif @@ -333,38 +333,64 @@ static bool _sendQ_done_handler(esp_err_t res, esp_http_client_handle_t client, #endif // parse some data from json response if one exists. - std::string jsonStatus; - std::string jsonMessage; + std::string szStatus; + std::string szMessage; std::string notify_url; cJSON * root = cJSON_Parse(r->results.c_str()); if (root) { + // check for exceptions and report + // https://www.twilio.com/docs/usage/twilios-response#response-formats-exceptions + cJSON * code = cJSON_GetObjectItem(root, "code"); + if (code) { + ESP_LOGI(TAG,"parse exception code json"); + szStatus = "Exception"; + int ret_code = code->valueint; + + std::string ret_message; + cJSON * jso_message = cJSON_GetObjectItem(root, "message"); + if (jso_message && jso_message->valuestring) { + ret_message = jso_message->valuestring; + } + + std::string ret_more_info; + cJSON * jso_more_info = cJSON_GetObjectItem(root, "more_info"); + if (jso_more_info && jso_more_info->valuestring) { + ret_more_info = jso_more_info->valuestring; + } + + int ret_status = -1; + cJSON * jso_status = cJSON_GetObjectItem(root, "status"); + if (jso_status) { + ret_status = jso_status->valueint; + } + + szMessage = ad2_string_printf("code: %i, message: '%s', more_info: '%s', status: %i", + ret_code, ret_message.c_str(), ret_more_info.c_str(), ret_status); + } // If the request contains a subresource_uris and Notifications.json then request it next else we are done. cJSON * subr_uris = cJSON_GetObjectItem(root, "subresource_uris"); if (subr_uris) { + ESP_LOGI(TAG,"parse subresource_uris json"); + szStatus = "subresource_uris"; // { "subresource_uris" : { "notifications" : "FOO", ...}} cJSON * notifications_url = cJSON_GetObjectItem(subr_uris, "notifications"); - if (notifications_url) { - notify_url = cJSON_GetStringValue(notifications_url); + if (notifications_url && notifications_url->valuestring) { + notify_url = notifications_url->valuestring; } } - // json status - cJSON * status = cJSON_GetObjectItem(root, "status"); - if (status) { - jsonStatus = cJSON_GetStringValue(status); - } - // json error cJSON * errors = cJSON_GetObjectItem(root, "errors"); if (errors) { - jsonStatus = "error"; + ESP_LOGI(TAG,"parse error json"); + szStatus = "error"; cJSON * error = cJSON_GetArrayItem(errors, 0); if (error) { cJSON * message = cJSON_GetObjectItem(error, "message"); - if (message) { - jsonMessage = cJSON_GetStringValue(message); + if (message && message->valuestring) { + szMessage = message->valuestring; } } } @@ -372,7 +398,7 @@ static bool _sendQ_done_handler(esp_err_t res, esp_http_client_handle_t client, cJSON_Delete(root); } - ESP_LOGI(TAG,"Notify slot #%i response code: '%i' status: '%s' message: '%s'", r->notify_slot, esp_http_client_get_status_code(client), jsonStatus.c_str(), jsonMessage.c_str()); + ESP_LOGI(TAG,"Notify slot #%i response code: '%i' status: '%s' message: '%s'", r->notify_slot, esp_http_client_get_status_code(client), szStatus.c_str(), szMessage.c_str()); // If first request was OK and we are scheduled to make GET for details. if (res == ESP_OK && r->state == TWILIO_NEXT_STATE_GET) { @@ -442,6 +468,11 @@ esp_err_t _twilio_http_event_handler(esp_http_client_event_t *evt) case HTTP_EVENT_DISCONNECTED: #if defined(DEBUG_TWILIO) ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED"); +#endif + break; + case HTTP_EVENT_REDIRECT: +#if defined(DEBUG_TWILIO) + ESP_LOGI(TAG, "HTTP_EVENT_REDIRECT"); #endif break; case HTTP_EVENT_ON_DATA: @@ -474,7 +505,7 @@ void on_search_match_cb_tw(std::string *msg, AD2PartitionState *s, void *arg) AD2EventSearch *es = (AD2EventSearch *)arg; #if defined(DEBUG_TWILIO) - ESP_LOGI(TAG, "ON_SEARCH_MATCH_CB: '%s' -> '%s' notify slot #%02i", msg->c_str(), es->out_message.c_str(), es->INT_ARG); + ESP_LOGI(TAG, "ON_SEARCH_MATCH_CB: '%s' -> '%s' notify slot #%i", msg->c_str(), es->out_message.c_str(), es->INT_ARG); #endif // es->PTR_ARG is the notification slots std::list for this notification. std::list *notify_list = (std::list*)es->PTR_ARG; @@ -637,7 +668,7 @@ static void _cli_cmd_twilio_event_generic(std::string &subcmd, const char *strin buf = "Y"; } } - ad2_printf_host(false, "Current acid #%02i '%s' value '%s'\r\n", accountId, subcmd.c_str(), buf.length() ? buf.c_str() : "EMPTY"); + ad2_printf_host(false, "Current acid #%i '%s' value '%s'\r\n", accountId, subcmd.c_str(), buf.length() ? buf.c_str() : "EMPTY"); } } else { ad2_printf_host(false, "Missing or invalid [1-999].\r\n"); @@ -953,12 +984,12 @@ void twilio_init() // incomplete switch so delete it and supporting pointers. delete pslots; delete es1; - ESP_LOGE(TAG, "Error in config section [switch %i]. Missing required open, close, or fault filter expressions.", swID); + ESP_LOGE(TAG, "Error in config section [switch %i]. Need at least one open, close, or trouble filter expressions.", swID); } } else { if (open_output_format.length() || close_output_format.length() || trouble_output_format.length()) { - ESP_LOGE(TAG, "Error in config for switch [switch %i]. Missing on or more required open,close, or fault output expressions.", swID); + ESP_LOGE(TAG, "Section config error. Need at least one open, close, or trouble output strings for switch %i.", swID); } } } diff --git a/components/usdupdate/CMakeLists.txt b/components/usdupdate/CMakeLists.txt new file mode 100644 index 00000000..9356fb1e --- /dev/null +++ b/components/usdupdate/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register(SRCS "usdupdate.cpp" + REQUIRES idf::nvs_flash + REQUIRES idf::alarmdecoder-api + REQUIRES idf::app_update + REQUIRES idf::esp-tls + REQUIRES idf::esp_https_ota + REQUIRES idf::json + INCLUDE_DIRS . ../../main/) +project(usdupdate) diff --git a/components/usdupdate/Kconfig b/components/usdupdate/Kconfig new file mode 100644 index 00000000..99f48b52 --- /dev/null +++ b/components/usdupdate/Kconfig @@ -0,0 +1,7 @@ +menu "AD2iot * Firmware update from uSD." + config AD2IOT_USDUPDATE + bool "uSD Firmware Updates" + default n + help + Enable uSD firmware updates. +endmenu \ No newline at end of file diff --git a/components/usdupdate/usdupdate.cpp b/components/usdupdate/usdupdate.cpp new file mode 100644 index 00000000..8815df33 --- /dev/null +++ b/components/usdupdate/usdupdate.cpp @@ -0,0 +1,150 @@ +/** + * @file usdupdate_util.c + * @author Sean Mathews + * @date 09/18/2020 + * @version 1.0.3 + * + * @brief uSD firmware update support. Flash firmware from file on uSD disk. + * + * @copyright Copyright (C) 2020 Nu Tech Software Solutions, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +static const char *TAG = "AD2LUPDATE"; + + // AlarmDecoder std includes +#include "alarmdecoder_main.h" + + // Disable via config +#if CONFIG_AD2IOT_USDUPDATE + +// esp component includes +#include +#include + +//#define DEBUG_LUPDATE + +#define CONFIG_FIRMWARE_PATH "/sdcard/firmware.bin" + +#define USDUPDATE_UPGRADE_CMD "upgradeusd" +#define USDUPDATE_VERSION_CMD "versionusd" + + +// forward decl +void usd_do_version(const char *arg); +void usd_do_update(const char *command); + +// OTA Update task +TaskHandle_t usdupdate_task_handle = NULL; + +/** + * @brief Firmware update task that preforms the update to the flash + * from the uSD disk. + */ +static void usd_task_func(void * command) +{ + ad2_printf_host(false, "Starting uSD update"); + + FILE *f = fopen(CONFIG_FIRMWARE_PATH, "rb"); + if (f == NULL) { + ad2_printf_host(false, "uSD update '" CONFIG_FIRMWARE_PATH "' not found, aborting."); + vTaskDelete(NULL); + } + + esp_ota_handle_t update_handle = 0; + const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL); + + // Start OTA + esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); + + char *buffer = (char *)malloc(4096); + while (true) { + size_t read = fread(buffer, 1, 4096, f); + if (read == 0) break; + esp_ota_write(update_handle, buffer, read); + } + + // Finish and set boot partition + esp_ota_end(update_handle); + esp_ota_set_boot_partition(update_partition); + + free(buffer); + fclose(f); + remove(CONFIG_FIRMWARE_PATH); + ad2_printf_host(true, "%s Prepare to restart system!", TAG); + hal_restart(); +} + +/** + * @brief Initiate and uSD update process + */ +void usd_do_update(const char *command) +{ + if (usdupdate_task_handle != NULL) { + ESP_LOGW(TAG, "Device is currently updating."); + return; + } + xTaskCreate(&usd_task_func, "AD2 uSD Update", 1024*8, strdup(command), tskIDLE_PRIORITY+2, &usdupdate_task_handle); +} + +/** + * @brief command list for module + */ +static struct cli_command uSDupdate_cmd_list[] = { + { + (char*)USDUPDATE_UPGRADE_CMD,(char*) + "Usage: upgradeusd\r\n" + "\r\n" + " Preform upgrade from connected uSD flash drive.\r\n" + , usd_do_update + }, + { + (char*)USDUPDATE_VERSION_CMD,(char*) + "Usage: versionusd\r\n" + "\r\n" + " Report the current version\r\n" + , usd_do_version + } +}; + + +/** + * @brief Register component cli commands. + */ +void usdupdate_register_cmds() +{ + // Register CLI commands + for (int i = 0; i < ARRAY_SIZE(uSDupdate_cmd_list); i++) { + cli_register_command(&uSDupdate_cmd_list[i]); + } +} + +/** + * @brief Init. + */ +void usdupdate_init() +{ + ad2_printf_host(true, "%s: Init done", TAG); +} + +/** + * @brief Show installed and available version + */ +void usd_do_version(const char *arg) +{ + ad2_printf_host(false, "Installed version(" FIRMWARE_VERSION ") build flag (" FIRMWARE_BUILDFLAGS ").\r\n"); +} + +#endif /* CONFIG_AD2IOT_USDUPDATE */ diff --git a/components/usdupdate/usdupdate.h b/components/usdupdate/usdupdate.h new file mode 100644 index 00000000..f791338c --- /dev/null +++ b/components/usdupdate/usdupdate.h @@ -0,0 +1,32 @@ +/** + * @file usdupdate_util.h + * @author Sean Mathews + * @date 09/18/2020 + * + * @brief uSD firmware update support + * + * @copyright Copyright (C) 2020 Nu Tech Software Solutions, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _USDUPDATE_UTIL_H +#define _USDUPDATE_UTIL_H + +void usd_do_update(const char *arg); +void usdupdate_register_cmds(); +void usdupdate_init(); + +#endif /* _USDUPDATE_UTIL_H */ + diff --git a/components/webUI/webUI.cpp b/components/webUI/webUI.cpp index a29532fb..939bb66a 100644 --- a/components/webUI/webUI.cpp +++ b/components/webUI/webUI.cpp @@ -104,20 +104,20 @@ struct ws_session_storage { void uptimeString(std::string &tstring) { // 64bit milliseconds since boot - int64_t ms = hal_uptime_us() / 1000; + uint64_t ms = hal_uptime_us() / 1000; // seconds - int32_t s = ms / 1000; + uint32_t s = ms / 1000; // days - int32_t d = s / 86400; + uint32_t d = s / 86400; s %= 86400; // hours - uint8_t h = s / 3600; + uint32_t h = s / 3600; s %= 3600; // minutes - uint8_t m = s / 60; + uint32_t m = s / 60; s %= 60; char fbuff[255]; - snprintf(fbuff, sizeof(fbuff), "%04dd:%02dh:%02dm:%02ds", d, h, m, s); + snprintf(fbuff, sizeof(fbuff), "%04lud:%02luh:%02lum:%02lus", d, h, m, s); tstring = fbuff; } @@ -583,6 +583,17 @@ esp_err_t http_acl_test(httpd_handle_t hd, int sockfd) void webui_server_task(void *pvParameters) { esp_err_t err; +#if defined(FTPD_DEBUG) + ESP_LOGI(TAG, "%s waiting for network layer to start.", TAG); +#endif + while (1) { + if (!hal_get_netif_started()) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } else { + break; + } + } + ESP_LOGI(TAG, "Network layer OK. %s daemon service starting.", TAG); // Configure the web server and handlers. server_config.uri_match_fn = httpd_uri_match_wildcard; @@ -758,12 +769,6 @@ void webui_register_cmds() */ void webui_init(void) { - // if netif not enabled then we can't start. - if (!hal_get_netif_started()) { - ad2_printf_host(true, "%s daemon disabled. Network interface not enabled.", TAG); - return; - } - bool en = -1; ad2_get_config_key_bool(WEBUI_CONFIG_SECTION, WEBUI_SUBCMD_ENABLE, &en); @@ -784,8 +789,6 @@ void webui_init(void) } } - ad2_printf_host(true, "%s: Init done, daemon starting.", TAG); - // Subscribe to AlarmDecoder events AD2Parse.subscribeTo(ON_ARM, webui_on_state_change, (void *)ON_ARM); AD2Parse.subscribeTo(ON_DISARM, webui_on_state_change, (void *)ON_DISARM); @@ -801,6 +804,7 @@ void webui_init(void) // SUbscribe to ON_ZONE_CHANGE events AD2Parse.subscribeTo(ON_ZONE_CHANGE, webui_on_state_change, (void *)ON_ZONE_CHANGE); + ad2_printf_host(true, "%s: Init done, daemon starting.", TAG); xTaskCreate(&webui_server_task, "AD2 webUI", 1024*5, NULL, tskIDLE_PRIORITY+1, NULL); } diff --git a/contrib/ESP32-DOWNLOAD-TOOL-UPLOADING-FIRMWARE.png b/contrib/ESP32-DOWNLOAD-TOOL-UPLOADING-FIRMWARE.png new file mode 100644 index 00000000..eae14081 Binary files /dev/null and b/contrib/ESP32-DOWNLOAD-TOOL-UPLOADING-FIRMWARE.png differ diff --git a/contrib/README-FLASH-ESP32.md b/contrib/README-FLASH-ESP32.md new file mode 100644 index 00000000..294e2394 --- /dev/null +++ b/contrib/README-FLASH-ESP32.md @@ -0,0 +1,23 @@ +### Flashing ESP32 with AD2IoT firmware + +#### Requirements +- ESP32 Flashing tool + - https://www.espressif.com/en/support/download/other-tools + - https://www.espressif.com/sites/default/files/tools/flash_download_tool_3.9.3.zip + +- USB Drivers + - Board specific drivers may be required programming. + - ESP32-POE-ISO USB drivers + - https://www.olimex.com/Products/IoT/ESP32/ESP32-POE-ISO/open-source-hardware + + +#### Using the ESP32 DOWNLOAD TOOL + - Select "Developer mode" + - Select "ESP32 DownloadTool" + - Match settings to provided example screen capture ESP32-DOWNLOAD-TOOL-UPLOADING-FIREMWARE.png + - Connect the ESP32 board via USB and be sure it registers as a com port in device manager. + - Press start. + +#### Configure the firmware operation +- Use Putty or other serial terminal program to connect to the COM port of the ESP32 board. +- Using the command line interface(CLI) to configure the device and confirm communication are correct to the AlarmDecoder hardware AD2pHat board or remote SER2SOCK connected AD2* device. \ No newline at end of file diff --git a/contrib/README-FLASH.md b/contrib/README-FLASH.md deleted file mode 100644 index f950eb2f..00000000 --- a/contrib/README-FLASH.md +++ /dev/null @@ -1,13 +0,0 @@ -Use the ESP32 flashing tool. -https://www.espressif.com/en/support/download/other-tools -https://www.espressif.com/sites/default/files/tools/flash_download_tool_v3.8.5.zip - - -Select "Developer mode" -Select "ESP32 DownloadTool" - -Match settings to provided example screen capture ESP32-DOWNLOAD-TOOL-UPLOADING-FIREMWARE.png -Connect the ESP32 board via USB and be sure it registers as a com port in device manager. Drivers may be required. -Press start. - -Use Putty to connect to the COM port and configure the device. diff --git a/contrib/webUI/flash-drive/README.md b/contrib/webUI/flash-drive/README-WWW.md similarity index 87% rename from contrib/webUI/flash-drive/README.md rename to contrib/webUI/flash-drive/README-WWW.md index 55cf2c1c..1e0e0de4 100644 --- a/contrib/webUI/flash-drive/README.md +++ b/contrib/webUI/flash-drive/README-WWW.md @@ -1,6 +1,6 @@ -### Place this folder in the root directory of a fat32 formatted uSD disk. +### Place the contents of this folder in the root directory of a fat32 formatted uSD disk. ``` -├── README.md +├── README-WWW.md └── www ├── 404.html ├── 404.html.tpl diff --git a/data/ad2iot.ini b/data/ad2iot.ini index 9fbcdc21..4244866d 100644 --- a/data/ad2iot.ini +++ b/data/ad2iot.ini @@ -26,6 +26,7 @@ # Set source to local attached uart with TX on GPIO 4 and RX on GPIO 36. # ```ad2source COM 4:36``` ############################################################################### +# Use caution changing this setting it can change GPIO pin settings. ad2source = C 4:36 ############################################################################### @@ -37,7 +38,11 @@ ad2source = C 4:36 # device. Can be partial config. # Example set mode Ademco with default address 18. # ```ad2config mode=A&address=18``` +# Example set mode DSC with default Partiion 1 Slot 2. +# ```ad2config mode=D&address=12``` ############################################################################### +# This will push these settings to the AD2* board. +# Avoid multiple systems trying to enforce this setting. That would be bad. ad2config = address=18&mode=A ############################################################################### @@ -160,6 +165,7 @@ description = {"type": "carbon_monoxide", "alpha": "Test CO2 ZONE"} [zone 26] description = {"type": "door", "alpha": "test door"} +# Ademco Vista specific special zone numbers. [zone 191] description = {"type": "problem", "alpha": "Long Range Radio trouble(bF)"} @@ -198,12 +204,22 @@ description = {"type": "problem", "alpha": "Failed to call(FC)"} # close IDX REGEX CLOSE event REGEX filter for IDX 1-8 # trouble IDX REGEX TROUBLE event REGEX filter for IDX 1-8 # Options: -# switchId ad2iot virtual switch ID 1-255 +# swid ad2iot virtual switch ID 1-255 # IDX REGEX index 1-8 for multiple tests # REGEX Regular expression or exact match string. # TYPE Message types [ALPHA,LRR,REL,EXP,RFX,AUI,KPM,KPE, # CRC,VER,ERR,EVENT] ############################################################################### +[switch 10] +# Test ZONE tracking event for zone 3 +default = 0 +reset = 0 +types = EVENT +filter = ZONE.* +open 1 = ZONE OPEN 003 +close 1 = ZONE CLOSE 003 +trouble 1 = ZONE TROUBLE 003 + [switch 60] # RFX serial 0123456 default = -1 @@ -420,6 +436,10 @@ switch 60 open = SWITCH 1 OPEN switch 60 close = SWITCH 1 CLOSE switch 60 trouble = SWITCH 1 TROUBLE +switch 91 notify = 1 +switch 91 open = ON MAIN AC POWER +switch 91 close = ON BATTERY BACKUP POWER + switch 95 notify = 1 switch 95 open = FIRE ON switch 95 close = FIRE OFF @@ -429,6 +449,10 @@ switch 99 notify = 1 switch 99 open = ALARM ACTIVE switch 99 close = ALARM CLEAR +switch 100 notify = 1 +switch 100 open = ARMED +switch 100 close = DISARMED + switch 103 notify = 1 switch 103 open = ARMED USER 3 switch 103 close = DISARMED USER 3 @@ -446,7 +470,7 @@ switch 103 close = DISARMED USER 3 # token acid [hash] Twilio Auth Token # from acid [address] Validated Email or Phone # # to acid [address] Email or Phone # -# type acid [M|C|E] Notification type Mail, Call, EMail +# type acid [M|C|E] Notification type SMS Text, Call, EMail # format acid [format] Output format string # switch swid SCMD [ARG] Configure switches # Sub-Commands: @@ -495,6 +519,11 @@ format 3 = <<{0}{0} END_OF_TEXT +# Notify POWER STATUS EMail+SMS +switch 91 notify = 1,2 +switch 91 open = ON MAIN AC POWER +switch 91 close = ON BATTERY BACKUP POWER + # Notify SOS Call, SMS, EMail EVERYONE! switch 95 notify = 1,2,3 switch 95 open = FIRE ALARM @@ -505,17 +534,12 @@ switch 99 notify = 1,2,3 switch 99 open = ALARM ACTIVE switch 99 close = ALARM CLEAR -# Notify POWER STATUS EMail+SMS -switch 2 notify = 1,2 -switch 2 open = ON MAIN AC POWER -switch 2 close = ON BATTERY BACKUP POWER - # Notify generic ARMED/DISARMED switch 100 notify = 1,2 switch 100 open = ALARM ARMED switch 100 close = ALARM DISARMED # Notify ARM/DISARM USER #3 on LRR/CID report but not both. -#switch 103 notify = 1,2 -#switch 103 open = ARMED USER 3 -#switch 103 close = DISARMED USER 3 +switch 103 notify = 1,2 +switch 103 open = ARMED USER 3 +switch 103 close = DISARMED USER 3 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 58c3c91d..75ffd154 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -3,6 +3,9 @@ idf_component_register(SRCS "ad2_utils.cpp" "alarmdecoder_main.cpp" "ad2_cli_cmd.cpp" "ad2_uart_cli.cpp" REQUIRES idf::esp-tls + REQUIRES idf::esp_wifi + REQUIRES idf::esp_eth + REQUIRES idf::spi_flash REQUIRES idf::json REQUIRES idf::fatfs REQUIRES idf::stsdk @@ -10,6 +13,7 @@ idf_component_register(SRCS "ad2_utils.cpp" "alarmdecoder_main.cpp" REQUIRES idf::spiffs REQUIRES idf::alarmdecoder-api REQUIRES idf::otaupdate + REQUIRES idf::usdupdate REQUIRES idf::pushover REQUIRES idf::twilio REQUIRES idf::ser2sock @@ -20,17 +24,13 @@ idf_component_register(SRCS "ad2_utils.cpp" "alarmdecoder_main.cpp" set(STDK_IOT_CORE_USE_DEFINED_CONFIG "y") -set(STDK_LINK_LIBRARY - __idf_libsodium - __idf_json - ) -set(STDK_INCLUDE_PATH - "$ENV{IDF_PATH}/components/freertos/include/freertos" - "$ENV{IDF_PATH}/components/nvs_flash/include" - "$ENV{IDF_PATH}/components/spi_flash/include" - "$ENV{IDF_PATH}/components/bootloader_support/include" - ) +# Fetch SimpleIni lib and as as include path. +# Todo: Only if not already downloaded. +include(FetchContent) # once in the project to include the module +FetchContent_Declare(simpleini GIT_REPOSITORY https://github.com/brofield/simpleini.git) +FetchContent_MakeAvailable(simpleini) +include_directories(${simpleini_SOURCE_DIR}) if(CONFIG_STDK_IOT_CORE) add_subdirectory($ENV{STDK_CORE_PATH} iotcore) diff --git a/main/ad2_cli_cmd.cpp b/main/ad2_cli_cmd.cpp index 24159839..4552b417 100644 --- a/main/ad2_cli_cmd.cpp +++ b/main/ad2_cli_cmd.cpp @@ -526,7 +526,6 @@ static void _cli_cmd_switch_event(const char *command_string) AD2SWITCH_SK_CLOSE " " AD2SWITCH_SK_TROUBLE); - bool command_found = false; ad2_printf_host(false, "## switch %i global configuration.\r\n[%s]\r\n", sId, key.c_str()); sk_index = 0; while (ss >> sk) { @@ -704,7 +703,7 @@ static struct cli_command cmd_list[] = { " close IDX REGEX CLOSE event REGEX filter for IDX 1-8\r\n" " trouble IDX REGEX TROUBLE event REGEX filter for IDX 1-8\r\n" "Options:\r\n" - " switchId ad2iot virtual switch ID 1-255\r\n" + " swid ad2iot virtual switch ID 1-255\r\n" " IDX REGEX index 1-8 for multiple tests\r\n" " REGEX Regular expression or exact match string.\r\n" " TYPE Message types [ALPHA,LRR,REL,EXP,RFX,AUI,KPM,KPE,\r\n" @@ -803,6 +802,8 @@ static struct cli_command cmd_list[] = { " device. Can be partial config.\r\n" " Example set mode Ademco with default address 18.\r\n" " ```ad2config mode=A&address=18```\r\n" + " Example set mode DSC with default address 1 Slot 2.\r\n" + " ```ad2config mode=D&address=12```\r\n" , _cli_cmd_ad2config_event }, { diff --git a/main/ad2_settings.h b/main/ad2_settings.h index 7bba53eb..97a07a80 100644 --- a/main/ad2_settings.h +++ b/main/ad2_settings.h @@ -24,7 +24,7 @@ //#define AD2_STACK_REPORT // @brief Firmware version string. -#define FIRMWARE_VERSION "AD2IOT-1101" +#define FIRMWARE_VERSION "AD2IOT-1103" #if defined(CONFIG_STDK_IOT_CORE) #define FIRMWARE_BUILDFLAGS "stsdk" diff --git a/main/ad2_utils.cpp b/main/ad2_utils.cpp index 810e03ac..bc78b427 100644 --- a/main/ad2_utils.cpp +++ b/main/ad2_utils.cpp @@ -39,7 +39,10 @@ static const char *TAG = "AD2UTIL"; // esp includes #include "nvs_flash.h" #include "mbedtls/base64.h" - +#include "esp_system.h" +#include "esp_mac.h" +#include "esp_chip_info.h" +#include "esp_flash.h" #include // ini config class static CSimpleIniA _ad2ini; @@ -976,7 +979,7 @@ int ad2_copy_nth_arg(std::string &dest, const char *src, int n, bool remaining) */ void ad2_config_update(const char *arg) { - + ESP_LOGI(TAG, "TODO: AD2IoT config update command"); } /** @@ -990,7 +993,7 @@ void ad2_config_update(const char *arg) */ void ad2_fw_update(const char *arg) { - + ESP_LOGI(TAG, "TODO: AD2 Firmware update command"); } /** @@ -1649,7 +1652,9 @@ cJSON *ad2_get_ad2iot_device_info_json() cJSON_AddItemToArray(cjson_cpu_features, cJSON_CreateString("BT")); } cJSON_AddItemToObject(root, "cpu_features", cjson_cpu_features); - cJSON_AddNumberToObject(root, "cpu_flash_size", spi_flash_get_chip_size()); + uint32_t size_flash_chip; + esp_flash_get_size(NULL, &size_flash_chip); + cJSON_AddNumberToObject(root, "cpu_flash_size", size_flash_chip); std::string flash_type = (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"; cJSON_AddStringToObject(root, "cpu_flash_type", flash_type.c_str()); diff --git a/main/alarmdecoder_main.cpp b/main/alarmdecoder_main.cpp index f5b8489e..006a7ac8 100644 --- a/main/alarmdecoder_main.cpp +++ b/main/alarmdecoder_main.cpp @@ -30,19 +30,23 @@ static const char *TAG = "AD2_IoT"; // esp component includes #include "driver/uart.h" #include "nvs_flash.h" -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) -#include "tcpip_adapter.h" -#else #include "esp_netif.h" #include "esp_tls.h" -#endif + // specific includes // SimpleIni #include -// OTA updates +// OTA firmware updates +#if CONFIG_AD2IOT_OTAUPDATE #include "ota_util.h" +#endif + +// uSD flash firmware updates +#if CONFIG_AD2IOT_USDUPDATE +#include "usdupdate.h" +#endif // Command line interface #include "ad2_cli_cmd.h" @@ -503,6 +507,7 @@ void _update_top_task_stats() */ bool _show_pretty_process_list() { +#if CONFIG_AD2IOT_TOP // Format string for data columns. // "Name", "ID", "State", "Priority", "Stack", "CPU#", "TIME", "%BUSY, %CUR" #define TABBED_HEADER_FMT "%-15s %-3s %-5s %-8s %-5s %-4s %-20s %-6s %-6s\r\n" @@ -621,6 +626,7 @@ bool _show_pretty_process_list() " Stack: Minimum stack free bytes, CPU#: CPU affinity\r\n" " TBusy: %% busy total, Busy: %% busy now\r\n" ); +#endif return true; } @@ -632,6 +638,7 @@ bool _show_pretty_process_list() */ static void _cli_cmd_top_event(const char *string) { +#if CONFIG_AD2IOT_TOP uint8_t rx_buffer[AD2_UART_RX_BUFF_SIZE]; while(1) { if (ulTaskNotifyTake( pdTRUE, pdMS_TO_TICKS(100)) != 0) { @@ -650,6 +657,7 @@ static void _cli_cmd_top_event(const char *string) break; } } +#endif } #endif @@ -935,13 +943,9 @@ void init_ad2_uart_client(const char *args) // esp_restart causes issues with UART2 don't set config if reset switch pushed. // https://github.com/espressif/esp-idf/issues/5274 -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) if (esp_reset_reason() != ESP_RST_SW) { -#endif uart_param_config((uart_port_t)g_ad2_client_handle, uart_config); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) } -#endif uart_set_pin((uart_port_t)g_ad2_client_handle, tx_pin, // TX @@ -966,11 +970,13 @@ void init_ad2_uart_client(const char *args) */ void dump_mem_stats() { +#if CONFIG_AD2IOT_TOP float Total = heap_caps_get_total_size(MALLOC_CAP_32BIT); float Free_Size = heap_caps_get_free_size(MALLOC_CAP_32BIT); float DRam = heap_caps_get_free_size(MALLOC_CAP_8BIT); float IRam = heap_caps_get_free_size(MALLOC_CAP_32BIT) - heap_caps_get_free_size(MALLOC_CAP_8BIT); ad2_printf_host(true, "Total Heap Size %.2f Kb\nFree Space %.2f Kb\nDRAM %.2f Kb\nIRAM %.2f Kb\n",Total/1024,Free_Size/1024,DRam/1024,IRam/1024); +#endif } // external call from FreeRTOS is CDECL @@ -1123,6 +1129,16 @@ extern "C" { ftpd_register_cmds(); #endif +#if CONFIG_AD2IOT_OTAUPDATE + // Register OTA firmware update commands. + ota_register_cmds(); +#endif + +#if CONFIG_AD2IOT_USDUPDATE + // Register uSD firmware update commands. + usdupdate_register_cmds(); +#endif + // Register AD2 CLI commands. register_ad2_cli_cmd(); @@ -1249,6 +1265,14 @@ extern "C" { // Initialize FTP daemon ftpd_init(); #endif +#if CONFIG_AD2IOT_OTAUPDATE + // Initialize OTA firmware updates + ota_init(); +#endif +#if CONFIG_AD2IOT_USDUPDATE + // Initialize uSD firmware updates + usdupdate_init(); +#endif // Sleep for another 5 seconds. Hopefully wifi is up before we continue connecting the AD2*. vTaskDelay(5000 / portTICK_PERIOD_MS); @@ -1256,9 +1280,6 @@ extern "C" { // Start main AlarmDecoder IoT app task xTaskCreate(ad2_app_main_task, "AD2 main", 1024*4, NULL, tskIDLE_PRIORITY+1, NULL); - // Start firmware update task - ota_init(); - // If the AD2* is a socket connection we can hopefully start it now. if (g_ad2_mode == 'S') { init_ser2sock_client(ad2_mode_args.c_str()); diff --git a/main/alarmdecoder_main.h b/main/alarmdecoder_main.h index b88353cd..846342bd 100644 --- a/main/alarmdecoder_main.h +++ b/main/alarmdecoder_main.h @@ -31,9 +31,7 @@ #include "esp_log.h" #include "cJSON.h" #include -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) #include "esp_crt_bundle.h" -#endif #include "esp_http_client.h" diff --git a/main/device_control.cpp b/main/device_control.cpp index 76846622..6b5a7259 100644 --- a/main/device_control.cpp +++ b/main/device_control.cpp @@ -38,8 +38,20 @@ static const char *TAG = "HAL"; #include "nvs_flash.h" #include "esp_spiffs.h" +#include "esp_system.h" +#include "esp_mac.h" +#include "esp_chip_info.h" +#include "esp_eth_phy.h" +#include "esp_flash.h" +#include "esp_timer.h" + // specific includes +#if CONFIG_AD2IOT_OTAUPDATE #include "ota_util.h" +#endif +#if CONFIG_AD2IOT_USDUPDATE +#include "usdupdate.h" +#endif // forward decl for private event handlers. void _eth_event_handler(void *arg, esp_event_base_t event_base, @@ -55,10 +67,7 @@ void _lost_ip_event_handler(void *arg, esp_event_base_t event_base, // track count of AP client connection attempts. static int ap_retry_num = 0; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) -#else static esp_netif_t* _netif = NULL; -#endif // networking event state bits @@ -68,7 +77,8 @@ const int NET_STA_CONNECT_BIT = BIT2; const int NET_STA_DISCONNECT_BIT = BIT3; const int NET_AP_START_BIT = BIT4; const int NET_AP_STOP_BIT = BIT5; -const int NET_CONNECT_STATE_BITS = BIT1|BIT2|BIT3|BIT4|BIT5; +const int NET_NETIF_HAS_IP = BIT6; +const int NET_CONNECT_STATE_BITS = BIT1|BIT2|BIT3|BIT4|BIT5|BIT6; static bool switchAState = SWITCH_OFF; static bool switchBState = SWITCH_OFF; @@ -173,7 +183,7 @@ int hal_get_button_event(int* button_event_type, int* button_event_count) vTaskDelay( pdMS_TO_TICKS(BUTTON_DEBOUNCE_TIME_MS) ); gpio_level = gpio_get_level((gpio_num_t)GPIO_INPUT_BUTTON); if (button_last_state != gpio_level) { - ESP_LOGI(TAG, "%s: Button event, val: %d, tick: %u", __func__, gpio_level, (uint32_t)xTaskGetTickCount()); + ESP_LOGI(TAG, "%s: Button event, val: %lu, tick: %lu", __func__, gpio_level, (uint32_t)xTaskGetTickCount()); button_last_state = gpio_level; if (gpio_level == BUTTON_GPIO_PRESSED) { button_count++; @@ -413,7 +423,7 @@ void _wifi_event_handler(void *arg, esp_event_base_t event_base, break; default: - ESP_LOGI(TAG, "wifi_event_handler = %d", event_id); + ESP_LOGI(TAG, "wifi_event_handler = %lu", event_id); break; } @@ -431,16 +441,10 @@ void hal_init_network_stack() ESP_LOGI(TAG, "network TCP/IP stack init start"); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - tcpip_adapter_init(); - ESP_ERROR_CHECK(esp_event_loop_init(nullptr, nullptr)); -#else ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); -#endif - - xEventGroupSetBits(g_ad2_net_event_group, NET_NETIF_STARTED_BIT); + hal_set_netif_started(true); ESP_LOGI(TAG, "network TCP/IP stack init finish"); return; @@ -474,19 +478,11 @@ void hal_init_wifi(std::string &args) return; } -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &_wifi_event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &_got_ip_event_handler, NULL)); -#if CONFIG_LWIP_IPV6 - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &_got_ip_event_handler, NULL)); -#endif -#else _netif = esp_netif_create_default_wifi_sta(); ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &_wifi_event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &_got_ip_event_handler, NULL, NULL)); #if CONFIG_LWIP_IPV6 ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_GOT_IP6, &_got_ip_event_handler, NULL, NULL)); -#endif #endif esp_ret = esp_wifi_set_storage(WIFI_STORAGE_RAM); @@ -551,65 +547,33 @@ void hal_init_wifi(std::string &args) res = AD2Parse.query_key_value_string(args, "DNS1", dns1); res = AD2Parse.query_key_value_string(args, "DNS2", dns2); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - tcpip_adapter_ip_info_t ip_info; - memset(&ip_info, 0, sizeof(tcpip_adapter_ip_info_t)); - tcpip_adapter_dns_info_t dns_info; - memset(&dns_info, 0, sizeof(tcpip_adapter_dns_info_t)); -#else esp_netif_ip_info_t ip_info; memset(&ip_info, 0, sizeof(esp_netif_ip_info_t)); esp_netif_dns_info_t dns_info; memset(&dns_info, 0, sizeof(esp_netif_dns_info_t)); -#endif // Assign static ip/netmask to interface -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ip4addr_aton(ip.c_str(), &ip_info.ip); - ip4addr_aton(netmask.c_str(), &ip_info.netmask); -#else ip_info.ip.addr = esp_ip4addr_aton(ip.c_str()); ip_info.netmask.addr = esp_ip4addr_aton(netmask.c_str()); -#endif // if gateway defined set it. if (gateway.length()) { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ip4addr_aton(gateway.c_str(), &ip_info.gw); -#else ip_info.gw.addr = esp_ip4addr_aton(gateway.c_str()); -#endif } -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - // stop dhcp client service on interface. - ESP_ERROR_CHECK(tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA)); - ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info)); -#else ESP_ERROR_CHECK(esp_netif_dhcpc_stop(_netif)); ESP_ERROR_CHECK(esp_netif_set_ip_info(_netif, &ip_info)); -#endif // assign DNS1 if avail. if (dns1.length()) { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ipaddr_aton(dns1.c_str(), &dns_info.ip); - ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_MAIN, &dns_info)); -#else dns_info.ip.u_addr.ip4.addr = esp_ip4addr_aton(dns1.c_str()); ESP_ERROR_CHECK(esp_netif_set_dns_info(_netif, ESP_NETIF_DNS_MAIN, &dns_info)); -#endif } // assign DNS2 if avail. if (dns2.length()) { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ipaddr_aton(dns2.c_str(), &dns_info.ip); - ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_BACKUP, &dns_info)); -#else dns_info.ip.u_addr.ip4.addr = esp_ip4addr_aton(dns2.c_str()); ESP_ERROR_CHECK(esp_netif_set_dns_info(_netif, ESP_NETIF_DNS_BACKUP, &dns_info)); -#endif } } else { ESP_LOGI(TAG, "DHCP Mode selected"); @@ -660,39 +624,28 @@ void hal_init_eth(std::string &args) vTaskDelay(pdMS_TO_TICKS(10)); #endif -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ESP_ERROR_CHECK(tcpip_adapter_set_default_eth_handlers()); - ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &_eth_event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &_got_ip_event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_LOST_IP, &_lost_ip_event_handler, NULL)); -#if CONFIG_LWIP_IPV6 - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &_got_ip_event_handler, NULL)); -#endif -#else esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); _netif = esp_netif_new(&cfg); - esp_eth_set_default_handlers(_netif); ESP_ERROR_CHECK(esp_event_handler_instance_register(ETH_EVENT, ESP_EVENT_ANY_ID, &_eth_event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &_got_ip_event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_LOST_IP, &_lost_ip_event_handler, NULL, NULL)); #if CONFIG_LWIP_IPV6 ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_GOT_IP6, &_got_ip_event_handler, NULL, NULL)); #endif -#endif - eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + eth_esp32_emac_config_t emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); phy_config.phy_addr = CONFIG_AD2IOT_ETH_PHY_ADDR; phy_config.reset_gpio_num = CONFIG_AD2IOT_ETH_PHY_RST_GPIO; phy_config.reset_timeout_ms = 100; #if CONFIG_AD2IOT_USE_INTERNAL_ETHERNET - mac_config.smi_mdc_gpio_num = (gpio_num_t)CONFIG_AD2IOT_ETH_MDC_GPIO; - mac_config.smi_mdio_gpio_num = (gpio_num_t)CONFIG_AD2IOT_ETH_MDIO_GPIO; - esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); + emac_config.smi_mdc_gpio_num = (gpio_num_t)CONFIG_AD2IOT_ETH_MDC_GPIO; + emac_config.smi_mdio_gpio_num = (gpio_num_t)CONFIG_AD2IOT_ETH_MDIO_GPIO; + esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&emac_config, &mac_config); #ifdef CONFIG_AD2IOT_ETH_PHY_LAN8720 - esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config); + esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config); #endif #endif @@ -700,12 +653,7 @@ void hal_init_eth(std::string &args) esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); esp_eth_handle_t eth_handle = NULL; ESP_ERROR_CHECK(esp_eth_driver_install(&config, ð_handle)); - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) -#pragma message "No netif in older espidf not using." -#else esp_netif_attach(_netif, esp_eth_new_netif_glue(eth_handle)); -#endif // test DHCP or Static configuration res = AD2Parse.query_key_value_string(args, "MODE", value); @@ -735,65 +683,34 @@ void hal_init_eth(std::string &args) res = AD2Parse.query_key_value_string(args, "DNS1", dns1); res = AD2Parse.query_key_value_string(args, "DNS2", dns2); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - tcpip_adapter_ip_info_t ip_info; - memset(&ip_info, 0, sizeof(tcpip_adapter_ip_info_t)); - tcpip_adapter_dns_info_t dns_info; - memset(&dns_info, 0, sizeof(tcpip_adapter_dns_info_t)); -#else esp_netif_ip_info_t ip_info; memset(&ip_info, 0, sizeof(esp_netif_ip_info_t)); esp_netif_dns_info_t dns_info; memset(&dns_info, 0, sizeof(esp_netif_dns_info_t)); -#endif // Assign static ip/netmask to interface -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ip4addr_aton(ip.c_str(), &ip_info.ip); - ip4addr_aton(netmask.c_str(), &ip_info.netmask); -#else ip_info.ip.addr = esp_ip4addr_aton(ip.c_str()); ip_info.netmask.addr = esp_ip4addr_aton(netmask.c_str()); -#endif // if gateway defined set it. if (gateway.length()) { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ip4addr_aton(gateway.c_str(), &ip_info.gw); -#else ip_info.gw.addr = esp_ip4addr_aton(gateway.c_str()); -#endif } // stop dhcp client service on interface and set the ip_info -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ESP_ERROR_CHECK(tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_ETH)); - ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_ETH, &ip_info)); -#else ESP_ERROR_CHECK(esp_netif_dhcpc_stop(_netif)); ESP_ERROR_CHECK(esp_netif_set_ip_info(_netif, &ip_info)); -#endif // assign DNS1 if avail. if (dns1.length()) { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ipaddr_aton(dns1.c_str(), &dns_info.ip); - ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_ETH, TCPIP_ADAPTER_DNS_MAIN, &dns_info)); -#else dns_info.ip.u_addr.ip4.addr = esp_ip4addr_aton(dns1.c_str()); ESP_ERROR_CHECK(esp_netif_set_dns_info(_netif, ESP_NETIF_DNS_MAIN, &dns_info)); -#endif } // assign DNS2 if avail. if (dns2.length()) { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ipaddr_aton(dns2.c_str(), &dns_info.ip); - ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_ETH, TCPIP_ADAPTER_DNS_BACKUP, &dns_info)); -#else dns_info.ip.u_addr.ip4.addr = esp_ip4addr_aton(dns2.c_str()); ESP_ERROR_CHECK(esp_netif_set_dns_info(_netif, ESP_NETIF_DNS_BACKUP, &dns_info)); -#endif } } else { ESP_LOGI(TAG, "DHCP Mode selected"); @@ -810,11 +727,7 @@ void hal_init_eth(std::string &args) void hal_set_wifi_hostname(const char *hostname) { esp_err_t ret; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ret = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, hostname); -#else ret = esp_netif_set_hostname(_netif, hostname); -#endif if(ret != ESP_OK ) { ESP_LOGE(TAG,"failed to set wifi hostname: %i %i '%s'", ret, errno, strerror(errno)); } @@ -826,11 +739,7 @@ void hal_set_wifi_hostname(const char *hostname) void hal_set_eth_hostname(const char *hostname) { esp_err_t ret; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - ret = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_ETH, hostname); -#else ret = esp_netif_set_hostname(_netif, hostname); -#endif if(ret != ESP_OK ) { ESP_LOGE(TAG,"failed to set eth hostname: %i %i '%s'", ret, errno, strerror(errno)); } @@ -879,12 +788,8 @@ void _got_ip_event_handler(void *arg, esp_event_base_t event_base, #endif ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,1,0) - const tcpip_adapter_ip_info_t *ip_info = &event->ip_info; -#else const esp_netif_ip_info_t *ip_info = &event->ip_info; IF = esp_netif_get_desc(event->esp_netif); -#endif IP = ad2_string_printf(IPSTR, IP2STR(&ip_info->ip)); MASK = ad2_string_printf(IPSTR, IP2STR(&ip_info->netmask)); GW = ad2_string_printf(IPSTR, IP2STR(&ip_info->gw)); @@ -966,8 +871,15 @@ void hal_ad2_reset() { #if defined(GPIO_AD2_RESET) ESP_LOGI(TAG, "Asserting reset on AD2."); - gpio_pad_select_gpio(GPIO_AD2_RESET); - gpio_set_direction(GPIO_AD2_RESET, GPIO_MODE_OUTPUT); + + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = (gpio_num_t) 1 << GPIO_AD2_RESET; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&io_conf); + gpio_set_level(GPIO_AD2_RESET, 0); vTaskDelay(1000 / portTICK_PERIOD_MS); gpio_set_level(GPIO_AD2_RESET, 1); @@ -982,6 +894,18 @@ bool hal_get_netif_started() return xEventGroupGetBits(g_ad2_net_event_group) & NET_NETIF_STARTED_BIT; } +/** + * @brief Set network interface stack init state. + */ +void hal_set_netif_started(bool started) +{ + if (started) { + xEventGroupSetBits(g_ad2_net_event_group, NET_NETIF_STARTED_BIT); + } else { + xEventGroupClearBits(g_ad2_net_event_group, NET_NETIF_STARTED_BIT); + } +} + /** * @brief Get ip network connection state. */ @@ -990,12 +914,30 @@ bool hal_get_network_connected() return xEventGroupGetBits(g_ad2_net_event_group) & NET_STA_CONNECT_BIT; } +/** + * @brief SET ip network connection state. + */ +void hal_set_network_connected(bool connected) +{ + if (connected) { + xEventGroupSetBits(g_ad2_net_event_group, NET_STA_CONNECT_BIT); + } else { + xEventGroupClearBits(g_ad2_net_event_group, NET_STA_CONNECT_BIT); + } +} + /** * @brief Do an OTA update. */ -void hal_ota_do_update(const char * arg) +void hal_do_fwupdate(const char * arg) { +#if CONFIG_AD2IOT_OTAUPDATE ota_do_update((char*)arg); +#else +#if CONFIG_AD2IOT_USDUPDATE + usd_do_update((char*)arg); +#endif +#endif } /** @@ -1006,18 +948,6 @@ uint64_t hal_uptime_us() return esp_timer_get_time(); } -/** - * @brief get CONNECTED state. - */ -void hal_set_network_connected(bool set) -{ - ESP_LOGD(TAG, "hal_set_network_connected %i", set); - if (set) { - xEventGroupSetBits(g_ad2_net_event_group, NET_STA_CONNECT_BIT); - } else { - xEventGroupClearBits(g_ad2_net_event_group, NET_STA_CONNECT_BIT); - } -} /** * @brief Initialize the uSD reader if one is connected. @@ -1051,7 +981,6 @@ bool hal_init_sd_card() } else { ad2_printf_host(false, " pass."); // show stats and return true - size_t total = 0, used = 0; FATFS *fs; DWORD fre_clust, fre_sect, tot_sect; FRESULT res; @@ -1179,7 +1108,10 @@ void hal_dump_hw_info() ad2_printf_host(false, "silicon revision %d, ", chip_info.revision); - ad2_printf_host(false, "%dMB %s flash", spi_flash_get_chip_size() / (1024 * 1024), + uint32_t size_flash_chip; + esp_flash_get_size(NULL, &size_flash_chip); + + ad2_printf_host(false, "%dMB %s flash", size_flash_chip / (1024 * 1024), (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); } diff --git a/main/device_control.h b/main/device_control.h index 6468bba7..80338109 100644 --- a/main/device_control.h +++ b/main/device_control.h @@ -133,9 +133,10 @@ void hal_set_eth_hostname(const char *); void hal_host_uart_init(); void hal_ad2_reset(); bool hal_get_netif_started(); +void hal_set_netif_started(bool connected); bool hal_get_network_connected(); -void hal_ota_do_update(const char *); -void hal_set_network_connected(bool); +void hal_set_network_connected(bool connected); +void hal_do_fwupdate(const char *); bool hal_init_sd_card(); void hal_get_socket_client_ip(int sockfd, std::string& IP); void hal_get_socket_local_ip(int sockfd, std::string& IP); diff --git a/platformio.ini b/platformio.ini index 7117e3c7..e97249d0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,13 +21,14 @@ upload_port = /dev/ttyUSB0 framework = espidf build_flags = -I main -# Include simpleini as a dependency lib. We can then include -lib_deps = https://github.com/brofield/simpleini +lib_deps = + ; Include simpleini as a dependency lib. We can then include + https://github.com/brofield/simpleini#v4.19 # Specify version of espressif 32 platform. # https://docs.platformio.org/en/latest/platforms/espressif32.html # Force 4.3 branch -platform = espressif32@=4.3.* +platform = espressif32@=6.4.* [env:esp32dev] board = esp32dev diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 00f7f015..475f1c85 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -1,18 +1,50 @@ -# GENERAL +# Compiler settings +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 +CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + +# FreeRTOS settings CONFIG_FREERTOS_UNICORE=y -CONFIG_OPTIMIZATION_LEVEL_RELEASE=y +CONFIG_FREERTOS_ISR_STACKSIZE=2096 +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_USE_TRACE_FACILITY=y + +# General settings +CONFIG_LOG_COLORS=n CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y CONFIG_BOOTLOADER_LOG_LEVEL=0 + +# Hardware specific settings +CONFIG_ESP_INT_WDT_TIMEOUT_MS=3000 +## ESP32-WROOM-32E CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_BOOTLOADER_SPI_WP_PIN=7 CONFIG_ESPTOOLPY_FLASHSIZE="4MB" CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +## ESP32-POE-ISO ETHERNET config +CONFIG_ETH_USE_ESP32_EMAC=y +CONFIG_ETH_PHY_INTERFACE_RMII=y +CONFIG_ETH_RMII_CLK_OUT_GPIO=17 +CONFIG_ETH_RMII_CLK_OUTPUT=y +CONFIG_AD2IOT_ETH_PHY_LAN8720=y +CONFIG_AD2IOT_ETH_MDC_GPIO=23 +CONFIG_AD2IOT_ETH_MDIO_GPIO=18 +CONFIG_AD2IOT_ETH_PHY_RST_GPIO=-1 +CONFIG_AD2IOT_ETH_PHY_POWER_GPIO=12 +CONFIG_AD2IOT_ETH_PHY_ADDR=0 + +# Partition settings CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.4MB.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.4MB.csv" CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y -# CONFIG_FATFS_LFN_NONE is not set + +# FATFS settings CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_CODEPAGE_437=y CONFIG_FATFS_CODEPAGE=437 @@ -22,61 +54,68 @@ CONFIG_FATFS_API_ENCODING_ANSI_OEM=y CONFIG_FATFS_FS_LOCK=0 CONFIG_FATFS_TIMEOUT_MS=10000 CONFIG_FATFS_PER_FILE_CACHE=y -CONFIG_FREERTOS_USE_TRACE_FACILITY=y -CONFIG_COMPILER_CXX_EXCEPTIONS=y -CONFIG_CXX_EXCEPTIONS=y -CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 -CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 -# ESP32-POE-ISO ETHERNET -CONFIG_ETH_USE_ESP32_EMAC=y -CONFIG_ETH_PHY_INTERFACE_RMII=y -CONFIG_ETH_RMII_CLK_OUT_GPIO=17 -CONFIG_ETH_RMII_CLK_OUTPUT=y -CONFIG_AD2IOT_ETH_PHY_LAN8720=y -CONFIG_AD2IOT_ETH_MDC_GPIO=23 -CONFIG_AD2IOT_ETH_MDIO_GPIO=18 -CONFIG_AD2IOT_ETH_PHY_RST_GPIO=-1 -CONFIG_AD2IOT_ETH_PHY_POWER_GPIO=12 -CONFIG_AD2IOT_ETH_PHY_ADDR=0 +# Coredump settings +CONFIG_ESP_COREDUMP_ENABLE=y +CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y +CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=y +CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=y +CONFIG_ESP_COREDUMP_CHECK_BOOT=y +CONFIG_ESP_COREDUMP_MAX_TASKS_NUM=20 -# DEFAULT COMPONENTS & SETTINGS +# LWIP settings CONFIG_LWIP_LOCAL_HOSTNAME="ad2iot" +CONFIG_LWIP_IPV6_AUTOCONFIG=y CONFIG_LWIP_MAX_SOCKETS=16 -CONFIG_AD2IOT_USE_WIFI=y -CONFIG_AD2IOT_USE_ETHERNET=y -CONFIG_AD2IOT_USE_INTERNAL_ETHERNET=y -CONFIG_MQTT_REPORT_DELETED_MESSAGES=y + +# MQTT client settings CONFIG_MQTT_PROTOCOL_311=y CONFIG_MQTT_TRANSPORT_SSL=y CONFIG_MQTT_TRANSPORT_WEBSOCKET=y CONFIG_MQTT_MSG_ID_INCREMENTAL=y -CONFIG_MQTT_CUSTOM_OUTBOX=y +CONFIG_MQTT_REPORT_DELETED_MESSAGES=y + +# HTTP(s) / WebSockets server CONFIG_ESP_HTTPS_SERVER_ENABLE=y CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y CONFIG_HTTPD_WS_SUPPORT=y +## TODO: Make better... +CONFIG_ESP_TLS_INSECURE=y +CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y + +# SSL/TLS certificate bundles CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="certs" +CONFIG_MBEDTLS_DYNAMIC_BUFFER=y +CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y +CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT=y + +# STSDK build settings CONFIG_STDK_IOT_CORE=y +CONFIG_STDK_IOT_CORE_BSP_SUPPORT_ESP32=y +CONFIG_STDK_IOT_CORE_EASYSETUP_DISCOVERY_SSID=y CONFIG_STDK_IOT_CORE_EASYSETUP_HTTP=y +CONFIG_STDK_IOT_CORE_EASYSETUP_HTTP_USE_SOCKET_API=y CONFIG_STDK_IOT_CORE_LOG_LEVEL_ERROR=y CONFIG_STDK_IOT_CORE_LOG_LEVEL_WARN=y CONFIG_STDK_IOT_CORE_LOG_LEVEL_INFO=y +# CONFIG_STDK_IOT_CORE_LOG_LEVEL_DEBUG is not set # CONFIG_STDK_IOT_CORE_SUPPORT_STNV_PARTITION is not set -CONFIG_STDK_IOT_CORE_LOG_FILE=y -CONFIG_STDK_IOT_CORE_LOG_FILE_RAM_ONLY=y -CONFIG_STDK_IOT_CORE_LOG_FILE_RAM_BUF_SIZE=8192 -CONFIG_STDK_IOT_CORE_BSP_SUPPORT_ESP32=y -CONFIG_STDK_IOT_CORE_OS_SUPPORT_FREERTOS=y -CONFIG_STDK_IOT_CORE_USE_MBEDTLS=y -CONFIG_STDK_IOT_CORE_CRYPTO_SUPPORT_ED25519=y -CONFIG_STDK_IOT_CORE_SECURITY_BACKEND_SOFTWARE=y -CONFIG_STDK_IOT_CORE_FS_HW_ENCRYPTION=y -CONFIG_STDK_IOT_CORE_NET_MBEDTLS=y + +# AD2IoT network settings +CONFIG_AD2IOT_USE_WIFI=y +CONFIG_AD2IOT_USE_ETHERNET=y +CONFIG_AD2IOT_USE_INTERNAL_ETHERNET=y + +# AD2IoT default components enable/disable CONFIG_AD2IOT_TOP=y +CONFIG_AD2IOT_FTP_DAEMON=y +CONFIG_AD2IOT_MQTT_CLIENT=y +CONFIG_AD2IOT_WEBSERVER_UI=y +CONFIG_AD2IOT_SER2SOCKD=y CONFIG_AD2IOT_TWILIO_CLIENT=y CONFIG_AD2IOT_PUSHOVER_CLIENT=y -CONFIG_AD2IOT_SER2SOCKD=y -CONFIG_AD2IOT_WEBSERVER_UI=y -CONFIG_AD2IOT_MQTT_CLIENT=y +CONFIG_AD2IOT_OTAUPDATE=n +CONFIG_AD2IOT_USDUPDATE=y