support for ena-eke-proxy, improved interface

This commit is contained in:
Lurkars 2020-12-06 12:48:12 +01:00
parent 4029099d2a
commit d0e52071f1
53 changed files with 1478 additions and 5252 deletions

2
.gitignore vendored
View File

@ -44,3 +44,5 @@ sdkconfig
sdkconfig.old
.local
cert.pem

View File

@ -20,6 +20,7 @@ The following acronyms will be used in code and comments:
* permanent storage on flash of last keys, beacons and exposures (storage is limited, see [storage math](#some-storage-math) for details)
* parsing of Exposure Key export binaries as defined in [Exposure Key export file format and verification](https://developers.google.com/android/exposure-notifications/exposure-key-file-format) (big thanks to [nanopb](https://github.com/nanopb/nanopb) for making this easier than I thought!)
* calculating exposure risks/scores (after adding reported keys and storing exposure information) as defined in [ENExposureConfiguration (Apple)](https://developer.apple.com/documentation/exposurenotification/enexposureconfiguration/calculating_the_exposure_risk_value_in_exposurenotification_version_1)
* receive Exposue Key export from an ENA Exposure Key export proxy server [ena-eke-proxy module](#ena-eke-proxy), see [ena-eke-proxy server reference implemenation](https://github.com/Lurkars/ena-eke-proxy)
Additional features for full ENA device
* RTC support with DS3231 (for correct system time)
@ -31,18 +32,16 @@ Additional features for full ENA device
* delete data
* set language
* enter tan for infection status (dummy for now)
* battery support with ESP32 dev-boards with integrated LiPo support
### Features in development
* automatically receive Exposure Key export from web (started with [Corona Warn App](https://github.com/corona-warn-app))
* send infected status (will test [Corona Warn App](https://github.com/corona-warn-app))
* battery support
* 3d print case
### Limitations/Problems/Questions
* WiFi or other external connection needed for infections status (auto-connect to open WiFis?)
* obtaining accessibility
* all parameters (scanning time, thresholds etc.)
* memory (RAM) is really low with BLE and WiFi enabled, unzipping a Exposure Key export not possible for now, maybe disable BLE service for download.
* service UUID is send reversed, RPI and AEM also send in reverse? Don't know BLE specification enough
### some storage math
@ -133,13 +132,15 @@ The ena module contains the main functions of eps-ena with bluetooth scanning an
* *ena-exposure* compare exposed keys with stored beacons, calculate score and risk
* *ena* run all together and timing for scanning and advertising
### ena-binary-export
### ena-eke-proxy
Module to decode Exposure Key export.
This module is for connecting to an Exposue Key export proxy server. The server must provide daily (and could hourly) fetch of daily keys in binary blob batches with the following format
### ena-cwa
| Key Data | Rolling Start Interval Number | Rolling Period | Days Since Onset Of Symptoms |
| :------: | :---------------------------: | :------------: | :--------------------------: |
| 16 bytes | 4 bytes | 4 bytes | 4 bytes |
Connection to german Exposure App ([Corona Warn App](https://github.com/corona-warn-app)) for download Exposure Key export (and maybe later report infection).
Request url is parametrized with {day-string},({hour} in hourly mode,) {page}, {page-size}.
### interface
@ -169,6 +170,14 @@ I2C driver for a DS3231 RTC, implementation of [rtc](#-rtc) module
I2C driver for a SSD1306 display, implementation of [display](#-display) module
### nanopb
### ena-binary-export \[deprecared\]
[Nanopb](https://github.com/nanopb/nanopb) for reading Protocol Buffers of Exposure Key export. Including already generated Headers from *.proto files.
Module to decode Exposure Key export. \[Depracated through ena-eke-proxy module\]
### ena-cwa \[deprecared\]
Connection to german Exposure App ([Corona Warn App](https://github.com/corona-warn-app)) for download Exposure Key export (and maybe later report infection). \[Depracated through ena-eke-proxy module\]
### nanopb \[deprecared\]
[Nanopb](https://github.com/nanopb/nanopb) for reading Protocol Buffers of Exposure Key export. Including already generated Headers from *.proto files. \[Depracated through ena-eke-proxy module\]

View File

@ -1,10 +0,0 @@
idf_component_register(
SRCS
"ena-binary-export.c"
INCLUDE_DIRS "."
PRIV_REQUIRES
ena
nanopb
EMBED_FILES
"test/export.bin"
)

View File

@ -1,90 +0,0 @@
#include <string.h>
#include <time.h>
#include <limits.h>
#include "esp_err.h"
#include "esp_log.h"
#include "ena-exposure.h"
#include "pb_decode.h"
#include "TemporaryExposureKeyExport.pb.h"
static const char kFileHeader[] = "EK Export v1 ";
static size_t kFileHeaderSize = sizeof(kFileHeader) - 1;
bool ena_binary_export_decode_key_data(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint8_t *key_data = (uint8_t *)*arg;
if (!pb_read(stream, key_data, stream->bytes_left))
{
ESP_LOGW(ENA_EXPOSURE_LOG, "Decoding failed: %s\n", PB_GET_ERROR(stream));
return false;
}
return true;
}
bool ena_binary_export_decode_key(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint8_t key_data[ENA_KEY_LENGTH] = {0};
TemporaryExposureKey tek = TemporaryExposureKey_init_zero;
tek.key_data = (pb_callback_t){
.funcs.decode = ena_binary_export_decode_key_data,
.arg = &key_data,
};
if (!pb_decode(stream, TemporaryExposureKey_fields, &tek))
{
ESP_LOGW(ENA_EXPOSURE_LOG, "Decoding failed: %s\n", PB_GET_ERROR(stream));
return false;
}
ESP_LOGD(ENA_EXPOSURE_LOG,
"check reported tek: rolling_start_interval_number %d, rolling_period %d, days_since_last_exposure %d, report_type %d, transmission_risk_values %d",
tek.rolling_start_interval_number, tek.rolling_period, tek.days_since_onset_of_symptoms, tek.report_type, tek.transmission_risk_level);
ESP_LOG_BUFFER_HEXDUMP(ENA_EXPOSURE_LOG, &key_data, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
ena_temporary_exposure_key_t temporary_exposure_key = {
.transmission_risk_level = tek.transmission_risk_level,
.rolling_start_interval_number = tek.rolling_start_interval_number,
.rolling_period = tek.rolling_period,
.report_type = tek.report_type,
.days_since_onset_of_symptoms = tek.days_since_onset_of_symptoms,
};
memcpy(temporary_exposure_key.key_data, key_data, ENA_KEY_LENGTH);
ena_exposure_check_temporary_exposure_key(temporary_exposure_key);
return true;
}
esp_err_t ena_binary_export_check_export(uint8_t *buf, size_t size)
{
// validate header
if (memcmp(kFileHeader, buf, kFileHeaderSize) != 0)
{
ESP_LOGW(ENA_EXPOSURE_LOG, "Wrong or missing header!");
return ESP_FAIL;
}
TemporaryExposureKeyExport tek_export = TemporaryExposureKeyExport_init_zero;
tek_export.keys = (pb_callback_t){
.funcs.decode = ena_binary_export_decode_key,
};
pb_istream_t stream = pb_istream_from_buffer(&buf[kFileHeaderSize], (size - kFileHeaderSize));
if (!pb_decode(&stream, TemporaryExposureKeyExport_fields, &tek_export))
{
ESP_LOGW(ENA_EXPOSURE_LOG, "Decoding failed: %s\n", PB_GET_ERROR(&stream));
return ESP_FAIL;
}
return ESP_OK;
}

View File

@ -1,37 +0,0 @@
// Copyright 2020 Lukas Haubaum
//
// Licensed under the GNU Affero General Public License, Version 3;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.gnu.org/licenses/agpl-3.0.html
//
// 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.
/**
* @file
*
* @brief decode Exposure Key export and run exposure check
*
*/
#ifndef _ena_BINARY_H_
#define _ena_BINARY_H_
#include <stdio.h>
#include "esp_err.h"
/**
* @brief reads a Temporary Exposue Key Export binary and check for exposures
*
* @param[in] buf the buffer containing the binary data
* @param[in] size the size of the buffer
*
* @return
* esp_err_t status of reading binary
*/
esp_err_t ena_binary_export_check_export(uint8_t *buf, size_t size);
#endif

View File

@ -1,33 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFwDCCBKigAwIBAgIIfjnHrR3Z8EMwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV
BAYTAkRFMSswKQYDVQQKDCJULVN5c3RlbXMgRW50ZXJwcmlzZSBTZXJ2aWNlcyBH
bWJIMR8wHQYDVQQLDBZULVN5c3RlbXMgVHJ1c3QgQ2VudGVyMSUwIwYDVQQDDBxU
LVRlbGVTZWMgR2xvYmFsUm9vdCBDbGFzcyAyMB4XDTE0MDIxMTE0MzkxMFoXDTI0
MDIxMTIzNTk1OVowgd8xCzAJBgNVBAYTAkRFMSUwIwYDVQQKDBxULVN5c3RlbXMg
SW50ZXJuYXRpb25hbCBHbWJIMR8wHQYDVQQLDBZULVN5c3RlbXMgVHJ1c3QgQ2Vu
dGVyMRwwGgYDVQQIDBNOb3JkcmhlaW4gV2VzdGZhbGVuMQ4wDAYDVQQRDAU1NzI1
MDEQMA4GA1UEBwwHTmV0cGhlbjEgMB4GA1UECQwXVW50ZXJlIEluZHVzdHJpZXN0
ci4gMjAxJjAkBgNVBAMMHVRlbGVTZWMgU2VydmVyUGFzcyBDbGFzcyAyIENBMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3oxwJVY3bSb6ejJ42f9VEt1N
vW2swwllcs5ifPsHAulpSoFc2Y9gMOKQqkuyjN1foCegDDeEr6FBLD5YuROldcX8
2aDNBKDh9GpSJYZMLrYwlfR4EJUGwLidHDn93H95j1M67sNlCyCfcbso0zFBQzXK
KO06sbC1QH9M1Xdrltz8bQS+LbGRTM5JcPYhhxXcnsFstQVaGmfqFQitPXhT3g9+
8Fbob6taSylFVk1E89G2N0NrrtIVTnaD0PcWF8AdMyX34zIoQAXMezyGV2kqst/Q
Ghvzd09jjMT6f8Q8pAlyGFTGuxsEjeU/rrS/yKU8bFEEvuR5WT/I4Kme+8OlzQID
AQABo4IB2TCCAdUwEgYDVR0TAQH/BAgwBgEB/wIBADBDBgNVHSAEPDA6MDgGBFUd
IAAwMDAuBggrBgEFBQcCARYiaHR0cDovL3BraS50ZWxlc2VjLmRlL2Nwcy9jcHMu
aHRtbDAOBgNVHQ8BAf8EBAMCAQYwge8GA1UdHwSB5zCB5DA1oDOgMYYvaHR0cDov
L3BraS50ZWxlc2VjLmRlL3JsL0dsb2JhbFJvb3RfQ2xhc3NfMi5jcmwwgaqggaeg
gaSGgaFsZGFwOi8vcGtpLnRlbGVzZWMuZGUvQ049VC1UZWxlU2VjJTIwR2xvYmFs
Um9vdCUyMENsYXNzJTIwMixPVT1ULVN5c3RlbXMlMjBUcnVzdCUyMENlbnRlcixP
PVQtU3lzdGVtcyUyMEVudGVycHJpc2UlMjBTZXJ2aWNlcyUyMEdtYkgsQz1ERT9B
dXRob3JpdHlSZXZvY2F0aW9uTGlzdDA4BggrBgEFBQcBAQQsMCowKAYIKwYBBQUH
MAGGHGh0dHA6Ly9vY3NwLnRlbGVzZWMuZGUvb2NzcHIwHQYDVR0OBBYEFJTIdEb1
OrRGSCb4K8o0HlYmBBIAMB8GA1UdIwQYMBaAFL9ZIDYAeaCgImuM1fJh0rgsy4JK
MA0GCSqGSIb3DQEBCwUAA4IBAQB55S9CfCkclWVtUIxl2c4aM5wqlLZRZ7zVhynK
KOhWKyTw+D2BOjc+TXIPkgRMqF3Sn8ZD4UTOARboJxswYnLZDkvBuvTbYa+N52Jy
oBP2TXIpEWEyJl7Oq8NFbERwg4X6MabLgjGvJETicPpKGfAINKDwPScQCsWHiCaX
X50cZzmWw17S0rWECOvPEt/4tXJ4Me9aAxx6WRm708n/K8O4mB3AzvA/M7VUDaP9
8LtreoTnWInjyg/8+Ahtce3foMXiIP4+9IX7fbm6yqh4u33tqMESDcRP6eGdzq4D
qnHyIvj9XNpuGgMvDgq357kZQS9e5XVH5icSvW1kr2kX2t1f
-----END CERTIFICATE-----

View File

@ -1,128 +0,0 @@
// Copyright 2020 Lukas Haubaum
//
// Licensed under the GNU Affero General Public License, Version 3;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.gnu.org/licenses/agpl-3.0.html
//
// 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.
#include <string.h>
#include "time.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_http_client.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ena-storage.h"
#include "ena-exposure.h"
#include "ena-binary-export.h"
#include "wifi-controller.h"
#include "ena-cwa.h"
extern uint8_t export_bin_start[] asm("_binary_export_bin_start"); // test data from Google or https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date/2020-07-22
extern uint8_t export_bin_end[] asm("_binary_export_bin_end");
extern const uint8_t telekom_pem_start[] asm("_binary_telekom_pem_start");
extern const uint8_t telekom__pem_end[] asm("_binary_telekom_pem_end");
esp_err_t ena_cwa_http_event_handler(esp_http_client_event_t *evt)
{
static char *output_buffer;
static int output_len;
switch (evt->event_id)
{
case HTTP_EVENT_ON_DATA:
if (!esp_http_client_is_chunked_response(evt->client))
{
if (output_buffer == NULL)
{
output_buffer = (char *)malloc(esp_http_client_get_content_length(evt->client));
output_len = 0;
if (output_buffer == NULL)
{
ESP_LOGE(ENA_CWA_LOG, "Failed to allocate memory for output buffer, memory: %d kB", (xPortGetFreeHeapSize() / 1024));
return ESP_FAIL;
}
}
memcpy(output_buffer + output_len, evt->data, evt->data_len);
output_len += evt->data_len;
}
break;
case HTTP_EVENT_ON_FINISH:
if (output_buffer != NULL)
{
ESP_LOGD(ENA_CWA_LOG, "memory: %d kB", (xPortGetFreeHeapSize() / 1024));
free(output_buffer);
output_buffer = NULL;
output_len = 0;
ESP_ERROR_CHECK_WITHOUT_ABORT(ena_binary_export_check_export(export_bin_start, (export_bin_end - export_bin_start)));
}
else
{
return ESP_FAIL;
}
break;
default:
break;
}
return ESP_OK;
}
esp_err_t ena_cwa_receive_keys(char *date_string)
{
char *url = malloc(strlen(ENA_CWA_KEYFILES_URL) + strlen(date_string));
sprintf(url, ENA_CWA_KEYFILES_URL, date_string);
esp_http_client_config_t config = {
.url = url,
.cert_pem = (char *)telekom_pem_start,
.event_handler = ena_cwa_http_event_handler,
};
ESP_LOGD(ENA_CWA_LOG, "start memory: %d kB, %s", (xPortGetFreeHeapSize() / 1024), date_string);
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK)
{
int content_length = esp_http_client_get_content_length(client);
ESP_LOGD(ENA_CWA_LOG, "Url = %s, Status = %d, content_length = %d", url,
esp_http_client_get_status_code(client),
content_length);
}
free(url);
esp_http_client_close(client);
esp_http_client_cleanup(client);
return err;
}
void ena_cwa_run(void)
{
static time_t current_time = 0;
static uint32_t last_check = 0;
current_time = time(NULL);
last_check = ena_storage_read_last_exposure_date();
if ((((uint32_t)current_time) - last_check) / (60 * 60 * 24) > 0 && wifi_controller_connection() != NULL)
{
char date_string[11];
struct tm *time_info;
time_info = localtime(&current_time);
time_info->tm_mday--;
strftime(date_string, 11, "%Y-%m-%d", time_info);
esp_err_t err = ena_cwa_receive_keys(date_string);
if (err == ESP_OK)
{
ena_storage_write_last_exposure_date((uint32_t)current_time);
ena_exposure_summary(ena_exposure_default_config());
}
}
}

View File

@ -1,43 +0,0 @@
// Copyright 2020 Lukas Haubaum
//
// Licensed under the GNU Affero General Public License, Version 3;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.gnu.org/licenses/agpl-3.0.html
//
// 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.
/**
* @file
*
* @brief connection to german Exposure Notification App (Corona Warn App)
*
* This is for receiving the Exposure Key export for germany and for report own infection inside germany.
*
*/
#ifndef _ena_CWA_H_
#define _ena_CWA_H_
#include "esp_err.h"
#define ENA_CWA_LOG "ESP-ENA-cwa" // TAG for Logging
#define ENA_CWA_KEYFILES_URL "https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date/%s"
/**
* @brief fetch key export for given date
*
* @param[in] date_string the date to fetch the data for
*/
esp_err_t ena_cwa_receive_keys(char *date_string);
/**
* @brief start ena CWA
*/
void ena_cwa_run(void);
#endif

View File

@ -1,12 +1,11 @@
idf_component_register(
SRCS
"ena-cwa.c"
"ena-eke-proxy.c"
INCLUDE_DIRS "."
PRIV_REQUIRES
esp_http_client
ena
ena-binary-export
wifi-controller
EMBED_FILES
"certs/telekom.pem"
"certs/cert.pem"
)

View File

@ -0,0 +1,51 @@
menu "ENA Exposue Key Export Proxy"
config ENA_EKE_PROXY_KEYFILES_DAILY_URL
string "Url to fetch daily keys"
default "https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys/country/DE/date/%s?page=%u&size=%u"
help
Defines the url to fetch keys. Datestring of ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT (%s), page (%u) and size (%u) are passed as paramter. (Default https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys/country/DE/date/%s?page=%u&size=%u)
config ENA_EKE_PROXY_KEYFILES_HOURLY
bool "Also fetch keys hourly"
default true
help
If enabled, the hourly keys will be fetched for current day.
config ENA_EKE_PROXY_KEYFILES_HOURLY_URL
string "Url to fetch daily keys"
depends on ENA_EKE_PROXY_KEYFILES_HOURLY
default "https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys/country/DE/date/%s/hour/%u?page=%u&size=%u"
help
Defines the url to fetch keys. Datestring of ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT (%s), hour (%u), page (%u) and size (%u) are passed as paramter. (Default https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys/country/DE/date/%s/hour/%u?page=%u&size=%u)
config ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT
string "Format of date to fetch"
default "%Y-%m-%d"
help
Defines the Datestring (%s) for setting the date in fetch url. (Default %Y-%m-%d)
config ENA_EKE_PROXY_KEYFILES_UPLOAD_URL
string "Url to upload keys"
default "https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys"
help
Defines the url to upload keys to. (Default https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys)
config ENA_EKE_PROXY_KEY_LIMIT
int "Limit of keys to receive"
default 500
help
Defines the limit of keys to receive in one request from server. (Default 500)
config ENA_EKE_PROXY_MAX_PAST_DAYS
int "Max. days to retrieve keys"
default 14
help
Defines the maximum number of days to receive missed keys from server. (Default 14)
config ENA_EKE_PROXY_AUTHORIZATION
string "Authorization Header value (PanTra)"
help
Defines the value to put in Authorization header during key upload. (For PanTra key server)
endmenu

View File

@ -0,0 +1,401 @@
// Copyright 2020 Lukas Haubaum
//
// Licensed under the GNU Affero General Public License, Version 3;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.gnu.org/licenses/agpl-3.0.html
//
// 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.
#include <string.h>
#include "time.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_http_client.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ena-crypto.h"
#include "ena-storage.h"
#include "ena-exposure.h"
#include "wifi-controller.h"
#include "ena-eke-proxy.h"
#define HOUR_IN_SECONDS (60 * 60)
#define DAY_IN_SECONDS (HOUR_IN_SECONDS * 24)
extern const uint8_t cert_pem_start[] asm("_binary_cert_pem_start");
extern const uint8_t cert_pem_end[] asm("_binary_cert_pem_end");
static size_t current_page = 0;
static time_t request_sleep = 0;
static uint32_t request_sleep_waiting = 30;
static time_t last_check = 0;
static bool wait_for_request = false;
esp_err_t ena_eke_proxy_fetch_event_handler(esp_http_client_event_t *evt)
{
static uint8_t *output_buffer;
static int output_len;
switch (evt->event_id)
{
case HTTP_EVENT_ON_DATA:
if (!esp_http_client_is_chunked_response(evt->client))
{
if (output_buffer == NULL)
{
output_buffer = malloc(esp_http_client_get_content_length(evt->client));
output_len = 0;
if (output_buffer == NULL)
{
ESP_LOGE(ENA_EKE_PROXY_LOG, "Failed to allocate memory for output buffer, memory: %d kB", (xPortGetFreeHeapSize() / 1024));
return ESP_FAIL;
}
}
memcpy(output_buffer + output_len, evt->data, evt->data_len);
output_len += evt->data_len;
}
break;
case HTTP_EVENT_ON_FINISH:
if (output_buffer != NULL && esp_http_client_get_status_code(evt->client) == 200)
{
if (output_len % 28 != 0)
{
ESP_LOGW(ENA_EKE_PROXY_LOG, "Response length does not match key size! %d", output_len);
}
size_t temporary_exposure_keys = (output_len / 28);
if (temporary_exposure_keys > 0)
{
uint32_t start_time = (uint32_t)time(NULL);
ena_temporary_exposure_key_t temporary_exposure_key;
int tek_index = 0;
memcpy(&(temporary_exposure_key.key_data), &output_buffer[tek_index * 28], ENA_KEY_LENGTH);
memcpy(&(temporary_exposure_key.rolling_start_interval_number), &output_buffer[tek_index * 28 + ENA_KEY_LENGTH], 4);
memcpy(&(temporary_exposure_key.rolling_period), &output_buffer[tek_index * 28 + ENA_KEY_LENGTH + 4], 4);
memcpy(&(temporary_exposure_key.days_since_onset_of_symptoms), &output_buffer[tek_index * 28 + ENA_KEY_LENGTH + 8], 4);
uint32_t timestamp_start = temporary_exposure_key.rolling_start_interval_number * ENA_TIME_WINDOW;
int min = ena_expore_check_find_min(timestamp_start);
tek_index = temporary_exposure_keys - 1;
memcpy(&(temporary_exposure_key.key_data), &output_buffer[tek_index * 28], ENA_KEY_LENGTH);
memcpy(&(temporary_exposure_key.rolling_start_interval_number), &output_buffer[tek_index * 28 + ENA_KEY_LENGTH], 4);
memcpy(&(temporary_exposure_key.rolling_period), &output_buffer[tek_index * 28 + ENA_KEY_LENGTH + 4], 4);
memcpy(&(temporary_exposure_key.days_since_onset_of_symptoms), &output_buffer[tek_index * 28 + ENA_KEY_LENGTH + 8], 4);
uint32_t timestamp_end = (temporary_exposure_key.rolling_start_interval_number + temporary_exposure_key.rolling_period) * ENA_TIME_WINDOW;
int max = ena_expore_check_find_max(timestamp_end);
if (min >= 0 && max >= 0 && min <= max)
{
ESP_LOGI(ENA_EKE_PROXY_LOG, "start check with beacons [%d,%d] for [%u,%u]", min, max, timestamp_start, timestamp_end);
ena_beacon_t beacon;
for (int y = min; y <= max; y++)
{
ena_storage_get_beacon(y, &beacon);
for (int i = 0; i < temporary_exposure_keys; i++)
{
memcpy(&(temporary_exposure_key.key_data), &output_buffer[i * 28], ENA_KEY_LENGTH);
memcpy(&(temporary_exposure_key.rolling_start_interval_number), &output_buffer[i * 28 + ENA_KEY_LENGTH], 4);
memcpy(&(temporary_exposure_key.rolling_period), &output_buffer[i * 28 + ENA_KEY_LENGTH + 4], 4);
memcpy(&(temporary_exposure_key.days_since_onset_of_symptoms), &output_buffer[i * 28 + ENA_KEY_LENGTH + 8], 4);
#ifdef DEBUG_ENA_EKE_PROXY
ESP_LOGD(ENA_EKE_PROXY_LOG, "key payload: ");
ESP_LOG_BUFFER_HEXDUMP(ENA_EKE_PROXY_LOG, &output_buffer[i * 28], 28, ESP_LOG_DEBUG);
ESP_LOGD(ENA_EKE_PROXY_LOG, "received key: ");
ESP_LOG_BUFFER_HEXDUMP(ENA_EKE_PROXY_LOG, &(temporary_exposure_key.key_data), ENA_KEY_LENGTH, ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEXDUMP(ENA_EKE_PROXY_LOG, &output_buffer[i * 28], ENA_KEY_LENGTH, ESP_LOG_DEBUG);
ESP_LOGD(ENA_EKE_PROXY_LOG, "rolling_start_interval_number %u", temporary_exposure_key.rolling_start_interval_number);
ESP_LOG_BUFFER_HEXDUMP(ENA_EKE_PROXY_LOG, &output_buffer[i * 28 + ENA_KEY_LENGTH], 4, ESP_LOG_DEBUG);
ESP_LOGD(ENA_EKE_PROXY_LOG, "rolling_period %u", temporary_exposure_key.rolling_period);
ESP_LOG_BUFFER_HEXDUMP(ENA_EKE_PROXY_LOG, &output_buffer[i * 28 + ENA_KEY_LENGTH + 4], 4, ESP_LOG_DEBUG);
ESP_LOGD(ENA_EKE_PROXY_LOG, "days_since_onset_of_symptoms %u", temporary_exposure_key.days_since_onset_of_symptoms);
ESP_LOG_BUFFER_HEXDUMP(ENA_EKE_PROXY_LOG, &output_buffer[i * 28 + ENA_KEY_LENGTH + 8], 4, ESP_LOG_DEBUG);
#endif
ena_exposure_check(beacon, temporary_exposure_key);
}
}
uint32_t end_time = (uint32_t)time(NULL);
ESP_LOGI(ENA_EKE_PROXY_LOG, "check took %u seconds", (end_time - start_time));
}
else
{
ESP_LOGD(ENA_EKE_PROXY_LOG, "no matching beacons for [%u,%u]", timestamp_start, timestamp_end);
}
}
else
{
ESP_LOGW(ENA_EKE_PROXY_LOG, "no keys in request, should not happen on 200 status!");
}
current_page = current_page + 1;
free(output_buffer);
}
else if (esp_http_client_get_status_code(evt->client) == 204)
{
// finished!
if (difftime(time(NULL), last_check) >= DAY_IN_SECONDS)
{
last_check = last_check + DAY_IN_SECONDS;
}
else
{
last_check = last_check + HOUR_IN_SECONDS;
}
ena_storage_write_last_exposure_date(last_check);
current_page = 0;
request_sleep = 0;
request_sleep_waiting = 30;
ena_exposure_summary(ena_exposure_default_config());
ena_exposure_summary_t *current_summary = ena_exposure_current_summary();
ESP_LOGD(ENA_EKE_PROXY_LOG, "current summary\nlast update: %u\ndays_since_last_exposure: %d\nnum_exposures: %d\nmax_risk_score: %d\nrisk_score_sum: %d",
current_summary->last_update,
current_summary->days_since_last_exposure,
current_summary->num_exposures,
current_summary->max_risk_score,
current_summary->risk_score_sum);
}
else
{
current_page = 0;
request_sleep = time(NULL) + request_sleep_waiting;
if (request_sleep_waiting < HOUR_IN_SECONDS)
{
request_sleep_waiting = request_sleep_waiting * 3;
}
}
output_buffer = NULL;
output_len = 0;
wait_for_request = false;
break;
default:
break;
}
return ESP_OK;
}
esp_err_t ena_eke_proxy_receive_keys(char *url)
{
static int retries = 0;
wait_for_request = true;
esp_http_client_config_t config = {
.url = url,
.timeout_ms = 30000,
.event_handler = ena_eke_proxy_fetch_event_handler,
};
if (memcmp(url, "https", 5) == 0)
{
config.cert_pem = (char *)cert_pem_start;
}
ESP_LOGD(ENA_EKE_PROXY_LOG, "start request: url = %s | memory: %d kB", url, (xPortGetFreeHeapSize() / 1024));
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK)
{
int content_length = esp_http_client_get_content_length(client);
ESP_LOGD(ENA_EKE_PROXY_LOG, "finished request: url = %s, status = %d, content_length = %d | memory: %d kB", url,
esp_http_client_get_status_code(client),
content_length, (xPortGetFreeHeapSize() / 1024));
retries = 0;
}
free(url);
esp_http_client_close(client);
esp_http_client_cleanup(client);
if (err != ESP_OK && retries < 6)
{
retries = retries + 1;
ESP_LOGD(ENA_EKE_PROXY_LOG, "retry %d for url = %s", retries, url);
return ena_eke_proxy_receive_keys(url);
}
return err;
}
esp_err_t ena_eke_proxy_receive_daily_keys(char *date_string, size_t page, size_t size)
{
char *url = malloc(strlen(ENA_EKE_PROXY_KEYFILES_DAILY_URL) + strlen(date_string) + 16);
sprintf(url, ENA_EKE_PROXY_KEYFILES_DAILY_URL, date_string, page, size);
return ena_eke_proxy_receive_keys(url);
}
esp_err_t ena_eke_proxy_receive_hourly_keys(char *date_string, uint8_t hour, size_t page, size_t size)
{
char *url = malloc(strlen(ENA_EKE_PROXY_KEYFILES_HOURLY_URL) + strlen(date_string) + 24);
sprintf(url, ENA_EKE_PROXY_KEYFILES_HOURLY_URL, date_string, hour, page, size);
return ena_eke_proxy_receive_keys(url);
}
void ena_eke_proxy_run(void)
{
static time_t current_time = 0;
static struct tm current_tm;
static struct tm last_check_tm;
static double check_diff = 0;
static time_t wifi_reconnect = 0;
static uint32_t wifi_reconnect_waiting = 15;
current_time = time(NULL);
last_check = (time_t)ena_storage_read_last_exposure_date();
check_diff = difftime(current_time, last_check);
if (check_diff > HOUR_IN_SECONDS && !wait_for_request && current_time > request_sleep)
{
if (wifi_controller_connection() == NULL && current_time > wifi_reconnect && wifi_reconnect_waiting < 86400)
{
wifi_controller_reconnect(NULL);
wifi_reconnect = current_time + wifi_reconnect_waiting;
wifi_reconnect_waiting = wifi_reconnect_waiting * 4;
}
else if (wifi_controller_connection() != NULL)
{
wifi_reconnect = 0;
wifi_reconnect_waiting = 15;
int current_day_offset = check_diff / DAY_IN_SECONDS;
if (current_day_offset > ENA_EKE_PROXY_MAX_PAST_DAYS)
{
current_day_offset = ENA_EKE_PROXY_MAX_PAST_DAYS;
last_check = (current_time - (DAY_IN_SECONDS * current_day_offset));
}
memcpy(&current_tm, gmtime(&current_time), sizeof current_tm);
memcpy(&last_check_tm, gmtime(&last_check), sizeof last_check_tm);
if (current_day_offset > 0 || current_tm.tm_mday > last_check_tm.tm_mday || current_tm.tm_mon > last_check_tm.tm_mon)
{
last_check_tm.tm_hour = 0;
if (current_day_offset <= 0)
{
current_day_offset = 1;
}
}
last_check_tm.tm_min = 0;
last_check_tm.tm_sec = 0;
last_check = mktime(&last_check_tm);
esp_err_t err;
char date_string[11];
strftime(date_string, 11, ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT, &last_check_tm);
if (current_day_offset == 0 && ENA_EKE_PROXY_KEYFILES_HOURLY)
{
ESP_LOGD(ENA_EKE_PROXY_LOG, "eke-proxy request for /%s/hour/%d?page=%d&size=%d : %d kB, ", date_string, last_check_tm.tm_hour, current_page, ENA_EKE_PROXY_DEFAULT_LIMIT, (xPortGetFreeHeapSize() / 1024));
err = ena_eke_proxy_receive_hourly_keys(date_string, last_check_tm.tm_hour, current_page, ENA_EKE_PROXY_DEFAULT_LIMIT);
}
else
{
ESP_LOGD(ENA_EKE_PROXY_LOG, "eke-proxy request for /%s?page=%d&size=%d : %d kB, ", date_string, current_page, ENA_EKE_PROXY_DEFAULT_LIMIT, (xPortGetFreeHeapSize() / 1024));
err = ena_eke_proxy_receive_daily_keys(date_string, current_page, ENA_EKE_PROXY_DEFAULT_LIMIT);
}
if (err != ESP_OK)
{
ESP_LOGD(ENA_EKE_PROXY_LOG, "error eke-proxy /%s/%u %d, ", date_string, last_check_tm.tm_hour, (xPortGetFreeHeapSize() / 1024));
}
}
}
}
esp_err_t ena_eke_proxy_fetch_upload_handler(esp_http_client_event_t *evt)
{
switch (evt->event_id)
{
case HTTP_EVENT_ON_DATA:
break;
case HTTP_EVENT_ON_FINISH:
break;
default:
break;
}
return ESP_OK;
}
esp_err_t ena_eke_proxy_upload(char *token, uint32_t days_since_onset_of_symptoms)
{
ESP_LOGD(ENA_EKE_PROXY_LOG, "try to upload keys:");
esp_http_client_config_t config = {
.url = ENA_EKE_PROXY_KEYFILES_UPLOAD_URL,
.timeout_ms = 30000,
.event_handler = ena_eke_proxy_fetch_upload_handler,
.method = HTTP_METHOD_POST,
};
if (memcmp(ENA_EKE_PROXY_KEYFILES_UPLOAD_URL, "https", 5) == 0)
{
config.cert_pem = (char *)cert_pem_start;
}
uint32_t tek_count = ena_storage_tek_count();
uint32_t teks_to_send = 0;
char *output_buffer = malloc(tek_count * 28);
ena_tek_t tek;
for (int i = 0; i < tek_count; i++)
{
ena_storage_get_tek(i, &tek);
if (((((uint32_t)time(NULL)) - (tek.enin * ENA_TIME_WINDOW)) / DAY_IN_SECONDS) < ENA_STORAGE_TEK_MAX)
{
memcpy(&output_buffer[teks_to_send * 28], &(tek.key_data), ENA_KEY_LENGTH);
memcpy(&output_buffer[teks_to_send * 28 + ENA_KEY_LENGTH], &(tek.enin), 4);
uint32_t rolling_period = tek.rolling_period;
memcpy(&output_buffer[teks_to_send * 28 + ENA_KEY_LENGTH + 4], &rolling_period, 4);
memcpy(&output_buffer[teks_to_send * 28 + ENA_KEY_LENGTH + 8], &days_since_onset_of_symptoms, 4);
teks_to_send++;
}
}
ESP_LOG_BUFFER_HEXDUMP(ENA_EKE_PROXY_LOG, output_buffer, teks_to_send * 28, ESP_LOG_DEBUG);
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_http_client_set_header(client, "Authorization", token);
esp_http_client_set_header(client, "Content-Type", "application/binary");
esp_http_client_set_header(client, "Accept", "application/octet-stream");
esp_http_client_set_post_field(client, output_buffer, teks_to_send * 28);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK)
{
int content_length = esp_http_client_get_content_length(client);
int status = esp_http_client_get_status_code(client);
if (status == 200)
{
ESP_LOGI(ENA_EKE_PROXY_LOG, "successfully uploaded keys : url = %s, status = %d, content_length = %d", ENA_EKE_PROXY_KEYFILES_UPLOAD_URL, status, content_length);
err = ESP_OK;
}
else
{
ESP_LOGW(ENA_EKE_PROXY_LOG, "failed to upload keys : url = %s, status = %d, content_length = %d", ENA_EKE_PROXY_KEYFILES_UPLOAD_URL, status, content_length);
err = ESP_FAIL;
}
}
else
{
ESP_LOGW(ENA_EKE_PROXY_LOG, "Keyupload failed!");
}
free(output_buffer);
esp_http_client_close(client);
esp_http_client_cleanup(client);
return err;
}

View File

@ -0,0 +1,77 @@
// Copyright 2020 Lukas Haubaum
//
// Licensed under the GNU Affero General Public License, Version 3;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.gnu.org/licenses/agpl-3.0.html
//
// 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.
/**
* @file
*
* @brief connection to an Exposure Key export proxy
*
* This is for receiving the Exposure Key export from a compatible proxy server and upload own infected keys to it.
*
*/
#ifndef _ena_EKE_PROXY_H_
#define _ena_EKE_PROXY_H_
#include "esp_err.h"
#include "ena-crypto.h"
#define ENA_EKE_PROXY_LOG "ESP-ENA-eke-proxy" // TAG for Logging
#define ENA_EKE_PROXY_KEYFILES_DAILY_URL CONFIG_ENA_EKE_PROXY_KEYFILES_DAILY_URL
#define ENA_EKE_PROXY_KEYFILES_HOURLY CONFIG_ENA_EKE_PROXY_KEYFILES_HOURLY
#define ENA_EKE_PROXY_KEYFILES_HOURLY_URL CONFIG_ENA_EKE_PROXY_KEYFILES_HOURLY_URL
#define ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT CONFIG_ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT
#define ENA_EKE_PROXY_KEYFILES_UPLOAD_URL CONFIG_ENA_EKE_PROXY_KEYFILES_UPLOAD_URL
#define ENA_EKE_PROXY_DEFAULT_LIMIT CONFIG_ENA_EKE_PROXY_KEY_LIMIT
#define ENA_EKE_PROXY_MAX_PAST_DAYS CONFIG_ENA_EKE_PROXY_MAX_PAST_DAYS // ENA_STORAGE_TEK_MAX
/**
* @brief fetch key export from given url
*
* @param[in] url the url to fetch the data from
*/
esp_err_t ena_eke_proxy_receive_keys(char *url);
/**
* @brief fetch key export for given date with limit and offset
*
* @param[in] date_string the date to fetch the data for
* @param[in] page the page to request
* @param[in] size the size of a page
*/
esp_err_t ena_eke_proxy_receive_daily_keys(char *date_string, size_t page, size_t size);
/**
* @brief fetch key export for given date with limit and offset
*
* @param[in] date_string the date to fetch the data for
* @param[in] hour the hour to fetch the data for
* @param[in] page the page to request
* @param[in] size the size of a page
*/
esp_err_t ena_eke_proxy_receive_hourly_keys(char *date_string, uint8_t hour, size_t page, size_t size);
/**
* @brief run ena eke proxy
*/
void ena_eke_proxy_run(void);
/**
* @brief Upload own keys to server
*
* @param[in] token token for authentication
* @param[in] days_since_onset_of_symptomstoken days after test/symptoms?
*/
esp_err_t ena_eke_proxy_upload(char *token, uint32_t days_since_onset_of_symptoms);
#endif

View File

@ -7,6 +7,12 @@ menu "Exposure Notification API"
help
Dump storage (stored TEKs, temp. beacons and perm. beacons) to serial output after scan.
config ENA_RESET_LAST_CHECK
bool "Reset last exposure check"
default false
help
Resets the last exposure check date on start.
config ENA_STORAGE_TEK_MAX
int "Max. TEKs"
default 14
@ -91,4 +97,5 @@ menu "Exposure Notification API"
Defines the TEK rolling period in 10 minute steps. (Default 144 => 24 hours)
endmenu
endmenu

View File

@ -243,6 +243,7 @@ void ena_exposure_summary(ena_exposure_config_t *config)
}
params.duration = exposure_info.duration_minutes;
params.attenuation = exposure_info.typical_attenuation;
params.report_type = exposure_info.report_type;
int score = ena_exposure_risk_score(config, params);
if (score > current_summary->max_risk_score)
{
@ -270,20 +271,13 @@ ena_exposure_config_t *ena_exposure_default_config(void)
void ena_exposure_check(ena_beacon_t beacon, ena_temporary_exposure_key_t temporary_exposure_key)
{
uint32_t timestamp_day_start = temporary_exposure_key.rolling_start_interval_number * ENA_TIME_WINDOW;
uint32_t timestamp_day_end = temporary_exposure_key.rolling_start_interval_number * ENA_TIME_WINDOW + temporary_exposure_key.rolling_period * ENA_TIME_WINDOW;
uint32_t timestamp_day_end = (temporary_exposure_key.rolling_start_interval_number + temporary_exposure_key.rolling_period) * ENA_TIME_WINDOW;
if (beacon.timestamp_first > timestamp_day_start && beacon.timestamp_last < timestamp_day_end)
{
ESP_LOGD(ENA_EXPOSURE_LOG, "matched timestamps!");
ESP_LOGD(ENA_EXPOSURE_LOG, "Beacon: %u,%u,%d", beacon.timestamp_first, beacon.timestamp_last, beacon.rssi);
ESP_LOG_BUFFER_HEXDUMP(ENA_EXPOSURE_LOG, beacon.rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEXDUMP(ENA_EXPOSURE_LOG, beacon.aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
ESP_LOGD(ENA_EXPOSURE_LOG, "Key: %u,%u,%d", timestamp_day_start, timestamp_day_end, temporary_exposure_key.rolling_period);
ESP_LOG_BUFFER_HEXDUMP(ENA_EXPOSURE_LOG, temporary_exposure_key.key_data, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
bool match = false;
ena_exposure_information_t exposure_info;
exposure_info.day = timestamp_day_start;
exposure_info.duration_minutes = 0;
exposure_info.min_attenuation = INT_MAX;
exposure_info.typical_attenuation = 0;
@ -298,8 +292,7 @@ void ena_exposure_check(ena_beacon_t beacon, ena_temporary_exposure_key_t tempor
if (memcmp(beacon.rpi, rpi, sizeof(ENA_KEY_LENGTH)) == 0)
{
match = true;
exposure_info.day = timestamp_day_start;
exposure_info.duration_minutes += (ENA_BEACON_TRESHOLD / 60);
exposure_info.duration_minutes += ((beacon.timestamp_last - beacon.timestamp_first) / 60);
exposure_info.typical_attenuation = (exposure_info.typical_attenuation + beacon.rssi) / 2;
if (beacon.rssi < exposure_info.min_attenuation)
{
@ -315,11 +308,90 @@ void ena_exposure_check(ena_beacon_t beacon, ena_temporary_exposure_key_t tempor
}
}
int ena_expore_check_find_min_rec(int min, int max, uint32_t timestamp)
{
if (min < 0)
{
return -1;
}
if (min >= max - 1)
{
return min;
}
ena_beacon_t beacon;
int mid = min + (max - min) / 2;
ena_storage_get_beacon(min, &beacon);
if (beacon.timestamp_first < timestamp)
{
return ena_expore_check_find_min_rec(mid, max, timestamp);
}
else if (beacon.timestamp_first > timestamp)
{
return ena_expore_check_find_min_rec(min - ((max - min) / 2), mid, timestamp);
}
else
{
return min;
}
}
int ena_expore_check_find_min(uint32_t timestamp)
{
return ena_expore_check_find_min_rec(0, (ena_storage_beacons_count() - 1), timestamp);
}
int ena_expore_check_find_max_rec(int min, int max, int max_max, uint32_t timestamp)
{
if (max > max_max)
{
return -1;
}
if (max <= (min + 1))
{
return max;
}
ena_beacon_t beacon;
int mid = min + (max - min) / 2;
ena_storage_get_beacon(max, &beacon);
if (beacon.timestamp_first > timestamp)
{
return ena_expore_check_find_max_rec(min, mid, max_max, timestamp);
}
else if (beacon.timestamp_first < timestamp)
{
return ena_expore_check_find_max_rec(mid, max + ((max - min) / 2), max_max, timestamp);
}
else
{
return max;
}
}
int ena_expore_check_find_max(uint32_t timestamp)
{
return ena_expore_check_find_max_rec(0, (ena_storage_beacons_count() - 1), (ena_storage_beacons_count() - 1), timestamp);
}
void ena_exposure_check_temporary_exposure_key(ena_temporary_exposure_key_t temporary_exposure_key)
{
uint32_t timestamp_start = temporary_exposure_key.rolling_start_interval_number * ENA_TIME_WINDOW;
uint32_t timestamp_end = (temporary_exposure_key.rolling_start_interval_number + temporary_exposure_key.rolling_period) * ENA_TIME_WINDOW;
int min = ena_expore_check_find_min(timestamp_start);
int max = ena_expore_check_find_max(timestamp_end);
if (min == -1 || max == -1)
{
return;
}
ena_beacon_t beacon;
uint32_t beacons_count = ena_storage_beacons_count();
for (int y = 0; y < beacons_count; y++)
for (int y = min; y <= max; y++)
{
ena_storage_get_beacon(y, &beacon);
ena_exposure_check(beacon, temporary_exposure_key);

View File

@ -179,10 +179,17 @@ void ena_storage_write_last_exposure_date(uint32_t timestamp)
ena_storage_write(ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS, &timestamp, sizeof(uint32_t));
}
uint32_t ena_storage_tek_count(void)
{
uint32_t count = 0;
ena_storage_read(ENA_STORAGE_TEK_COUNT_ADDRESS, &count, sizeof(uint32_t));
ESP_LOGD(ENA_STORAGE_LOG, "read TEK count: %u", count);
return count;
}
uint32_t ena_storage_read_last_tek(ena_tek_t *tek)
{
uint32_t tek_count = 0;
ena_storage_read(ENA_STORAGE_TEK_COUNT_ADDRESS, &tek_count, sizeof(uint32_t));
uint32_t tek_count = ena_storage_tek_count();
if (tek_count < 1)
{
return 0;
@ -195,6 +202,13 @@ uint32_t ena_storage_read_last_tek(ena_tek_t *tek)
return tek_count;
}
void ena_storage_get_tek(uint32_t index, ena_tek_t *tek)
{
ena_storage_read(ENA_STORAGE_TEK_START_ADDRESS + index * sizeof(ena_tek_t), tek, sizeof(ena_tek_t));
ESP_LOGD(ENA_STORAGE_LOG, "read %d tek %u:", index, tek->enin);
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, tek->key_data, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
}
void ena_storage_write_tek(ena_tek_t *tek)
{
uint32_t tek_count = 0;

View File

@ -91,6 +91,10 @@ void ena_start(void)
ena_storage_erase_all();
#endif
#ifdef CONFIG_ENA_RESET_LAST_CHECK
ena_storage_write_last_exposure_date(0);
#endif
if (ena_storage_read_last_exposure_date() == 0xFFFFFFFF)
{
ena_storage_erase_all();

View File

@ -239,6 +239,20 @@ ena_exposure_config_t *ena_exposure_default_config(void);
*/
void ena_exposure_check(ena_beacon_t beacon, ena_temporary_exposure_key_t temporary_exposure_key);
/**
* @brief find minimal key index of beacons for a certain timestamp
*
* @param[in] timestamp the timestamp to check against
*/
int ena_expore_check_find_min(uint32_t timestamp);
/**
* @brief find maximum key index of beacons for a certain timestamp
*
* @param[in] timestamp the timestamp to check against
*/
int ena_expore_check_find_max(uint32_t timestamp);
/**
* @brief reads Temporary Exposue Key and check for exposures with all beacons
*

View File

@ -113,6 +113,14 @@ uint32_t ena_storage_read_last_exposure_date(void);
*/
void ena_storage_write_last_exposure_date(uint32_t timestamp);
/**
* @brief get number of stored TEKs
*
* @return
* total number of TEKs stored
*/
uint32_t ena_storage_tek_count(void);
/**
* @brief get last stored TEK
*
@ -123,6 +131,15 @@ void ena_storage_write_last_exposure_date(uint32_t timestamp);
*/
uint32_t ena_storage_read_last_tek(ena_tek_t *tek);
/**
* @brief get stored TEK at given index
*
* @param[in] index the index of the TEK to read
* @param[out] tek pointer to write TEK to
*
*/
void ena_storage_get_tek(uint32_t index, ena_tek_t *tek);
/**
* @brief store given TEK
*

View File

@ -0,0 +1,15 @@
menu "I²C"
config I2C_SDA_PIN
int "I²C sda pin"
default 22
config I2C_SCL_PIN
int "I²C scl pin"
default 23
config I2C_CLOCKSPEED
int "I²C clock speed"
default 1000000
endmenu

View File

@ -20,9 +20,9 @@
#ifndef _i2c_main_H_
#define _i2c_main_H_
#define I2C_SDA_PIN (23)
#define I2C_SCL_PIN (22)
#define I2C_CLK_SPEED (1000000)
#define I2C_SDA_PIN (CONFIG_I2C_SDA_PIN)
#define I2C_SCL_PIN (CONFIG_I2C_SCL_PIN)
#define I2C_CLK_SPEED (CONFIG_I2C_CLOCKSPEED)
/**
* @brief initialize main I2C interface

View File

@ -4,6 +4,7 @@ idf_component_register(
"interface-main.c"
"interface-data.c"
"interface-datetime.c"
"interface-info.c"
"interface-input.c"
"interface-label.c"
"interface-report.c"
@ -12,6 +13,7 @@ idf_component_register(
INCLUDE_DIRS "."
PRIV_REQUIRES
ena
ena-eke-proxy
display
display-ssd1306
rtc

View File

@ -91,12 +91,12 @@ void interface_data_rst(void)
void interface_data_lft(void)
{
interface_wifi_start();
interface_datetime_start();
}
void interface_data_rht(void)
{
interface_settings_start();
interface_info_start();
}
void interface_data_mid(void)
@ -190,6 +190,7 @@ void interface_data_start(void)
interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_data_up);
interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_data_dwn);
interface_set_display_function(&interface_data_display);
interface_set_display_refresh_function(NULL);
ESP_LOGD(INTERFACE_LOG, "start delete data interface");
}

View File

@ -49,12 +49,12 @@ void interface_datetime_set(void)
void interface_datetime_lft(void)
{
interface_settings_start();
interface_wifi_start();
}
void interface_datetime_rht(void)
{
interface_wifi_start();
interface_data_start();
}
void interface_datetime_mid(void)
@ -106,7 +106,8 @@ void interface_datetime_display(void)
display_clear_line( edit_line + 1, false);
time(&current_timstamp);
current_tm = localtime(&current_timstamp);
current_tm = gmtime(&current_timstamp);
current_tm->tm_hour = current_tm->tm_hour + (interface_get_timezone_offset()) % 24;
strftime(time_buffer, 16, INTERFACE_FORMAT_TIME, current_tm);
display_text_line_column( time_buffer, 3, 4, false);
@ -168,6 +169,7 @@ void interface_datetime_start(void)
interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_datetime_dwn);
interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_datetime_set);
interface_register_command_callback(INTERFACE_COMMAND_RST, NULL);
interface_set_display_function(&interface_datetime_display);
interface_set_display_function(NULL);
interface_set_display_refresh_function(&interface_datetime_display);
ESP_LOGD(INTERFACE_LOG, "start datetime interface");
}

View File

@ -0,0 +1,111 @@
// Copyright 2020 Lukas Haubaum
//
// Licensed under the GNU Affero General Public License, Version 3;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.gnu.org/licenses/agpl-3.0.html
//
// 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.
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "display.h"
#include "display-gfx.h"
#include "ena-storage.h"
#include "ena-exposure.h"
#include "interface.h"
void interface_info_set(void)
{
interface_main_start();
}
void interface_info_rst(void)
{
}
void interface_info_lft(void)
{
interface_data_start();
}
void interface_info_rht(void)
{
interface_settings_start();
}
void interface_info_mid(void)
{
}
void interface_info_up(void)
{
}
void interface_info_dwn(void)
{
}
void interface_info_display(void)
{
char data_chars[10];
display_menu_headline(interface_get_label_text(&interface_text_headline_info), true, 0);
display_text_line_column(interface_get_label_text(&interface_text_info_num_keys), 2, 1, false);
sprintf(data_chars, "%u", ena_storage_beacons_count());
display_text_line_column(data_chars, 2, 9, false);
ena_exposure_summary_t *current_exposure_summary = ena_exposure_current_summary();
display_text_line_column(interface_get_label_text(&interface_text_info_exp_days), 4, 1, false);
sprintf(data_chars, "%d", current_exposure_summary->days_since_last_exposure);
if (current_exposure_summary->days_since_last_exposure < 0)
{
display_text_line_column("/", 4, 12, false);
}
else
{
display_text_line_column(data_chars, 4, 12, false);
}
display_text_line_column(interface_get_label_text(&interface_text_info_exp_num), 5, 1, false);
sprintf(data_chars, "%d", current_exposure_summary->num_exposures);
display_text_line_column(data_chars, 5, 12, false);
display_text_line_column(interface_get_label_text(&interface_text_info_exp_max), 6, 1, false);
sprintf(data_chars, "%d", current_exposure_summary->max_risk_score);
display_text_line_column(data_chars, 6, 12, false);
display_text_line_column(interface_get_label_text(&interface_text_info_exp_sum), 7, 1, false);
sprintf(data_chars, "%d", current_exposure_summary->risk_score_sum);
display_text_line_column(data_chars, 7, 12, false);
}
void interface_info_start(void)
{
interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_info_rst);
interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_info_set);
interface_register_command_callback(INTERFACE_COMMAND_LFT, &interface_info_lft);
interface_register_command_callback(INTERFACE_COMMAND_RHT, &interface_info_rht);
interface_register_command_callback(INTERFACE_COMMAND_MID, &interface_info_mid);
interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_info_up);
interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_info_dwn);
interface_set_display_function(&interface_info_display);
interface_set_display_refresh_function(NULL);
ESP_LOGD(INTERFACE_LOG, "start info interface");
}

View File

@ -154,6 +154,7 @@ void interface_input_mid(void)
current_text[current_cursor] = current_char_set[current_char_index];
printf("current_char_set: %d %s\n", strlen(current_char_set), current_char_set);
}
void interface_input_up(void)
{
if (current_char_index == 0)
@ -314,5 +315,6 @@ void interface_input(interface_text_callback callback_rst, interface_text_callba
interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_input_dwn);
interface_set_display_function(&interface_input_display);
interface_set_display_refresh_function(NULL);
ESP_LOGD(INTERFACE_LOG, "start input interface");
}

View File

@ -27,23 +27,36 @@ void interface_init_label(void)
// EN
interface_text_button_cancel.text[EN] = "CANCEL";
interface_text_button_ok.text[EN] = "OK";
interface_text_button_back.text[EN] = "BACK";
interface_text_button_menu.text[EN] = "MENU";
interface_text_button_report.text[EN] = "REPORT";
interface_text_headline_tan.text[EN] = "ENTER TAN";
interface_text_headline_report.text[EN] = "REPORT";
interface_text_headline_wifi.text[EN] = "WIFI";
interface_text_headline_time.text[EN] = "TIME/DATE";
interface_text_headline_data.text[EN] = "DEL DATA";
interface_text_headline_settings.text[EN] = "SETTING";
interface_text_headline_info.text[EN] = "INFO";
interface_text_headline_debug.text[EN] = "DEBUG";
interface_text_wifi_scanning.text[EN] = "Scanning...";
interface_text_settings_locale.text[EN] = "Language:";
interface_text_settings_locales[EN].text[EN] = "EN";
interface_text_settings_locales[DE].text[EN] = "DE";
interface_text_settings_timezone.text[EN] = "Timezone:";
interface_text_settings_timezone.text[EN] = "UTC:";
interface_text_wifi_scanning.text[EN] = "Scanning...";
interface_text_info_num_keys.text[EN] = "Seen:";
interface_text_info_exp_days.text[EN] = "Last Exp:";
interface_text_info_exp_num.text[EN] = "Num Exp:";
interface_text_info_exp_max.text[EN] = "Score:";
interface_text_info_exp_sum.text[EN] = "Scores:";
interface_text_report_pending.text[EN] = "Uploading...";
interface_text_report_success.text[EN] = "Upload succeed!";
interface_text_report_fail.text[EN] = "Upload failed!";
interface_text_data_del[0].text[EN] = "DEL TEK";
interface_text_data_del[1].text[EN] = "DEL Exp Info";
@ -75,26 +88,40 @@ void interface_init_label(void)
// DE
interface_text_button_cancel.text[DE] = "ZURÜCK";
interface_text_button_back.text[DE] = "ZURÜCK";
interface_text_button_ok.text[DE] = "OK";
interface_text_button_menu.text[DE] = "MENU";
interface_text_button_report.text[DE] = "MELDEN";
interface_text_headline_tan.text[DE] = "TAN EING.";
interface_text_headline_report.text[DE] = "MELDEN";
interface_text_headline_wifi.text[DE] = "WLAN";
interface_text_headline_time.text[DE] = "ZEIT/DATUM";
interface_text_headline_data.text[DE] = "DATEN ENTF";
interface_text_headline_settings.text[DE] = "EINSTEL.";
interface_text_headline_info.text[DE] = "INFOS";
interface_text_headline_debug.text[DE] = "DEBUG";
interface_text_wifi_scanning.text[DE] = "Scannen...";
interface_text_settings_locale.text[DE] = "Sprache:";
interface_text_settings_locales[EN].text[DE] = "EN";
interface_text_settings_locales[DE].text[DE] = "DE";
interface_text_settings_timezone.text[DE] = "Zeitzone:";
interface_text_settings_timezone.text[DE] = "GMT:";
interface_text_wifi_scanning.text[DE] = "Scannen...";
interface_text_info_num_keys.text[DE] = "Gesehen:";
interface_text_info_exp_days.text[DE] = "letz. Exp:";
interface_text_info_exp_num.text[DE] = "Anz. Exp:";
interface_text_info_exp_max.text[DE] = "Score:";
interface_text_info_exp_sum.text[DE] = "Scores:";
interface_text_data_del[0].text[DE] = "ENTF TEK";
interface_text_report_pending.text[DE] = "Hochladen...";
interface_text_report_success.text[DE] = "Erfolgreich!";
interface_text_report_fail.text[DE] = "Fehlgeschlagen!";
interface_text_data_del[0]
.text[DE] = "ENTF TEK";
interface_text_data_del[1].text[DE] = "ENTF Exp Info";
interface_text_data_del[2].text[DE] = "ENTF Tmp RPI";
interface_text_data_del[3].text[DE] = "ENTF RPI";

View File

@ -30,11 +30,10 @@ static time_t current_timstamp;
static struct tm *current_tm;
static char time_buffer[32];
static char text_buffer[32];
static bool wifi_connected;
void interface_main_set(void)
{
interface_datetime_start();
interface_info_start();
}
void interface_main_rst(void)
@ -44,56 +43,10 @@ void interface_main_rst(void)
void interface_main_display(void)
{
if (wifi_connected)
{
display_data( display_gfx_wifi, 8, 0, 0, false);
}
else
{
display_data( display_gfx_cross, 8, 0, 0, false);
}
time(&current_timstamp);
current_tm = localtime(&current_timstamp);
// curent date
sprintf(text_buffer, "%s %s %d",
interface_get_label_text(&interface_texts_weekday[current_tm->tm_wday]),
interface_get_label_text(&interface_texts_month[current_tm->tm_mon]),
current_tm->tm_mday);
display_text_line_column( text_buffer, 0, 16 - strlen(text_buffer), false);
// current time
strftime(time_buffer, 16, INTERFACE_FORMAT_TIME, current_tm);
display_text_line_column( time_buffer, 1, 16 - strlen(time_buffer), false);
}
void interface_main_start(void)
{
interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_main_rst);
interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_main_set);
interface_register_command_callback(INTERFACE_COMMAND_LFT, NULL);
interface_register_command_callback(INTERFACE_COMMAND_RHT, NULL);
interface_register_command_callback(INTERFACE_COMMAND_MID, NULL);
interface_register_command_callback(INTERFACE_COMMAND_UP, NULL);
interface_register_command_callback(INTERFACE_COMMAND_DWN, NULL);
if (wifi_controller_connection() != NULL)
{
wifi_connected = true;
}
else
{
wifi_connected = false;
}
interface_set_display_function(&interface_main_display);
ena_exposure_summary_t *current_exposure_summary = ena_exposure_current_summary();
time(&current_timstamp);
uint32_t last_update = ena_storage_read_last_exposure_date();
uint32_t last_update = current_exposure_summary->last_update;
// status unknown if no update or last update older than two days
if (last_update == 0 || ((current_timstamp - last_update) / (60 * 60 * 24)) > 2)
@ -121,7 +74,9 @@ void interface_main_start(void)
display_data(display_gfx_clock, 8, 4, 8, false);
// last update
struct tm *last_update_tm = localtime((time_t*) &last_update);
struct tm *last_update_tm = gmtime((time_t *)&last_update);
last_update_tm->tm_hour = last_update_tm->tm_hour + (interface_get_timezone_offset()) % 24;
sprintf(time_buffer, "%02d %s %02d:%02d",
last_update_tm->tm_mday,
@ -137,6 +92,50 @@ void interface_main_start(void)
// buttons
display_set_button(interface_get_label_text(&interface_text_button_menu), true, false);
display_set_button(interface_get_label_text(&interface_text_button_report), false, true);
}
void interface_main_display_refresh(void)
{
if (wifi_controller_connection() != NULL)
{
display_data(display_gfx_wifi, 8, 0, 0, false);
}
else
{
display_data(display_gfx_cross, 8, 0, 0, false);
}
time(&current_timstamp);
current_tm = gmtime(&current_timstamp);
current_tm->tm_hour = current_tm->tm_hour + (interface_get_timezone_offset()) % 24;
// curent date
sprintf(text_buffer, "%s %s %d",
interface_get_label_text(&interface_texts_weekday[current_tm->tm_wday]),
interface_get_label_text(&interface_texts_month[current_tm->tm_mon]),
current_tm->tm_mday);
display_text_line_column(text_buffer, 0, 16 - strlen(text_buffer), false);
// current time
strftime(time_buffer, 16, INTERFACE_FORMAT_TIME, current_tm);
display_text_line_column(time_buffer, 1, 16 - strlen(time_buffer), false);
}
void interface_main_start(void)
{
interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_main_rst);
interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_main_set);
interface_register_command_callback(INTERFACE_COMMAND_LFT, NULL);
interface_register_command_callback(INTERFACE_COMMAND_RHT, NULL);
interface_register_command_callback(INTERFACE_COMMAND_MID, NULL);
interface_register_command_callback(INTERFACE_COMMAND_UP, NULL);
interface_register_command_callback(INTERFACE_COMMAND_DWN, NULL);
interface_set_display_function(&interface_main_display);
interface_set_display_refresh_function(&interface_main_display_refresh);
interface_main_display();
ESP_LOGD(INTERFACE_LOG, "start main interface");
}

View File

@ -24,74 +24,42 @@
#include "wifi-controller.h"
#include "ena-eke-proxy.h"
#include "interface.h"
static int interface_report_tan[10] = {5, 5, 5, 5, 5, 5, 5, 5, 5, 5};
static int interface_report_tan_index = 0;
static char current_tan[10];
static uint8_t current_cursor;
static char current_char_set[32];
static uint8_t current_char_index;
static char char_set_uppercase[32];
static char char_set_numeric[32];
static uint8_t current_max_index;
void interface_report_set(void)
typedef enum
{
interface_main_start();
}
INTERFACE_REPORT_STATUS_NONE = 0,
INTERFACE_REPORT_STATUS_PENDING,
INTERFACE_REPORT_STATUS_SUCCESS,
INTERFACE_REPORT_STATUS_FAIL
} report_status_e;
void interface_report_rst(void)
{
// TODO: REPORT here, check tan etc.
}
void interface_report_lft(void)
{
if (interface_report_tan_index > 0)
{
interface_report_tan_index--;
}
ESP_LOGD(INTERFACE_LOG, "tan index %d", interface_report_tan_index);
}
void interface_report_rht(void)
{
if (interface_report_tan_index < 9)
{
interface_report_tan_index++;
}
ESP_LOGD(INTERFACE_LOG, "tan index %d", interface_report_tan_index);
}
void interface_report_up(void)
{
if (interface_report_tan[interface_report_tan_index] > 0)
{
interface_report_tan[interface_report_tan_index]--;
}
else
{
interface_report_tan[interface_report_tan_index] = 9;
}
}
void interface_report_dwn(void)
{
if (interface_report_tan[interface_report_tan_index] < 9)
{
interface_report_tan[interface_report_tan_index]++;
}
else
{
interface_report_tan[interface_report_tan_index] = 0;
}
}
static int current_report_status = INTERFACE_REPORT_STATUS_NONE;
void interface_report_display(void)
{
display_clear();
if (current_report_status == INTERFACE_REPORT_STATUS_NONE)
{
display_menu_headline(interface_get_label_text(&interface_text_headline_tan), false, 0);
// buttons
display_set_button(interface_get_label_text(&interface_text_button_cancel), true, false);
if (current_cursor == 9)
{
display_set_button(interface_get_label_text(&interface_text_button_ok), false, true);
}
static char tan_buffer[10] = {0};
if (interface_report_tan_index > 0)
if (current_cursor > 0)
{
display_data(display_gfx_arrow_left, 8, 3, 8, false);
}
@ -100,7 +68,7 @@ void interface_report_display(void)
display_data(display_gfx_clear, 8, 3, 8, false);
}
if (interface_report_tan_index < 9)
if (current_cursor < 9)
{
display_data(display_gfx_arrow_right, 8, 3, 112, false);
}
@ -109,11 +77,6 @@ void interface_report_display(void)
display_data(display_gfx_clear, 8, 3, 112, false);
}
for (int i = 0; i < interface_report_tan_index + 1; i++)
{
sprintf(&tan_buffer[i], "%d", interface_report_tan[i]);
}
display_clear_line(2, false);
display_clear_line(4, false);
@ -126,52 +89,225 @@ void interface_report_display(void)
int offset = 2;
for (int i = 0; i < 3; i++)
{
if (i < interface_report_tan_index)
if (i < current_cursor)
{
display_chars( &tan_buffer[i], 1, 3, i + offset, true);
display_chars(&current_tan[i], 1, 3, i + offset, true);
}
}
if (interface_report_tan_index > 2)
if (current_cursor > 2)
{
offset = 3;
for (int i = 3; i < 6; i++)
{
if (i < interface_report_tan_index)
if (i < current_cursor)
{
display_chars( &tan_buffer[i], 1, 3, i + offset, true);
display_chars(&current_tan[i], 1, 3, i + offset, true);
}
}
}
if (interface_report_tan_index > 5)
if (current_cursor > 5)
{
offset = 4;
for (int i = 6; i < 10; i++)
{
if (i < interface_report_tan_index)
if (i < current_cursor)
{
display_chars( &tan_buffer[i], 1, 3, i + offset, true);
display_chars(&current_tan[i], 1, 3, i + offset, true);
}
}
}
display_data( display_gfx_arrow_up, 8, 2, interface_report_tan_index * 8 + offset * 8, false);
display_data( display_gfx_arrow_down, 8, 4, interface_report_tan_index * 8 + offset * 8, false);
display_data(display_gfx_arrow_up, 8, 2, current_cursor * 8 + offset * 8, false);
display_data(display_gfx_arrow_down, 8, 4, current_cursor * 8 + offset * 8, false);
display_chars( &tan_buffer[interface_report_tan_index], 1, 3, interface_report_tan_index + offset, false);
display_chars(&current_tan[current_cursor], 1, 3, current_cursor + offset, false);
}
else if (current_report_status == INTERFACE_REPORT_STATUS_PENDING)
{
display_menu_headline(interface_get_label_text(&interface_text_headline_report), false, 0);
display_text_line_column(interface_get_label_text(&interface_text_report_pending), 4, 1, false);
}
else if (current_report_status == INTERFACE_REPORT_STATUS_SUCCESS)
{
display_menu_headline(interface_get_label_text(&interface_text_headline_report), false, 0);
display_text_line_column(interface_get_label_text(&interface_text_report_success), 3, 1, false);
display_set_button(interface_get_label_text(&interface_text_button_ok), false, true);
}
else if (current_report_status == INTERFACE_REPORT_STATUS_FAIL)
{
display_menu_headline(interface_get_label_text(&interface_text_headline_report), false, 0);
display_text_line_column(interface_get_label_text(&interface_text_report_fail), 3, 1, false);
display_set_button(interface_get_label_text(&interface_text_button_back), true, false);
display_set_button(interface_get_label_text(&interface_text_button_ok), false, true);
}
}
void interface_report_set_char_set(void)
{
char *ret;
char cur_char = current_tan[current_cursor];
if ((ret = strchr(char_set_uppercase, cur_char)) != NULL)
{
strcpy(current_char_set, char_set_uppercase);
current_char_index = strlen(current_char_set) - strlen(ret);
}
else if ((ret = strchr(char_set_numeric, cur_char)) != NULL)
{
strcpy(current_char_set, char_set_numeric);
current_char_index = strlen(current_char_set) - strlen(ret);
}
}
void interface_report_set(void)
{
if (current_report_status == INTERFACE_REPORT_STATUS_NONE)
{
interface_main_start();
}
else if (current_report_status == INTERFACE_REPORT_STATUS_FAIL)
{
current_report_status = INTERFACE_REPORT_STATUS_NONE;
}
}
void interface_report_rst(void)
{
if (current_cursor == 9 && current_report_status == INTERFACE_REPORT_STATUS_NONE)
{
current_report_status = INTERFACE_REPORT_STATUS_PENDING;
interface_report_display();
ESP_LOGI(INTERFACE_LOG, "publish tan: %s", current_tan);
esp_err_t err = ena_eke_proxy_upload(current_tan, 0);
if (err == ESP_OK)
{
current_report_status = INTERFACE_REPORT_STATUS_SUCCESS;
}
else
{
current_report_status = INTERFACE_REPORT_STATUS_FAIL;
}
interface_report_display();
}
else if (current_report_status == INTERFACE_REPORT_STATUS_SUCCESS || current_report_status == INTERFACE_REPORT_STATUS_FAIL)
{
interface_main_start();
}
}
void interface_report_mid(void)
{
if (current_report_status == INTERFACE_REPORT_STATUS_NONE)
{
if (current_char_set[0] == char_set_uppercase[0])
{
strcpy(current_char_set, char_set_numeric);
}
else if (current_char_set[0] == char_set_numeric[0])
{
strcpy(current_char_set, char_set_uppercase);
}
current_char_index = 0;
current_tan[current_cursor] = current_char_set[current_char_index];
}
}
void interface_report_lft(void)
{
if (current_report_status == INTERFACE_REPORT_STATUS_NONE)
{
if (current_cursor > 0)
{
current_cursor--;
interface_report_set_char_set();
}
}
}
void interface_report_rht(void)
{
if (current_report_status == INTERFACE_REPORT_STATUS_NONE)
{
if (current_cursor < 9)
{
current_cursor++;
if (current_cursor > current_max_index)
{
current_max_index = current_cursor;
strcpy(current_char_set, char_set_uppercase);
current_char_index = 0;
current_tan[current_cursor] = current_char_set[current_char_index];
}
else
{
interface_report_set_char_set();
}
}
}
}
void interface_report_up(void)
{
if (current_report_status == INTERFACE_REPORT_STATUS_NONE)
{
if (current_char_index == 0)
{
current_char_index = strlen(current_char_set) - 1;
}
else
{
current_char_index--;
}
current_tan[current_cursor] = current_char_set[current_char_index];
}
}
void interface_report_dwn(void)
{
if (current_report_status == INTERFACE_REPORT_STATUS_NONE)
{
if (current_char_index == strlen(current_char_set) - 1)
{
current_char_index = 0;
}
else
{
current_char_index++;
}
current_tan[current_cursor] = current_char_set[current_char_index];
}
}
void interface_report_start(void)
{
display_utf8_to_ascii("ABCDEFGHIJKLMNOPQRSTUVWXYZ", char_set_uppercase);
display_utf8_to_ascii("0123456789", char_set_numeric);
strcpy(current_char_set, char_set_uppercase);
current_report_status = INTERFACE_REPORT_STATUS_NONE;
current_max_index = 0;
current_char_index = 0;
current_cursor = 0;
current_tan[current_cursor] = current_char_set[current_char_index];
interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_report_rst);
interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_report_set);
interface_register_command_callback(INTERFACE_COMMAND_MID, &interface_report_mid);
interface_register_command_callback(INTERFACE_COMMAND_LFT, &interface_report_lft);
interface_register_command_callback(INTERFACE_COMMAND_RHT, &interface_report_rht);
interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_report_up);
interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_report_dwn);
interface_register_command_callback(INTERFACE_COMMAND_MID, NULL);
interface_set_display_function(&interface_report_display);
interface_set_display_refresh_function(NULL);
ESP_LOGD(INTERFACE_LOG, "start report interface");
}

View File

@ -33,6 +33,17 @@ typedef enum
} interface_settings_state_t;
static int current_interface_settings_state;
static int current_timezone_offset = 0;
int interface_get_timezone_offset(void)
{
return current_timezone_offset;
}
void interface_set_timezone_offset(int timezone_offset)
{
current_timezone_offset = timezone_offset;
}
void interface_settings_set(void)
{
@ -45,12 +56,12 @@ void interface_settings_rst(void)
void interface_settings_lft(void)
{
interface_data_start();
interface_info_start();
}
void interface_settings_rht(void)
{
interface_datetime_start();
interface_wifi_start();
}
void interface_settings_mid(void)
@ -83,6 +94,14 @@ void interface_settings_up(void)
interface_set_locale(interface_get_locale() - 1);
}
}
else if (current_interface_settings_state == INTERFACE_SETTINGS_TIMEZONE)
{
current_timezone_offset++;
if (current_timezone_offset > 12)
{
current_timezone_offset = -11;
}
}
}
void interface_settings_dwn(void)
@ -99,6 +118,14 @@ void interface_settings_dwn(void)
interface_set_locale(interface_get_locale() + 1);
}
}
else if (current_interface_settings_state == INTERFACE_SETTINGS_TIMEZONE)
{
current_timezone_offset--;
if (current_timezone_offset < -11)
{
current_timezone_offset = 12;
}
}
}
void interface_settings_display(void)
@ -111,22 +138,52 @@ void interface_settings_display(void)
if (current_interface_settings_state == INTERFACE_SETTINGS_LOCALE)
{
display_data( display_gfx_arrow_up, 8, 2, 12 * 8 + 4, false);
display_data(display_gfx_arrow_up, 8, 2, 11 * 8 + 4, false);
display_text_line_column(
interface_get_label_text(&interface_text_settings_locales[interface_get_locale()]), 3, 12, true);
display_data( display_gfx_arrow_down, 8, 4, 12 * 8 + 4, false);
interface_get_label_text(&interface_text_settings_locales[interface_get_locale()]), 3, 11, true);
display_data(display_gfx_arrow_down, 8, 4, 11 * 8 + 4, false);
}
else
{
display_text_line_column(
interface_get_label_text(&interface_text_settings_locales[interface_get_locale()]), 3, 12, true);
interface_get_label_text(&interface_text_settings_locales[interface_get_locale()]), 3, 11, false);
}
char timezone_char[32];
timezone_char[0] = ' ';
if (current_timezone_offset == 0)
{
timezone_char[0] = ' ';
sprintf(&timezone_char[1], "%d", current_timezone_offset);
timezone_char[2] = ' ';
}
else if (current_timezone_offset > 0)
{
timezone_char[0] = '+';
sprintf(&timezone_char[1], "%d", current_timezone_offset);
if (current_timezone_offset < 10)
{
timezone_char[2] = ' ';
}
}
else if (current_timezone_offset < 0)
{
sprintf(&timezone_char[0], "%d", current_timezone_offset);
if (current_timezone_offset > -10)
{
timezone_char[2] = ' ';
}
}
if (current_interface_settings_state == INTERFACE_SETTINGS_TIMEZONE)
{
display_data( display_gfx_arrow_up, 8, 5, 12 * 8 + 4, false);
display_data( display_gfx_arrow_down, 8, 7, 12 * 8 + 4, false);
display_data(display_gfx_arrow_up, 8, 5, 7 * 8, false);
display_chars(timezone_char, 3, 6, 6, true);
display_data(display_gfx_arrow_down, 8, 7, 7 * 8, false);
}
else
{
display_chars(timezone_char, 3, 6, 6, false);
}
}
@ -142,6 +199,7 @@ void interface_settings_start(void)
interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_settings_up);
interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_settings_dwn);
interface_set_display_function(&interface_settings_display);
interface_set_display_refresh_function(NULL);
ESP_LOGD(INTERFACE_LOG, "start settings interface");
}

View File

@ -45,7 +45,7 @@ void interface_wifi_input_set(char *text, uint8_t cursor)
ESP_LOGD(INTERFACE_LOG, "ssid: '%s' password '%s'", current_wifi_config.sta.ssid, current_wifi_config.sta.password);
if (wifi_controller_connect(current_wifi_config) == ESP_OK)
if (wifi_controller_connect(current_wifi_config, NULL) == ESP_OK)
{
interface_main_start();
}
@ -63,19 +63,18 @@ void interface_wifi_set(void)
void interface_wifi_lft(void)
{
interface_datetime_start();
interface_settings_start();
}
void interface_wifi_rht(void)
{
interface_data_start();
interface_datetime_start();
}
void interface_wifi_mid(void)
{
memset(&current_wifi_config, 0, sizeof(wifi_config_t));
memcpy(current_wifi_config.sta.ssid, ap_info[ap_selected].ssid, strlen((char *)ap_info[ap_selected].ssid));
interface_input(&interface_wifi_input_rst, &interface_wifi_input_set, 64);
interface_input_set_text("muffimuffi");
}
void interface_wifi_up(void)
{
@ -115,7 +114,10 @@ void interface_wifi_dwn(void)
void interface_wifi_display(void)
{
display_menu_headline(interface_get_label_text(&interface_text_headline_wifi), true, 0);
}
void interface_wifi_display_refresh(void)
{
if (ap_count > 0)
{
display_clear_line(2, false);
@ -172,6 +174,8 @@ void interface_wifi_start(void)
interface_register_command_callback(INTERFACE_COMMAND_RST, NULL);
interface_set_display_function(&interface_wifi_display);
interface_set_display_refresh_function(&interface_wifi_display_refresh);
interface_wifi_display();
ESP_LOGD(INTERFACE_LOG, "start wifi interface and scanning");

View File

@ -14,6 +14,7 @@
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <freertos/timers.h>
#include "driver/gpio.h"
#include "esp_log.h"
@ -24,6 +25,10 @@
static interface_command_callback command_callbacks[INTERFACE_COMMANDS_SIZE];
static interface_display_function current_display_function;
static interface_display_function current_display_refresh_function;
static TimerHandle_t interface_idle_timer;
static bool interface_idle = false;
void interface_register_command_callback(interface_command_t command, interface_command_callback callback)
{
@ -36,30 +41,70 @@ void interface_set_display_function(interface_display_function display_function)
current_display_function = display_function;
}
void interface_execute_command(interface_command_t command)
{
if (command_callbacks[command] != NULL)
void interface_set_display_refresh_function(interface_display_function display_function)
{
display_clear();
current_display_refresh_function = display_function;
}
void interface_execute_command(interface_command_t command)
{
if (!interface_idle && command_callbacks[command] != NULL)
{
xTimerReset(interface_idle_timer, 0);
(*command_callbacks[command])();
if (current_display_function != NULL || current_display_refresh_function != NULL)
{
display_clear();
if (current_display_refresh_function != NULL)
{
vTaskDelay(10 / portTICK_PERIOD_MS);
(*current_display_refresh_function)();
}
if (current_display_function != NULL)
{
vTaskDelay(10 / portTICK_PERIOD_MS);
(*current_display_function)();
}
}
}
else if (interface_idle && command == INTERFACE_COMMAND_SET)
{
xTimerReset(interface_idle_timer, 0);
interface_idle = false;
display_on(true);
}
}
void interface_display_task(void *pvParameter)
{
xTimerStart(interface_idle_timer, 0);
while (1)
{
if (current_display_function != NULL)
if (current_display_refresh_function != NULL)
{
(*current_display_function)();
(*current_display_refresh_function)();
}
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void interface_idle_callback(TimerHandle_t timer)
{
display_on(false);
interface_idle = true;
}
void interface_start(void)
{
xTaskCreate(&interface_display_task, "interface_display_task", 4096, NULL, 5, NULL);
interface_idle_timer = xTimerCreate(
"interface_idle",
(15 * 1000) / portTICK_PERIOD_MS,
false,
NULL,
interface_idle_callback);
// init label
interface_init_label();
@ -71,4 +116,6 @@ void interface_start(void)
{
display_data(display_gfx_logo[i], 64, i, 32, false);
}
xTaskCreate(&interface_display_task, "interface_display_task", 4096, NULL, 5, NULL);
}

View File

@ -24,8 +24,6 @@
#define INTERFACE_LOG "INTERFACE" // TAG for Logging
#define INTERFACE_FORMAT_TIME "%X"
#define INTERFACE_NUM_LOCALE 2
@ -64,23 +62,37 @@ typedef struct
// label variables
interface_label_t interface_text_button_cancel;
interface_label_t interface_text_button_ok;
interface_label_t interface_text_button_back;
interface_label_t interface_text_button_menu;
interface_label_t interface_text_button_report;
interface_label_t interface_text_headline_tan;
interface_label_t interface_text_headline_report;
interface_label_t interface_text_headline_wifi;
interface_label_t interface_text_headline_time;
interface_label_t interface_text_headline_data;
interface_label_t interface_text_headline_settings;
interface_label_t interface_text_headline_info;
interface_label_t interface_text_headline_debug;
interface_label_t interface_text_settings_locale;
interface_label_t interface_text_settings_locales[INTERFACE_NUM_LOCALE];
interface_label_t interface_text_settings_timezone;
interface_label_t interface_text_info_num_keys;
interface_label_t interface_text_info_exp_update;
interface_label_t interface_text_info_exp_days;
interface_label_t interface_text_info_exp_num;
interface_label_t interface_text_info_exp_max;
interface_label_t interface_text_info_exp_sum;
interface_label_t interface_text_report_pending;
interface_label_t interface_text_report_success;
interface_label_t interface_text_report_fail;
interface_label_t interface_text_wifi_scanning;
interface_label_t interface_text_data_del[5];
interface_label_t interface_text_data_del[6];
interface_label_t interface_texts_weekday[7];
@ -117,7 +129,7 @@ void interface_init_label(void);
char *interface_get_label_text(interface_label_t *label);
/**
* @brief set locale for interface
* @brief get locale for interface
*
* @return
* interface_locale_t current locale
@ -131,6 +143,21 @@ interface_locale_t interface_get_locale(void);
*/
void interface_set_locale(interface_locale_t locale);
/**
* @brief get timezone offset for interface
*
* @return
* int current timezone offset
*/
int interface_get_timezone_offset(void);
/**
* @brief set timezone offset for interface
*
* @param[in] timezone_offset the timezone offset to set
*/
void interface_set_timezone_offset(int timezone_offset);
/**
* @brief register a callback function for command event
*
@ -153,6 +180,13 @@ void interface_execute_command(interface_command_t command);
*/
void interface_set_display_function(interface_display_function display_function);
/**
* @brief set the display refresh function
*
* @param[in] display_function display function
*/
void interface_set_display_refresh_function(interface_display_function display_function);
/**
* @brief start interface logic
*
@ -190,6 +224,11 @@ void interface_wifi_start(void);
*/
void interface_settings_start(void);
/**
* @brief start info interface
*/
void interface_info_start(void);
/**
* @brief start interface for input
*

View File

@ -1,8 +0,0 @@
idf_component_register(
SRCS
"pb_common.c"
"pb_decode.c"
"pb_encode.c"
"TemporaryExposureKeyExport.pb.c"
INCLUDE_DIRS "."
)

View File

@ -1,20 +0,0 @@
Copyright (c) 2011 Petteri Aimonen <jpa at nanopb.mail.kapsi.fi>
This software is provided 'as-is', without any express or
implied warranty. In no event will the authors be held liable
for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

View File

@ -1,19 +0,0 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.2 */
#include "TemporaryExposureKeyExport.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(TemporaryExposureKeyExport, TemporaryExposureKeyExport, AUTO)
PB_BIND(SignatureInfo, SignatureInfo, AUTO)
PB_BIND(TemporaryExposureKey, TemporaryExposureKey, AUTO)

View File

@ -1,147 +0,0 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.2 */
#ifndef PB_TEMPORARYEXPOSUREKEYEXPORT_PB_H_INCLUDED
#define PB_TEMPORARYEXPOSUREKEYEXPORT_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _TemporaryExposureKey_ReportType {
TemporaryExposureKey_ReportType_UNKNOWN = 0,
TemporaryExposureKey_ReportType_CONFIRMED_TEST = 1,
TemporaryExposureKey_ReportType_CONFIRMED_CLINICAL_DIAGNOSIS = 2,
TemporaryExposureKey_ReportType_SELF_REPORT = 3,
TemporaryExposureKey_ReportType_RECURSIVE = 4,
TemporaryExposureKey_ReportType_REVOKED = 5
} TemporaryExposureKey_ReportType;
/* Struct definitions */
typedef struct _SignatureInfo {
pb_callback_t verification_key_version;
pb_callback_t verification_key_id;
pb_callback_t signature_algorithm;
} SignatureInfo;
typedef struct _TemporaryExposureKey {
pb_callback_t key_data;
bool has_transmission_risk_level;
int32_t transmission_risk_level;
bool has_rolling_start_interval_number;
int32_t rolling_start_interval_number;
bool has_rolling_period;
int32_t rolling_period;
bool has_report_type;
TemporaryExposureKey_ReportType report_type;
bool has_days_since_onset_of_symptoms;
int32_t days_since_onset_of_symptoms;
} TemporaryExposureKey;
typedef struct _TemporaryExposureKeyExport {
bool has_start_timestamp;
uint64_t start_timestamp;
bool has_end_timestamp;
uint64_t end_timestamp;
pb_callback_t region;
bool has_batch_num;
int32_t batch_num;
bool has_batch_size;
int32_t batch_size;
pb_callback_t signature_infos;
pb_callback_t keys;
pb_callback_t revised_keys;
} TemporaryExposureKeyExport;
/* Helper constants for enums */
#define _TemporaryExposureKey_ReportType_MIN TemporaryExposureKey_ReportType_UNKNOWN
#define _TemporaryExposureKey_ReportType_MAX TemporaryExposureKey_ReportType_REVOKED
#define _TemporaryExposureKey_ReportType_ARRAYSIZE ((TemporaryExposureKey_ReportType)(TemporaryExposureKey_ReportType_REVOKED+1))
/* Initializer values for message structs */
#define TemporaryExposureKeyExport_init_default {false, 0, false, 0, {{NULL}, NULL}, false, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
#define SignatureInfo_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
#define TemporaryExposureKey_init_default {{{NULL}, NULL}, false, 0, false, 0, false, 144, false, _TemporaryExposureKey_ReportType_MIN, false, 0}
#define TemporaryExposureKeyExport_init_zero {false, 0, false, 0, {{NULL}, NULL}, false, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
#define SignatureInfo_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
#define TemporaryExposureKey_init_zero {{{NULL}, NULL}, false, 0, false, 0, false, 0, false, _TemporaryExposureKey_ReportType_MIN, false, 0}
/* Field tags (for use in manual encoding/decoding) */
#define SignatureInfo_verification_key_version_tag 3
#define SignatureInfo_verification_key_id_tag 4
#define SignatureInfo_signature_algorithm_tag 5
#define TemporaryExposureKey_key_data_tag 1
#define TemporaryExposureKey_transmission_risk_level_tag 2
#define TemporaryExposureKey_rolling_start_interval_number_tag 3
#define TemporaryExposureKey_rolling_period_tag 4
#define TemporaryExposureKey_report_type_tag 5
#define TemporaryExposureKey_days_since_onset_of_symptoms_tag 6
#define TemporaryExposureKeyExport_start_timestamp_tag 1
#define TemporaryExposureKeyExport_end_timestamp_tag 2
#define TemporaryExposureKeyExport_region_tag 3
#define TemporaryExposureKeyExport_batch_num_tag 4
#define TemporaryExposureKeyExport_batch_size_tag 5
#define TemporaryExposureKeyExport_signature_infos_tag 6
#define TemporaryExposureKeyExport_keys_tag 7
#define TemporaryExposureKeyExport_revised_keys_tag 8
/* Struct field encoding specification for nanopb */
#define TemporaryExposureKeyExport_FIELDLIST(X, a) \
X(a, STATIC, OPTIONAL, FIXED64, start_timestamp, 1) \
X(a, STATIC, OPTIONAL, FIXED64, end_timestamp, 2) \
X(a, CALLBACK, OPTIONAL, STRING, region, 3) \
X(a, STATIC, OPTIONAL, INT32, batch_num, 4) \
X(a, STATIC, OPTIONAL, INT32, batch_size, 5) \
X(a, CALLBACK, REPEATED, MESSAGE, signature_infos, 6) \
X(a, CALLBACK, REPEATED, MESSAGE, keys, 7) \
X(a, CALLBACK, REPEATED, MESSAGE, revised_keys, 8)
#define TemporaryExposureKeyExport_CALLBACK pb_default_field_callback
#define TemporaryExposureKeyExport_DEFAULT NULL
#define TemporaryExposureKeyExport_signature_infos_MSGTYPE SignatureInfo
#define TemporaryExposureKeyExport_keys_MSGTYPE TemporaryExposureKey
#define TemporaryExposureKeyExport_revised_keys_MSGTYPE TemporaryExposureKey
#define SignatureInfo_FIELDLIST(X, a) \
X(a, CALLBACK, OPTIONAL, STRING, verification_key_version, 3) \
X(a, CALLBACK, OPTIONAL, STRING, verification_key_id, 4) \
X(a, CALLBACK, OPTIONAL, STRING, signature_algorithm, 5)
#define SignatureInfo_CALLBACK pb_default_field_callback
#define SignatureInfo_DEFAULT NULL
#define TemporaryExposureKey_FIELDLIST(X, a) \
X(a, CALLBACK, OPTIONAL, BYTES, key_data, 1) \
X(a, STATIC, OPTIONAL, INT32, transmission_risk_level, 2) \
X(a, STATIC, OPTIONAL, INT32, rolling_start_interval_number, 3) \
X(a, STATIC, OPTIONAL, INT32, rolling_period, 4) \
X(a, STATIC, OPTIONAL, UENUM, report_type, 5) \
X(a, STATIC, OPTIONAL, SINT32, days_since_onset_of_symptoms, 6)
#define TemporaryExposureKey_CALLBACK pb_default_field_callback
#define TemporaryExposureKey_DEFAULT (const pb_byte_t*)"\x20\x90\x01\x00"
extern const pb_msgdesc_t TemporaryExposureKeyExport_msg;
extern const pb_msgdesc_t SignatureInfo_msg;
extern const pb_msgdesc_t TemporaryExposureKey_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define TemporaryExposureKeyExport_fields &TemporaryExposureKeyExport_msg
#define SignatureInfo_fields &SignatureInfo_msg
#define TemporaryExposureKey_fields &TemporaryExposureKey_msg
/* Maximum encoded size of messages (where known) */
/* TemporaryExposureKeyExport_size depends on runtime parameters */
/* SignatureInfo_size depends on runtime parameters */
/* TemporaryExposureKey_size depends on runtime parameters */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -1,65 +0,0 @@
syntax = "proto2";
message TemporaryExposureKeyExport {
// Time window of keys in this batch based on arrival to server, in UTC seconds.
optional fixed64 start_timestamp = 1;
optional fixed64 end_timestamp = 2;
// Region for which these keys came from, such as country.
optional string region = 3;
// For example, file 2 in batch size of 10. Ordinal, 1-based numbering.
// Note: Not yet supported on iOS.
optional int32 batch_num = 4;
optional int32 batch_size = 5;
// Information about associated signatures
repeated SignatureInfo signature_infos = 6;
// The TemporaryExposureKeys for initial release of keys.
// Keys should be included in this list for initial release,
// whereas revised or revoked keys should go in revised_keys.
repeated TemporaryExposureKey keys = 7;
// TemporaryExposureKeys that have changed status.
// Keys should be included in this list if they have changed status
// or have been revoked.
repeated TemporaryExposureKey revised_keys = 8;
}
message SignatureInfo {
// The first two fields have been deprecated
reserved 1, 2;
reserved "app_bundle_id", "android_package";
// Key version for rollovers
// Must be in character class [a-zA-Z0-9_]. For example, 'v1'
optional string verification_key_version = 3;
// Alias with which to identify public key to be used for verification
// Must be in character class [a-zA-Z0-9_.]
// For cross-compatibility with Apple, you can use your region's three-digit
// mobile country code (MCC). If your region has more than one MCC, choose the
// one that Apple has configured.
optional string verification_key_id = 4;
// ASN.1 OID for Algorithm Identifier. For example, `1.2.840.10045.4.3.2'
optional string signature_algorithm = 5;
}
message TemporaryExposureKey {
// Key of infected user
optional bytes key_data = 1;
// Varying risk associated with a key depending on diagnosis method
optional int32 transmission_risk_level = 2 [deprecated = true];
// The interval number since epoch for which a key starts
optional int32 rolling_start_interval_number = 3;
// Increments of 10 minutes describing how long a key is valid
optional int32 rolling_period = 4
[default = 144]; // defaults to 24 hours
// Data type representing why this key was published.
enum ReportType {
UNKNOWN = 0; // Never returned by the client API.
CONFIRMED_TEST = 1;
CONFIRMED_CLINICAL_DIAGNOSIS = 2;
SELF_REPORT = 3;
RECURSIVE = 4; // Reserved for future use.
REVOKED = 5; // Used to revoke a key, never returned by client API.
}
// Type of diagnosis associated with a key.
optional ReportType report_type = 5;
// Number of days elapsed between symptom onset and the TEK being used.
// E.g. 2 means TEK is 2 days after onset of symptoms.
optional sint32 days_since_onset_of_symptoms = 6;
}

View File

@ -1,868 +0,0 @@
/* Common parts of the nanopb library. Most of these are quite low-level
* stuff. For the high-level interface, see pb_encode.h and pb_decode.h.
*/
#ifndef PB_H_INCLUDED
#define PB_H_INCLUDED
/*****************************************************************
* Nanopb compilation time options. You can change these here by *
* uncommenting the lines, or on the compiler command line. *
*****************************************************************/
/* Enable support for dynamically allocated fields */
/* #define PB_ENABLE_MALLOC 1 */
/* Define this if your CPU / compiler combination does not support
* unaligned memory access to packed structures. */
/* #define PB_NO_PACKED_STRUCTS 1 */
/* Increase the number of required fields that are tracked.
* A compiler warning will tell if you need this. */
/* #define PB_MAX_REQUIRED_FIELDS 256 */
/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */
/* #define PB_FIELD_32BIT 1 */
/* Disable support for error messages in order to save some code space. */
/* #define PB_NO_ERRMSG 1 */
/* Disable support for custom streams (support only memory buffers). */
/* #define PB_BUFFER_ONLY 1 */
/* Disable support for 64-bit datatypes, for compilers without int64_t
or to save some code space. */
/* #define PB_WITHOUT_64BIT 1 */
/* Don't encode scalar arrays as packed. This is only to be used when
* the decoder on the receiving side cannot process packed scalar arrays.
* Such example is older protobuf.js. */
/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */
/* Enable conversion of doubles to floats for platforms that do not
* support 64-bit doubles. Most commonly AVR. */
/* #define PB_CONVERT_DOUBLE_FLOAT 1 */
/* Check whether incoming strings are valid UTF-8 sequences. Slows down
* the string processing slightly and slightly increases code size. */
/* #define PB_VALIDATE_UTF8 1 */
/******************************************************************
* You usually don't need to change anything below this line. *
* Feel free to look around and use the defined macros, though. *
******************************************************************/
/* Version of the nanopb library. Just in case you want to check it in
* your own program. */
#define NANOPB_VERSION nanopb-0.4.2
/* Include all the system headers needed by nanopb. You will need the
* definitions of the following:
* - strlen, memcpy, memset functions
* - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t
* - size_t
* - bool
*
* If you don't have the standard header files, you can instead provide
* a custom header that defines or includes all this. In that case,
* define PB_SYSTEM_HEADER to the path of this file.
*/
#ifdef PB_SYSTEM_HEADER
#include PB_SYSTEM_HEADER
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#ifdef PB_ENABLE_MALLOC
#include <stdlib.h>
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Macro for defining packed structures (compiler dependent).
* This just reduces memory requirements, but is not required.
*/
#if defined(PB_NO_PACKED_STRUCTS)
/* Disable struct packing */
# define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END
# define pb_packed
#elif defined(__GNUC__) || defined(__clang__)
/* For GCC and clang */
# define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END
# define pb_packed __attribute__((packed))
#elif defined(__ICCARM__) || defined(__CC_ARM)
/* For IAR ARM and Keil MDK-ARM compilers */
# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
# define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
# define pb_packed
#elif defined(_MSC_VER) && (_MSC_VER >= 1500)
/* For Microsoft Visual C++ */
# define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
# define PB_PACKED_STRUCT_END __pragma(pack(pop))
# define pb_packed
#else
/* Unknown compiler */
# define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END
# define pb_packed
#endif
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
#ifndef PB_UNUSED
#define PB_UNUSED(x) (void)(x)
#endif
/* Harvard-architecture processors may need special attributes for storing
* field information in program memory. */
#ifndef PB_PROGMEM
#ifdef __AVR__
#include <avr/pgmspace.h>
#define PB_PROGMEM PROGMEM
#define PB_PROGMEM_READU32(x) pgm_read_dword(&x)
#else
#define PB_PROGMEM
#define PB_PROGMEM_READU32(x) (x)
#endif
#endif
/* Compile-time assertion, used for checking compatible compilation options.
* If this does not work properly on your compiler, use
* #define PB_NO_STATIC_ASSERT to disable it.
*
* But before doing that, check carefully the error message / place where it
* comes from to see if the error has a real cause. Unfortunately the error
* message is not always very clear to read, but you can see the reason better
* in the place where the PB_STATIC_ASSERT macro was called.
*/
#ifndef PB_NO_STATIC_ASSERT
# ifndef PB_STATIC_ASSERT
# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
/* C11 standard _Static_assert mechanism */
# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG);
# else
/* Classic negative-size-array static assert mechanism */
# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER
# endif
# endif
#else
/* Static asserts disabled by PB_NO_STATIC_ASSERT */
# define PB_STATIC_ASSERT(COND,MSG)
#endif
/* Number of required fields to keep track of. */
#ifndef PB_MAX_REQUIRED_FIELDS
#define PB_MAX_REQUIRED_FIELDS 64
#endif
#if PB_MAX_REQUIRED_FIELDS < 64
#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64).
#endif
#ifdef PB_WITHOUT_64BIT
#ifdef PB_CONVERT_DOUBLE_FLOAT
/* Cannot use doubles without 64-bit types */
#undef PB_CONVERT_DOUBLE_FLOAT
#endif
#endif
/* List of possible field types. These are used in the autogenerated code.
* Least-significant 4 bits tell the scalar type
* Most-significant 4 bits specify repeated/required/packed etc.
*/
typedef uint_least8_t pb_type_t;
/**** Field data types ****/
/* Numeric types */
#define PB_LTYPE_BOOL 0x00U /* bool */
#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */
#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */
#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */
#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */
#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */
/* Marker for last packable field type. */
#define PB_LTYPE_LAST_PACKABLE 0x05U
/* Byte array with pre-allocated buffer.
* data_size is the length of the allocated PB_BYTES_ARRAY structure. */
#define PB_LTYPE_BYTES 0x06U
/* String with pre-allocated buffer.
* data_size is the maximum length. */
#define PB_LTYPE_STRING 0x07U
/* Submessage
* submsg_fields is pointer to field descriptions */
#define PB_LTYPE_SUBMESSAGE 0x08U
/* Submessage with pre-decoding callback
* The pre-decoding callback is stored as pb_callback_t right before pSize.
* submsg_fields is pointer to field descriptions */
#define PB_LTYPE_SUBMSG_W_CB 0x09U
/* Extension pseudo-field
* The field contains a pointer to pb_extension_t */
#define PB_LTYPE_EXTENSION 0x0AU
/* Byte array with inline, pre-allocated byffer.
* data_size is the length of the inline, allocated buffer.
* This differs from PB_LTYPE_BYTES by defining the element as
* pb_byte_t[data_size] rather than pb_bytes_array_t. */
#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU
/* Number of declared LTYPES */
#define PB_LTYPES_COUNT 0x0CU
#define PB_LTYPE_MASK 0x0FU
/**** Field repetition rules ****/
#define PB_HTYPE_REQUIRED 0x00U
#define PB_HTYPE_OPTIONAL 0x10U
#define PB_HTYPE_SINGULAR 0x10U
#define PB_HTYPE_REPEATED 0x20U
#define PB_HTYPE_FIXARRAY 0x20U
#define PB_HTYPE_ONEOF 0x30U
#define PB_HTYPE_MASK 0x30U
/**** Field allocation types ****/
#define PB_ATYPE_STATIC 0x00U
#define PB_ATYPE_POINTER 0x80U
#define PB_ATYPE_CALLBACK 0x40U
#define PB_ATYPE_MASK 0xC0U
#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \
PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB)
/* Data type used for storing sizes of struct fields
* and array counts.
*/
#if defined(PB_FIELD_32BIT)
typedef uint32_t pb_size_t;
typedef int32_t pb_ssize_t;
#else
typedef uint_least16_t pb_size_t;
typedef int_least16_t pb_ssize_t;
#endif
#define PB_SIZE_MAX ((pb_size_t)-1)
/* Data type for storing encoded data and other byte streams.
* This typedef exists to support platforms where uint8_t does not exist.
* You can regard it as equivalent on uint8_t on other platforms.
*/
typedef uint_least8_t pb_byte_t;
/* Forward declaration of struct types */
typedef struct pb_istream_s pb_istream_t;
typedef struct pb_ostream_s pb_ostream_t;
typedef struct pb_field_iter_s pb_field_iter_t;
/* This structure is used in auto-generated constants
* to specify struct fields.
*/
PB_PACKED_STRUCT_START
typedef struct pb_msgdesc_s pb_msgdesc_t;
struct pb_msgdesc_s {
pb_size_t field_count;
const uint32_t *field_info;
const pb_msgdesc_t * const * submsg_info;
const pb_byte_t *default_value;
bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field);
} pb_packed;
PB_PACKED_STRUCT_END
/* Iterator for message descriptor */
struct pb_field_iter_s {
const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */
void *message; /* Pointer to start of the structure */
pb_size_t index; /* Index of the field */
pb_size_t field_info_index; /* Index to descriptor->field_info array */
pb_size_t required_field_index; /* Index that counts only the required fields */
pb_size_t submessage_index; /* Index that counts only submessages */
pb_size_t tag; /* Tag of current field */
pb_size_t data_size; /* sizeof() of a single item */
pb_size_t array_size; /* Number of array entries */
pb_type_t type; /* Type of current field */
void *pField; /* Pointer to current field in struct */
void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */
void *pSize; /* Pointer to count/has field */
const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */
};
/* For compatibility with legacy code */
typedef pb_field_iter_t pb_field_t;
/* Make sure that the standard integer types are of the expected sizes.
* Otherwise fixed32/fixed64 fields can break.
*
* If you get errors here, it probably means that your stdint.h is not
* correct for your platform.
*/
#ifndef PB_WITHOUT_64BIT
PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE)
PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE)
#endif
/* This structure is used for 'bytes' arrays.
* It has the number of bytes in the beginning, and after that an array.
* Note that actual structs used will have a different length of bytes array.
*/
#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; }
#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes))
struct pb_bytes_array_s {
pb_size_t size;
pb_byte_t bytes[1];
};
typedef struct pb_bytes_array_s pb_bytes_array_t;
/* This structure is used for giving the callback function.
* It is stored in the message structure and filled in by the method that
* calls pb_decode.
*
* The decoding callback will be given a limited-length stream
* If the wire type was string, the length is the length of the string.
* If the wire type was a varint/fixed32/fixed64, the length is the length
* of the actual value.
* The function may be called multiple times (especially for repeated types,
* but also otherwise if the message happens to contain the field multiple
* times.)
*
* The encoding callback will receive the actual output stream.
* It should write all the data in one call, including the field tag and
* wire type. It can write multiple fields.
*
* The callback can be null if you want to skip a field.
*/
typedef struct pb_callback_s pb_callback_t;
struct pb_callback_s {
/* Callback functions receive a pointer to the arg field.
* You can access the value of the field as *arg, and modify it if needed.
*/
union {
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
} funcs;
/* Free arg for use by callback */
void *arg;
};
extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);
/* Wire types. Library user needs these only in encoder callbacks. */
typedef enum {
PB_WT_VARINT = 0,
PB_WT_64BIT = 1,
PB_WT_STRING = 2,
PB_WT_32BIT = 5
} pb_wire_type_t;
/* Structure for defining the handling of unknown/extension fields.
* Usually the pb_extension_type_t structure is automatically generated,
* while the pb_extension_t structure is created by the user. However,
* if you want to catch all unknown fields, you can also create a custom
* pb_extension_type_t with your own callback.
*/
typedef struct pb_extension_type_s pb_extension_type_t;
typedef struct pb_extension_s pb_extension_t;
struct pb_extension_type_s {
/* Called for each unknown field in the message.
* If you handle the field, read off all of its data and return true.
* If you do not handle the field, do not read anything and return true.
* If you run into an error, return false.
* Set to NULL for default handler.
*/
bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
uint32_t tag, pb_wire_type_t wire_type);
/* Called once after all regular fields have been encoded.
* If you have something to write, do so and return true.
* If you do not have anything to write, just return true.
* If you run into an error, return false.
* Set to NULL for default handler.
*/
bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
/* Free field for use by the callback. */
const void *arg;
};
struct pb_extension_s {
/* Type describing the extension field. Usually you'll initialize
* this to a pointer to the automatically generated structure. */
const pb_extension_type_t *type;
/* Destination for the decoded data. This must match the datatype
* of the extension field. */
void *dest;
/* Pointer to the next extension handler, or NULL.
* If this extension does not match a field, the next handler is
* automatically called. */
pb_extension_t *next;
/* The decoder sets this to true if the extension was found.
* Ignored for encoding. */
bool found;
};
#define pb_extension_init_zero {NULL,NULL,NULL,false}
/* Memory allocation functions to use. You can define pb_realloc and
* pb_free to custom functions if you want. */
#ifdef PB_ENABLE_MALLOC
# ifndef pb_realloc
# define pb_realloc(ptr, size) realloc(ptr, size)
# endif
# ifndef pb_free
# define pb_free(ptr) free(ptr)
# endif
#endif
/* This is used to inform about need to regenerate .pb.h/.pb.c files. */
#define PB_PROTO_HEADER_VERSION 40
/* These macros are used to declare pb_field_t's in the constant array. */
/* Size of a structure member, in bytes. */
#define pb_membersize(st, m) (sizeof ((st*)0)->m)
/* Number of entries in an array. */
#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0]))
/* Delta from start of one member to the start of another member. */
#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2))
/* Force expansion of macro value */
#define PB_EXPAND(x) x
/* Binding of a message field set into a specific structure */
#define PB_BIND(msgname, structname, width) \
const uint32_t structname ## _field_info[] PB_PROGMEM = \
{ \
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \
0 \
}; \
const pb_msgdesc_t* const structname ## _submsg_info[] = \
{ \
msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \
NULL \
}; \
const pb_msgdesc_t structname ## _msg = \
{ \
0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \
structname ## _field_info, \
structname ## _submsg_info, \
msgname ## _DEFAULT, \
msgname ## _CALLBACK, \
}; \
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname)
#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1
/* X-macro for generating the entries in struct_field_info[] array. */
#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
/* X-macro for generating asserts that entries fit in struct_field_info[] array.
* The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(),
* but it is not easily reused because of how macro substitutions work. */
#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname)
#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname)
#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname))
#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname)
#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname)
#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname)
#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname)
#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname)
#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname)
#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0
#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0
#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname))
#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname)
#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname)
#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname)
#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count)
#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0
#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0
#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0
#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname)
#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0
#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname)
#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname)
#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0
#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname)
#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname)
#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1
#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1
#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1
#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1
#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1
#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname)
#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname)
#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0])
#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname)
#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname)
#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname)
#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0])
#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0])
#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple)
#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname
#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername
#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname
#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \
PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname)
#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname))
#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername)
#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE)
#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SI_PB_LTYPE_BOOL(t)
#define PB_SI_PB_LTYPE_BYTES(t)
#define PB_SI_PB_LTYPE_DOUBLE(t)
#define PB_SI_PB_LTYPE_ENUM(t)
#define PB_SI_PB_LTYPE_UENUM(t)
#define PB_SI_PB_LTYPE_FIXED32(t)
#define PB_SI_PB_LTYPE_FIXED64(t)
#define PB_SI_PB_LTYPE_FLOAT(t)
#define PB_SI_PB_LTYPE_INT32(t)
#define PB_SI_PB_LTYPE_INT64(t)
#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t)
#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t)
#define PB_SI_PB_LTYPE_SFIXED32(t)
#define PB_SI_PB_LTYPE_SFIXED64(t)
#define PB_SI_PB_LTYPE_SINT32(t)
#define PB_SI_PB_LTYPE_SINT64(t)
#define PB_SI_PB_LTYPE_STRING(t)
#define PB_SI_PB_LTYPE_UINT32(t)
#define PB_SI_PB_LTYPE_UINT64(t)
#define PB_SI_PB_LTYPE_EXTENSION(t)
#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t)
#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg),
/* The field descriptors use a variable width format, with width of either
* 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always
* encode the descriptor size, 6 lowest bits of field tag number, and 8 bits
* of the field type.
*
* Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words.
*
* Formats, listed starting with the least significant bit of the first word.
* 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size]
*
* 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset]
* [16-bit data_offset] [12-bit data_size] [4-bit tag>>6]
*
* 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size]
* [8-bit size_offset] [24-bit tag>>6]
* [32-bit data_offset]
* [32-bit data_size]
*
* 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved]
* [8-bit size_offset] [24-bit tag>>6]
* [32-bit data_offset]
* [32-bit data_size]
* [32-bit array_size]
* [32-bit reserved]
* [32-bit reserved]
* [32-bit reserved]
*/
#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \
(0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \
(((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)),
#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \
(1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \
(((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)),
#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \
(2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \
(data_offset), (data_size),
#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \
(3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \
(data_offset), (data_size), (array_size), 0, 0, 0,
/* These assertions verify that the field information fits in the allocated space.
* The generator tries to automatically determine the correct width that can fit all
* data associated with a message. These asserts will fail only if there has been a
* problem in the automatic logic - this may be worth reporting as a bug. As a workaround,
* you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting
* descriptorsize option in .options file.
*/
#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<<bits))
#define PB_FIELDINFO_ASSERT_1(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,6) && PB_FITS(data_offset,8) && PB_FITS(size_offset,4) && PB_FITS(data_size,4) && PB_FITS(array_size,1), FIELDINFO_DOES_NOT_FIT_width1_field ## tag)
#define PB_FIELDINFO_ASSERT_2(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,10) && PB_FITS(data_offset,16) && PB_FITS(size_offset,4) && PB_FITS(data_size,12) && PB_FITS(array_size,12), FIELDINFO_DOES_NOT_FIT_width2_field ## tag)
#ifndef PB_FIELD_32BIT
/* Maximum field sizes are still 16-bit if pb_size_t is 16-bit */
#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag)
#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width8_field ## tag)
#else
/* Up to 32-bit fields supported.
* Note that the checks are against 31 bits to avoid compiler warnings about shift wider than type in the test.
* I expect that there is no reasonable use for >2GB messages with nanopb anyway.
*/
#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag)
#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag)
#endif
/* Automatic picking of FIELDINFO width:
* Uses width 1 when possible, otherwise resorts to width 2.
* This is used when PB_BIND() is called with "AUTO" as the argument.
* The generator will give explicit size argument when it knows that a message
* structure grows beyond 1-word format limits.
*/
#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype)
#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype)
#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype)
#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2
#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype
#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype
#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype
#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype
#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2
#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2
#define PB_FI_WIDTH_PB_LTYPE_BOOL 1
#define PB_FI_WIDTH_PB_LTYPE_BYTES 2
#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1
#define PB_FI_WIDTH_PB_LTYPE_ENUM 1
#define PB_FI_WIDTH_PB_LTYPE_UENUM 1
#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1
#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1
#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1
#define PB_FI_WIDTH_PB_LTYPE_INT32 1
#define PB_FI_WIDTH_PB_LTYPE_INT64 1
#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2
#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2
#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1
#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1
#define PB_FI_WIDTH_PB_LTYPE_SINT32 1
#define PB_FI_WIDTH_PB_LTYPE_SINT64 1
#define PB_FI_WIDTH_PB_LTYPE_STRING 2
#define PB_FI_WIDTH_PB_LTYPE_UINT32 1
#define PB_FI_WIDTH_PB_LTYPE_UINT64 1
#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1
#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2
/* The mapping from protobuf types to LTYPEs is done using these macros. */
#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL
#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES
#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT
#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT
#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT
#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT
#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE
#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB
#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT
#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT
#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING
#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT
#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT
#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION
#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES
/* These macros are used for giving out error messages.
* They are mostly a debugging aid; the main error information
* is the true/false return value from functions.
* Some code space can be saved by disabling the error
* messages if not used.
*
* PB_SET_ERROR() sets the error message if none has been set yet.
* msg must be a constant string literal.
* PB_GET_ERROR() always returns a pointer to a string.
* PB_RETURN_ERROR() sets the error and returns false from current
* function.
*/
#ifdef PB_NO_ERRMSG
#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream)
#define PB_GET_ERROR(stream) "(errmsg disabled)"
#else
#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg))
#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)")
#endif
#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false
#ifdef __cplusplus
} /* extern "C" */
#endif
#ifdef __cplusplus
#if __cplusplus >= 201103L
#define PB_CONSTEXPR constexpr
#else // __cplusplus >= 201103L
#define PB_CONSTEXPR
#endif // __cplusplus >= 201103L
#if __cplusplus >= 201703L
#define PB_INLINE_CONSTEXPR inline constexpr
#else // __cplusplus >= 201703L
#define PB_INLINE_CONSTEXPR PB_CONSTEXPR
#endif // __cplusplus >= 201703L
namespace nanopb {
// Each type will be partially specialized by the generator.
template <typename GenMessageT> struct MessageDescriptor;
} // namespace nanopb
#endif /* __cplusplus */
#endif

View File

@ -1,345 +0,0 @@
/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c.
*
* 2014 Petteri Aimonen <jpa@kapsi.fi>
*/
#include "pb_common.h"
static bool load_descriptor_values(pb_field_iter_t *iter)
{
uint32_t word0;
uint32_t data_offset;
uint_least8_t format;
int_least8_t size_offset;
if (iter->index >= iter->descriptor->field_count)
return false;
word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
format = word0 & 3;
iter->tag = (pb_size_t)((word0 >> 2) & 0x3F);
iter->type = (pb_type_t)((word0 >> 8) & 0xFF);
if (format == 0)
{
/* 1-word format */
iter->array_size = 1;
size_offset = (int_least8_t)((word0 >> 24) & 0x0F);
data_offset = (word0 >> 16) & 0xFF;
iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F);
}
else if (format == 1)
{
/* 2-word format */
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF);
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 28) << 6));
size_offset = (int_least8_t)((word0 >> 28) & 0x0F);
data_offset = word1 & 0xFFFF;
iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF);
}
else if (format == 2)
{
/* 4-word format */
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
iter->array_size = (pb_size_t)(word0 >> 16);
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 8) << 6));
size_offset = (int_least8_t)(word1 & 0xFF);
data_offset = word2;
iter->data_size = (pb_size_t)word3;
}
else
{
/* 8-word format */
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]);
iter->array_size = (pb_size_t)word4;
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 8) << 6));
size_offset = (int_least8_t)(word1 & 0xFF);
data_offset = word2;
iter->data_size = (pb_size_t)word3;
}
if (!iter->message)
{
/* Avoid doing arithmetic on null pointers, it is undefined */
iter->pField = NULL;
iter->pSize = NULL;
}
else
{
iter->pField = (char*)iter->message + data_offset;
if (size_offset)
{
iter->pSize = (char*)iter->pField - size_offset;
}
else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED &&
(PB_ATYPE(iter->type) == PB_ATYPE_STATIC ||
PB_ATYPE(iter->type) == PB_ATYPE_POINTER))
{
/* Fixed count array */
iter->pSize = &iter->array_size;
}
else
{
iter->pSize = NULL;
}
if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL)
{
iter->pData = *(void**)iter->pField;
}
else
{
iter->pData = iter->pField;
}
}
if (PB_LTYPE_IS_SUBMSG(iter->type))
{
iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index];
}
else
{
iter->submsg_desc = NULL;
}
return true;
}
static void advance_iterator(pb_field_iter_t *iter)
{
iter->index++;
if (iter->index >= iter->descriptor->field_count)
{
/* Restart */
iter->index = 0;
iter->field_info_index = 0;
iter->submessage_index = 0;
iter->required_field_index = 0;
}
else
{
/* Increment indexes based on previous field type.
* All field info formats have the following fields:
* - lowest 2 bits tell the amount of words in the descriptor (2^n words)
* - bits 2..7 give the lowest bits of tag number.
* - bits 8..15 give the field type.
*/
uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF;
pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3));
iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len);
if (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)
{
iter->required_field_index++;
}
if (PB_LTYPE_IS_SUBMSG(prev_type))
{
iter->submessage_index++;
}
}
}
bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message)
{
memset(iter, 0, sizeof(*iter));
iter->descriptor = desc;
iter->message = message;
return load_descriptor_values(iter);
}
bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension)
{
const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg;
bool status;
uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]);
if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER)
{
/* For pointer extensions, the pointer is stored directly
* in the extension structure. This avoids having an extra
* indirection. */
status = pb_field_iter_begin(iter, msg, &extension->dest);
}
else
{
status = pb_field_iter_begin(iter, msg, extension->dest);
}
iter->pSize = &extension->found;
return status;
}
bool pb_field_iter_next(pb_field_iter_t *iter)
{
advance_iterator(iter);
(void)load_descriptor_values(iter);
return iter->index != 0;
}
bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
{
if (iter->tag == tag)
{
return true; /* Nothing to do, correct field already. */
}
else
{
pb_size_t start = iter->index;
uint32_t fieldinfo;
do
{
/* Advance iterator but don't load values yet */
advance_iterator(iter);
/* Do fast check for tag number match */
fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F))
{
/* Good candidate, check further */
(void)load_descriptor_values(iter);
if (iter->tag == tag &&
PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION)
{
/* Found it */
return true;
}
}
} while (iter->index != start);
/* Searched all the way back to start, and found nothing. */
(void)load_descriptor_values(iter);
return false;
}
}
static void *pb_const_cast(const void *p)
{
/* Note: this casts away const, in order to use the common field iterator
* logic for both encoding and decoding. The cast is done using union
* to avoid spurious compiler warnings. */
union {
void *p1;
const void *p2;
} t;
t.p2 = p;
return t.p1;
}
bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message)
{
return pb_field_iter_begin(iter, desc, pb_const_cast(message));
}
bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension)
{
return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension));
}
bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field)
{
if (field->data_size == sizeof(pb_callback_t))
{
pb_callback_t *pCallback = (pb_callback_t*)field->pData;
if (pCallback != NULL)
{
if (istream != NULL && pCallback->funcs.decode != NULL)
{
return pCallback->funcs.decode(istream, field, &pCallback->arg);
}
if (ostream != NULL && pCallback->funcs.encode != NULL)
{
return pCallback->funcs.encode(ostream, field, &pCallback->arg);
}
}
}
return true; /* Success, but didn't do anything */
}
#ifdef PB_VALIDATE_UTF8
/* This function checks whether a string is valid UTF-8 text.
*
* Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
* Original copyright: Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> 2005-03-30
* Licensed under "Short code license", which allows use under MIT license or
* any compatible with it.
*/
bool pb_validate_utf8(const char *str)
{
const pb_byte_t *s = (const pb_byte_t*)str;
while (*s)
{
if (*s < 0x80)
{
/* 0xxxxxxx */
s++;
}
else if ((s[0] & 0xe0) == 0xc0)
{
/* 110XXXXx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 ||
(s[0] & 0xfe) == 0xc0) /* overlong? */
return false;
else
s += 2;
}
else if ((s[0] & 0xf0) == 0xe0)
{
/* 1110XXXX 10Xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 ||
(s[2] & 0xc0) != 0x80 ||
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
(s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
(s[0] == 0xef && s[1] == 0xbf &&
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
return false;
else
s += 3;
}
else if ((s[0] & 0xf8) == 0xf0)
{
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 ||
(s[2] & 0xc0) != 0x80 ||
(s[3] & 0xc0) != 0x80 ||
(s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
(s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */
return false;
else
s += 4;
}
else
{
return false;
}
}
return true;
}
#endif

View File

@ -1,45 +0,0 @@
/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c.
* These functions are rarely needed by applications directly.
*/
#ifndef PB_COMMON_H_INCLUDED
#define PB_COMMON_H_INCLUDED
#include "pb.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Initialize the field iterator structure to beginning.
* Returns false if the message type is empty. */
bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message);
/* Get a field iterator for extension field. */
bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension);
/* Same as pb_field_iter_begin(), but for const message pointer.
* Note that the pointers in pb_field_iter_t will be non-const but shouldn't
* be written to when using these functions. */
bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message);
bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension);
/* Advance the iterator to the next field.
* Returns false when the iterator wraps back to the first field. */
bool pb_field_iter_next(pb_field_iter_t *iter);
/* Advance the iterator until it points at a field with the given tag.
* Returns false if no such field exists. */
bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag);
#ifdef PB_VALIDATE_UTF8
/* Validate UTF-8 text string */
bool pb_validate_utf8(const char *s);
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,196 +0,0 @@
/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c.
* The main function is pb_decode. You also need an input stream, and the
* field descriptions created by nanopb_generator.py.
*/
#ifndef PB_DECODE_H_INCLUDED
#define PB_DECODE_H_INCLUDED
#include "pb.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Structure for defining custom input streams. You will need to provide
* a callback function to read the bytes from your storage, which can be
* for example a file or a network socket.
*
* The callback must conform to these rules:
*
* 1) Return false on IO errors. This will cause decoding to abort.
* 2) You can use state to store your own data (e.g. buffer pointer),
* and rely on pb_read to verify that no-body reads past bytes_left.
* 3) Your callback may be used with substreams, in which case bytes_left
* is different than from the main stream. Don't use bytes_left to compute
* any pointers.
*/
struct pb_istream_s
{
#ifdef PB_BUFFER_ONLY
/* Callback pointer is not used in buffer-only configuration.
* Having an int pointer here allows binary compatibility but
* gives an error if someone tries to assign callback function.
*/
int *callback;
#else
bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count);
#endif
void *state; /* Free field for use by callback implementation */
size_t bytes_left;
#ifndef PB_NO_ERRMSG
const char *errmsg;
#endif
};
#ifndef PB_NO_ERRMSG
#define PB_ISTREAM_EMPTY {0,0,0,0}
#else
#define PB_ISTREAM_EMPTY {0,0,0}
#endif
/***************************
* Main decoding functions *
***************************/
/* Decode a single protocol buffers message from input stream into a C structure.
* Returns true on success, false on any failure.
* The actual struct pointed to by dest must match the description in fields.
* Callback fields of the destination structure must be initialized by caller.
* All other fields will be initialized by this function.
*
* Example usage:
* MyMessage msg = {};
* uint8_t buffer[64];
* pb_istream_t stream;
*
* // ... read some data into buffer ...
*
* stream = pb_istream_from_buffer(buffer, count);
* pb_decode(&stream, MyMessage_fields, &msg);
*/
bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct);
/* Extended version of pb_decode, with several options to control
* the decoding process:
*
* PB_DECODE_NOINIT: Do not initialize the fields to default values.
* This is slightly faster if you do not need the default
* values and instead initialize the structure to 0 using
* e.g. memset(). This can also be used for merging two
* messages, i.e. combine already existing data with new
* values.
*
* PB_DECODE_DELIMITED: Input message starts with the message size as varint.
* Corresponds to parseDelimitedFrom() in Google's
* protobuf API.
*
* PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows
* reading null terminated messages.
* NOTE: Until nanopb-0.4.0, pb_decode() also allows
* null-termination. This behaviour is not supported in
* most other protobuf implementations, so PB_DECODE_DELIMITED
* is a better option for compatibility.
*
* Multiple flags can be combined with bitwise or (| operator)
*/
#define PB_DECODE_NOINIT 0x01U
#define PB_DECODE_DELIMITED 0x02U
#define PB_DECODE_NULLTERMINATED 0x04U
bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags);
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT)
#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED)
#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT)
#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED)
#ifdef PB_ENABLE_MALLOC
/* Release any allocated pointer fields. If you use dynamic allocation, you should
* call this for any successfully decoded message when you are done with it. If
* pb_decode() returns with an error, the message is already released.
*/
void pb_release(const pb_msgdesc_t *fields, void *dest_struct);
#endif
/**************************************
* Functions for manipulating streams *
**************************************/
/* Create an input stream for reading from a memory buffer.
*
* msglen should be the actual length of the message, not the full size of
* allocated buffer.
*
* Alternatively, you can use a custom stream that reads directly from e.g.
* a file or a network socket.
*/
pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen);
/* Function to read from a pb_istream_t. You can use this if you need to
* read some custom header data, or to read data in field callbacks.
*/
bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
/************************************************
* Helper functions for writing field callbacks *
************************************************/
/* Decode the tag for the next field in the stream. Gives the wire type and
* field tag. At end of the message, returns false and sets eof to true. */
bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
/* Skip the field payload data, given the wire type. */
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
/* Decode an integer in the varint format. This works for enum, int32,
* int64, uint32 and uint64 field types. */
#ifndef PB_WITHOUT_64BIT
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
#else
#define pb_decode_varint pb_decode_varint32
#endif
/* Decode an integer in the varint format. This works for enum, int32,
* and uint32 field types. */
bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest);
/* Decode a bool value in varint format. */
bool pb_decode_bool(pb_istream_t *stream, bool *dest);
/* Decode an integer in the zig-zagged svarint format. This works for sint32
* and sint64. */
#ifndef PB_WITHOUT_64BIT
bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
#else
bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest);
#endif
/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
* a 4-byte wide C variable. */
bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
#ifndef PB_WITHOUT_64BIT
/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to
* a 8-byte wide C variable. */
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
#endif
#ifdef PB_CONVERT_DOUBLE_FLOAT
/* Decode a double value into float variable. */
bool pb_decode_double_as_float(pb_istream_t *stream, float *dest);
#endif
/* Make a limited-length substream for reading a PB_WT_STRING field. */
bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -1,978 +0,0 @@
/* pb_encode.c -- encode a protobuf using minimal resources
*
* 2011 Petteri Aimonen <jpa@kapsi.fi>
*/
#include "pb.h"
#include "pb_encode.h"
#include "pb_common.h"
/* Use the GCC warn_unused_result attribute to check that all return values
* are propagated correctly. On other compilers and gcc before 3.4.0 just
* ignore the annotation.
*/
#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
#define checkreturn
#else
#define checkreturn __attribute__((warn_unused_result))
#endif
/**************************************
* Declarations internal to this file *
**************************************/
static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field);
static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field);
static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field);
static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high);
static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field);
static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field);
#ifdef PB_WITHOUT_64BIT
#define pb_int64_t int32_t
#define pb_uint64_t uint32_t
#else
#define pb_int64_t int64_t
#define pb_uint64_t uint64_t
#endif
/*******************************
* pb_ostream_t implementation *
*******************************/
static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
{
size_t i;
pb_byte_t *dest = (pb_byte_t*)stream->state;
stream->state = dest + count;
for (i = 0; i < count; i++)
dest[i] = buf[i];
return true;
}
pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize)
{
pb_ostream_t stream;
#ifdef PB_BUFFER_ONLY
stream.callback = (void*)1; /* Just a marker value */
#else
stream.callback = &buf_write;
#endif
stream.state = buf;
stream.max_size = bufsize;
stream.bytes_written = 0;
#ifndef PB_NO_ERRMSG
stream.errmsg = NULL;
#endif
return stream;
}
bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
{
if (count > 0 && stream->callback != NULL)
{
if (stream->bytes_written + count < stream->bytes_written ||
stream->bytes_written + count > stream->max_size)
{
PB_RETURN_ERROR(stream, "stream full");
}
#ifdef PB_BUFFER_ONLY
if (!buf_write(stream, buf, count))
PB_RETURN_ERROR(stream, "io error");
#else
if (!stream->callback(stream, buf, count))
PB_RETURN_ERROR(stream, "io error");
#endif
}
stream->bytes_written += count;
return true;
}
/*************************
* Encode a single field *
*************************/
/* Read a bool value without causing undefined behavior even if the value
* is invalid. See issue #434 and
* https://stackoverflow.com/questions/27661768/weird-results-for-conditional
*/
static bool safe_read_bool(const void *pSize)
{
const char *p = (const char *)pSize;
size_t i;
for (i = 0; i < sizeof(bool); i++)
{
if (p[i] != 0)
return true;
}
return false;
}
/* Encode a static array. Handles the size calculations and possible packing. */
static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field)
{
pb_size_t i;
pb_size_t count;
#ifndef PB_ENCODE_ARRAYS_UNPACKED
size_t size;
#endif
count = *(pb_size_t*)field->pSize;
if (count == 0)
return true;
if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size)
PB_RETURN_ERROR(stream, "array max size exceeded");
#ifndef PB_ENCODE_ARRAYS_UNPACKED
/* We always pack arrays if the datatype allows it. */
if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
{
if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
return false;
/* Determine the total size of packed array. */
if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32)
{
size = 4 * (size_t)count;
}
else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
{
size = 8 * (size_t)count;
}
else
{
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
void *pData_orig = field->pData;
for (i = 0; i < count; i++)
{
if (!pb_enc_varint(&sizestream, field))
PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream));
field->pData = (char*)field->pData + field->data_size;
}
field->pData = pData_orig;
size = sizestream.bytes_written;
}
if (!pb_encode_varint(stream, (pb_uint64_t)size))
return false;
if (stream->callback == NULL)
return pb_write(stream, NULL, size); /* Just sizing.. */
/* Write the data */
for (i = 0; i < count; i++)
{
if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
{
if (!pb_enc_fixed(stream, field))
return false;
}
else
{
if (!pb_enc_varint(stream, field))
return false;
}
field->pData = (char*)field->pData + field->data_size;
}
}
else /* Unpacked fields */
#endif
{
for (i = 0; i < count; i++)
{
/* Normally the data is stored directly in the array entries, but
* for pointer-type string and bytes fields, the array entries are
* actually pointers themselves also. So we have to dereference once
* more to get to the actual data. */
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER &&
(PB_LTYPE(field->type) == PB_LTYPE_STRING ||
PB_LTYPE(field->type) == PB_LTYPE_BYTES))
{
bool status;
void *pData_orig = field->pData;
field->pData = *(void* const*)field->pData;
if (!field->pData)
{
/* Null pointer in array is treated as empty string / bytes */
status = pb_encode_tag_for_field(stream, field) &&
pb_encode_varint(stream, 0);
}
else
{
status = encode_basic_field(stream, field);
}
field->pData = pData_orig;
if (!status)
return false;
}
else
{
if (!encode_basic_field(stream, field))
return false;
}
field->pData = (char*)field->pData + field->data_size;
}
}
return true;
}
/* In proto3, all fields are optional and are only encoded if their value is "non-zero".
* This function implements the check for the zero value. */
static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field)
{
pb_type_t type = field->type;
if (PB_ATYPE(type) == PB_ATYPE_STATIC)
{
if (PB_HTYPE(type) == PB_HTYPE_REQUIRED)
{
/* Required proto2 fields inside proto3 submessage, pretty rare case */
return false;
}
else if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
{
/* Repeated fields inside proto3 submessage: present if count != 0 */
return *(const pb_size_t*)field->pSize == 0;
}
else if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
{
/* Oneof fields */
return *(const pb_size_t*)field->pSize == 0;
}
else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL)
{
/* Proto2 optional fields inside proto3 message, or proto3
* submessage fields. */
return safe_read_bool(field->pSize) == false;
}
/* Rest is proto3 singular fields */
if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
{
/* Simple integer / float fields */
pb_size_t i;
const char *p = (const char*)field->pData;
for (i = 0; i < field->data_size; i++)
{
if (p[i] != 0)
{
return false;
}
}
return true;
}
else if (PB_LTYPE(type) == PB_LTYPE_BYTES)
{
const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData;
return bytes->size == 0;
}
else if (PB_LTYPE(type) == PB_LTYPE_STRING)
{
return *(const char*)field->pData == '\0';
}
else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES)
{
/* Fixed length bytes is only empty if its length is fixed
* as 0. Which would be pretty strange, but we can check
* it anyway. */
return field->data_size == 0;
}
else if (PB_LTYPE_IS_SUBMSG(type))
{
/* Check all fields in the submessage to find if any of them
* are non-zero. The comparison cannot be done byte-per-byte
* because the C struct may contain padding bytes that must
* be skipped. Note that usually proto3 submessages have
* a separate has_field that is checked earlier in this if.
*/
pb_field_iter_t iter;
if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData))
{
do
{
if (!pb_check_proto3_default_value(&iter))
{
return false;
}
} while (pb_field_iter_next(&iter));
}
return true;
}
}
else if (PB_ATYPE(type) == PB_ATYPE_POINTER)
{
return field->pData == NULL;
}
else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
{
if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
{
const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData;
return extension == NULL;
}
else if (field->descriptor->field_callback == pb_default_field_callback)
{
pb_callback_t *pCallback = (pb_callback_t*)field->pData;
return pCallback->funcs.encode == NULL;
}
else
{
return field->descriptor->field_callback == NULL;
}
}
return false; /* Not typically reached, safe default for weird special cases. */
}
/* Encode a field with static or pointer allocation, i.e. one whose data
* is available to the encoder directly. */
static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field)
{
if (!field->pData)
{
/* Missing pointer field */
return true;
}
if (!pb_encode_tag_for_field(stream, field))
return false;
switch (PB_LTYPE(field->type))
{
case PB_LTYPE_BOOL:
return pb_enc_bool(stream, field);
case PB_LTYPE_VARINT:
case PB_LTYPE_UVARINT:
case PB_LTYPE_SVARINT:
return pb_enc_varint(stream, field);
case PB_LTYPE_FIXED32:
case PB_LTYPE_FIXED64:
return pb_enc_fixed(stream, field);
case PB_LTYPE_BYTES:
return pb_enc_bytes(stream, field);
case PB_LTYPE_STRING:
return pb_enc_string(stream, field);
case PB_LTYPE_SUBMESSAGE:
case PB_LTYPE_SUBMSG_W_CB:
return pb_enc_submessage(stream, field);
case PB_LTYPE_FIXED_LENGTH_BYTES:
return pb_enc_fixed_length_bytes(stream, field);
default:
PB_RETURN_ERROR(stream, "invalid field type");
}
}
/* Encode a field with callback semantics. This means that a user function is
* called to provide and encode the actual data. */
static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field)
{
if (field->descriptor->field_callback != NULL)
{
if (!field->descriptor->field_callback(NULL, stream, field))
PB_RETURN_ERROR(stream, "callback error");
}
return true;
}
/* Encode a single field of any callback, pointer or static type. */
static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field)
{
/* Check field presence */
if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF)
{
if (*(const pb_size_t*)field->pSize != field->tag)
{
/* Different type oneof field */
return true;
}
}
else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL)
{
if (field->pSize)
{
if (safe_read_bool(field->pSize) == false)
{
/* Missing optional field */
return true;
}
}
else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC)
{
/* Proto3 singular field */
if (pb_check_proto3_default_value(field))
return true;
}
}
if (!field->pData)
{
if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED)
PB_RETURN_ERROR(stream, "missing required field");
/* Pointer field set to NULL */
return true;
}
/* Then encode field contents */
if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK)
{
return encode_callback_field(stream, field);
}
else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
{
return encode_array(stream, field);
}
else
{
return encode_basic_field(stream, field);
}
}
/* Default handler for extension fields. Expects to have a pb_msgdesc_t
* pointer in the extension->type->arg field, pointing to a message with
* only one field in it. */
static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension)
{
pb_field_iter_t iter;
if (!pb_field_iter_begin_extension_const(&iter, extension))
PB_RETURN_ERROR(stream, "invalid extension");
return encode_field(stream, &iter);
}
/* Walk through all the registered extensions and give them a chance
* to encode themselves. */
static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field)
{
const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData;
while (extension)
{
bool status;
if (extension->type->encode)
status = extension->type->encode(stream, extension);
else
status = default_extension_encoder(stream, extension);
if (!status)
return false;
extension = extension->next;
}
return true;
}
/*********************
* Encode all fields *
*********************/
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct)
{
pb_field_iter_t iter;
if (!pb_field_iter_begin_const(&iter, fields, src_struct))
return true; /* Empty message type */
do {
if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION)
{
/* Special case for the extension field placeholder */
if (!encode_extension_field(stream, &iter))
return false;
}
else
{
/* Regular field */
if (!encode_field(stream, &iter))
return false;
}
} while (pb_field_iter_next(&iter));
return true;
}
bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags)
{
if ((flags & PB_ENCODE_DELIMITED) != 0)
{
return pb_encode_submessage(stream, fields, src_struct);
}
else if ((flags & PB_ENCODE_NULLTERMINATED) != 0)
{
const pb_byte_t zero = 0;
if (!pb_encode(stream, fields, src_struct))
return false;
return pb_write(stream, &zero, 1);
}
else
{
return pb_encode(stream, fields, src_struct);
}
}
bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct)
{
pb_ostream_t stream = PB_OSTREAM_SIZING;
if (!pb_encode(&stream, fields, src_struct))
return false;
*size = stream.bytes_written;
return true;
}
/********************
* Helper functions *
********************/
/* This function avoids 64-bit shifts as they are quite slow on many platforms. */
static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high)
{
size_t i = 0;
pb_byte_t buffer[10];
pb_byte_t byte = (pb_byte_t)(low & 0x7F);
low >>= 7;
while (i < 4 && (low != 0 || high != 0))
{
byte |= 0x80;
buffer[i++] = byte;
byte = (pb_byte_t)(low & 0x7F);
low >>= 7;
}
if (high)
{
byte = (pb_byte_t)(byte | ((high & 0x07) << 4));
high >>= 3;
while (high)
{
byte |= 0x80;
buffer[i++] = byte;
byte = (pb_byte_t)(high & 0x7F);
high >>= 7;
}
}
buffer[i++] = byte;
return pb_write(stream, buffer, i);
}
bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value)
{
if (value <= 0x7F)
{
/* Fast path: single byte */
pb_byte_t byte = (pb_byte_t)value;
return pb_write(stream, &byte, 1);
}
else
{
#ifdef PB_WITHOUT_64BIT
return pb_encode_varint_32(stream, value, 0);
#else
return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32));
#endif
}
}
bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value)
{
pb_uint64_t zigzagged;
if (value < 0)
zigzagged = ~((pb_uint64_t)value << 1);
else
zigzagged = (pb_uint64_t)value << 1;
return pb_encode_varint(stream, zigzagged);
}
bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
{
uint32_t val = *(const uint32_t*)value;
pb_byte_t bytes[4];
bytes[0] = (pb_byte_t)(val & 0xFF);
bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
return pb_write(stream, bytes, 4);
}
#ifndef PB_WITHOUT_64BIT
bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
{
uint64_t val = *(const uint64_t*)value;
pb_byte_t bytes[8];
bytes[0] = (pb_byte_t)(val & 0xFF);
bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
bytes[4] = (pb_byte_t)((val >> 32) & 0xFF);
bytes[5] = (pb_byte_t)((val >> 40) & 0xFF);
bytes[6] = (pb_byte_t)((val >> 48) & 0xFF);
bytes[7] = (pb_byte_t)((val >> 56) & 0xFF);
return pb_write(stream, bytes, 8);
}
#endif
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
{
pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype;
return pb_encode_varint(stream, tag);
}
bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field )
{
pb_wire_type_t wiretype;
switch (PB_LTYPE(field->type))
{
case PB_LTYPE_BOOL:
case PB_LTYPE_VARINT:
case PB_LTYPE_UVARINT:
case PB_LTYPE_SVARINT:
wiretype = PB_WT_VARINT;
break;
case PB_LTYPE_FIXED32:
wiretype = PB_WT_32BIT;
break;
case PB_LTYPE_FIXED64:
wiretype = PB_WT_64BIT;
break;
case PB_LTYPE_BYTES:
case PB_LTYPE_STRING:
case PB_LTYPE_SUBMESSAGE:
case PB_LTYPE_SUBMSG_W_CB:
case PB_LTYPE_FIXED_LENGTH_BYTES:
wiretype = PB_WT_STRING;
break;
default:
PB_RETURN_ERROR(stream, "invalid field type");
}
return pb_encode_tag(stream, wiretype, field->tag);
}
bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size)
{
if (!pb_encode_varint(stream, (pb_uint64_t)size))
return false;
return pb_write(stream, buffer, size);
}
bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct)
{
/* First calculate the message size using a non-writing substream. */
pb_ostream_t substream = PB_OSTREAM_SIZING;
size_t size;
bool status;
if (!pb_encode(&substream, fields, src_struct))
{
#ifndef PB_NO_ERRMSG
stream->errmsg = substream.errmsg;
#endif
return false;
}
size = substream.bytes_written;
if (!pb_encode_varint(stream, (pb_uint64_t)size))
return false;
if (stream->callback == NULL)
return pb_write(stream, NULL, size); /* Just sizing */
if (stream->bytes_written + size > stream->max_size)
PB_RETURN_ERROR(stream, "stream full");
/* Use a substream to verify that a callback doesn't write more than
* what it did the first time. */
substream.callback = stream->callback;
substream.state = stream->state;
substream.max_size = size;
substream.bytes_written = 0;
#ifndef PB_NO_ERRMSG
substream.errmsg = NULL;
#endif
status = pb_encode(&substream, fields, src_struct);
stream->bytes_written += substream.bytes_written;
stream->state = substream.state;
#ifndef PB_NO_ERRMSG
stream->errmsg = substream.errmsg;
#endif
if (substream.bytes_written != size)
PB_RETURN_ERROR(stream, "submsg size changed");
return status;
}
/* Field encoders */
static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field)
{
uint32_t value = safe_read_bool(field->pData) ? 1 : 0;
PB_UNUSED(field);
return pb_encode_varint(stream, value);
}
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field)
{
if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT)
{
/* Perform unsigned integer extension */
pb_uint64_t value = 0;
if (field->data_size == sizeof(uint_least8_t))
value = *(const uint_least8_t*)field->pData;
else if (field->data_size == sizeof(uint_least16_t))
value = *(const uint_least16_t*)field->pData;
else if (field->data_size == sizeof(uint32_t))
value = *(const uint32_t*)field->pData;
else if (field->data_size == sizeof(pb_uint64_t))
value = *(const pb_uint64_t*)field->pData;
else
PB_RETURN_ERROR(stream, "invalid data_size");
return pb_encode_varint(stream, value);
}
else
{
/* Perform signed integer extension */
pb_int64_t value = 0;
if (field->data_size == sizeof(int_least8_t))
value = *(const int_least8_t*)field->pData;
else if (field->data_size == sizeof(int_least16_t))
value = *(const int_least16_t*)field->pData;
else if (field->data_size == sizeof(int32_t))
value = *(const int32_t*)field->pData;
else if (field->data_size == sizeof(pb_int64_t))
value = *(const pb_int64_t*)field->pData;
else
PB_RETURN_ERROR(stream, "invalid data_size");
if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT)
return pb_encode_svarint(stream, value);
#ifdef PB_WITHOUT_64BIT
else if (value < 0)
return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1);
#endif
else
return pb_encode_varint(stream, (pb_uint64_t)value);
}
}
static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field)
{
#ifdef PB_CONVERT_DOUBLE_FLOAT
if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
{
return pb_encode_float_as_double(stream, *(float*)field->pData);
}
#endif
if (field->data_size == sizeof(uint32_t))
{
return pb_encode_fixed32(stream, field->pData);
}
#ifndef PB_WITHOUT_64BIT
else if (field->data_size == sizeof(uint64_t))
{
return pb_encode_fixed64(stream, field->pData);
}
#endif
else
{
PB_RETURN_ERROR(stream, "invalid data_size");
}
}
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field)
{
const pb_bytes_array_t *bytes = NULL;
bytes = (const pb_bytes_array_t*)field->pData;
if (bytes == NULL)
{
/* Treat null pointer as an empty bytes field */
return pb_encode_string(stream, NULL, 0);
}
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes))
{
PB_RETURN_ERROR(stream, "bytes size exceeded");
}
return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size);
}
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field)
{
size_t size = 0;
size_t max_size = (size_t)field->data_size;
const char *str = (const char*)field->pData;
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
{
max_size = (size_t)-1;
}
else
{
/* pb_dec_string() assumes string fields end with a null
* terminator when the type isn't PB_ATYPE_POINTER, so we
* shouldn't allow more than max-1 bytes to be written to
* allow space for the null terminator.
*/
if (max_size == 0)
PB_RETURN_ERROR(stream, "zero-length string");
max_size -= 1;
}
if (str == NULL)
{
size = 0; /* Treat null pointer as an empty string */
}
else
{
const char *p = str;
/* strnlen() is not always available, so just use a loop */
while (size < max_size && *p != '\0')
{
size++;
p++;
}
if (*p != '\0')
{
PB_RETURN_ERROR(stream, "unterminated string");
}
}
#ifdef PB_VALIDATE_UTF8
if (!pb_validate_utf8(str))
PB_RETURN_ERROR(stream, "invalid utf8");
#endif
return pb_encode_string(stream, (const pb_byte_t*)str, size);
}
static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field)
{
if (field->submsg_desc == NULL)
PB_RETURN_ERROR(stream, "invalid field descriptor");
if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL)
{
/* Message callback is stored right before pSize. */
pb_callback_t *callback = (pb_callback_t*)field->pSize - 1;
if (callback->funcs.encode)
{
if (!callback->funcs.encode(stream, field, &callback->arg))
return false;
}
}
return pb_encode_submessage(stream, field->submsg_desc, field->pData);
}
static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field)
{
return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size);
}
#ifdef PB_CONVERT_DOUBLE_FLOAT
bool pb_encode_float_as_double(pb_ostream_t *stream, float value)
{
union { float f; uint32_t i; } in;
uint_least8_t sign;
int exponent;
uint64_t mantissa;
in.f = value;
/* Decompose input value */
sign = (uint_least8_t)((in.i >> 31) & 1);
exponent = (int)((in.i >> 23) & 0xFF) - 127;
mantissa = in.i & 0x7FFFFF;
if (exponent == 128)
{
/* Special value (NaN etc.) */
exponent = 1024;
}
else if (exponent == -127)
{
if (!mantissa)
{
/* Zero */
exponent = -1023;
}
else
{
/* Denormalized */
mantissa <<= 1;
while (!(mantissa & 0x800000))
{
mantissa <<= 1;
exponent--;
}
mantissa &= 0x7FFFFF;
}
}
/* Combine fields */
mantissa <<= 29;
mantissa |= (uint64_t)(exponent + 1023) << 52;
mantissa |= (uint64_t)sign << 63;
return pb_encode_fixed64(stream, &mantissa);
}
#endif

View File

@ -1,185 +0,0 @@
/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c.
* The main function is pb_encode. You also need an output stream, and the
* field descriptions created by nanopb_generator.py.
*/
#ifndef PB_ENCODE_H_INCLUDED
#define PB_ENCODE_H_INCLUDED
#include "pb.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Structure for defining custom output streams. You will need to provide
* a callback function to write the bytes to your storage, which can be
* for example a file or a network socket.
*
* The callback must conform to these rules:
*
* 1) Return false on IO errors. This will cause encoding to abort.
* 2) You can use state to store your own data (e.g. buffer pointer).
* 3) pb_write will update bytes_written after your callback runs.
* 4) Substreams will modify max_size and bytes_written. Don't use them
* to calculate any pointers.
*/
struct pb_ostream_s
{
#ifdef PB_BUFFER_ONLY
/* Callback pointer is not used in buffer-only configuration.
* Having an int pointer here allows binary compatibility but
* gives an error if someone tries to assign callback function.
* Also, NULL pointer marks a 'sizing stream' that does not
* write anything.
*/
int *callback;
#else
bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
#endif
void *state; /* Free field for use by callback implementation. */
size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */
size_t bytes_written; /* Number of bytes written so far. */
#ifndef PB_NO_ERRMSG
const char *errmsg;
#endif
};
/***************************
* Main encoding functions *
***************************/
/* Encode a single protocol buffers message from C structure into a stream.
* Returns true on success, false on any failure.
* The actual struct pointed to by src_struct must match the description in fields.
* All required fields in the struct are assumed to have been filled in.
*
* Example usage:
* MyMessage msg = {};
* uint8_t buffer[64];
* pb_ostream_t stream;
*
* msg.field1 = 42;
* stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
* pb_encode(&stream, MyMessage_fields, &msg);
*/
bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct);
/* Extended version of pb_encode, with several options to control the
* encoding process:
*
* PB_ENCODE_DELIMITED: Prepend the length of message as a varint.
* Corresponds to writeDelimitedTo() in Google's
* protobuf API.
*
* PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination.
* NOTE: This behaviour is not supported in most other
* protobuf implementations, so PB_ENCODE_DELIMITED
* is a better option for compatibility.
*/
#define PB_ENCODE_DELIMITED 0x02U
#define PB_ENCODE_NULLTERMINATED 0x04U
bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags);
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED)
#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED)
/* Encode the message to get the size of the encoded data, but do not store
* the data. */
bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct);
/**************************************
* Functions for manipulating streams *
**************************************/
/* Create an output stream for writing into a memory buffer.
* The number of bytes written can be found in stream.bytes_written after
* encoding the message.
*
* Alternatively, you can use a custom stream that writes directly to e.g.
* a file or a network socket.
*/
pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize);
/* Pseudo-stream for measuring the size of a message without actually storing
* the encoded data.
*
* Example usage:
* MyMessage msg = {};
* pb_ostream_t stream = PB_OSTREAM_SIZING;
* pb_encode(&stream, MyMessage_fields, &msg);
* printf("Message size is %d\n", stream.bytes_written);
*/
#ifndef PB_NO_ERRMSG
#define PB_OSTREAM_SIZING {0,0,0,0,0}
#else
#define PB_OSTREAM_SIZING {0,0,0,0}
#endif
/* Function to write into a pb_ostream_t stream. You can use this if you need
* to append or prepend some custom headers to the message.
*/
bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
/************************************************
* Helper functions for writing field callbacks *
************************************************/
/* Encode field header based on type and field number defined in the field
* structure. Call this from the callback before writing out field contents. */
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field);
/* Encode field header by manually specifing wire type. You need to use this
* if you want to write out packed arrays from a callback field. */
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
/* Encode an integer in the varint format.
* This works for bool, enum, int32, int64, uint32 and uint64 field types. */
#ifndef PB_WITHOUT_64BIT
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
#else
bool pb_encode_varint(pb_ostream_t *stream, uint32_t value);
#endif
/* Encode an integer in the zig-zagged svarint format.
* This works for sint32 and sint64. */
#ifndef PB_WITHOUT_64BIT
bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
#else
bool pb_encode_svarint(pb_ostream_t *stream, int32_t value);
#endif
/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */
bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size);
/* Encode a fixed32, sfixed32 or float value.
* You need to pass a pointer to a 4-byte wide C variable. */
bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
#ifndef PB_WITHOUT_64BIT
/* Encode a fixed64, sfixed64 or double value.
* You need to pass a pointer to a 8-byte wide C variable. */
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
#endif
#ifdef PB_CONVERT_DOUBLE_FLOAT
/* Encode a float value so that it appears like a double in the encoded
* message. */
bool pb_encode_float_as_double(pb_ostream_t *stream, float value);
#endif
/* Encode a submessage field.
* You need to pass the pb_field_t array and pointer to struct, just like
* with pb_encode(). This internally encodes the submessage twice, first to
* calculate message size and then to actually write it out.
*/
bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -33,9 +33,41 @@
static EventGroupHandle_t s_wifi_event_group;
static bool initialized = false;
static bool connecting = false;
static wifi_ap_record_t current_wifi_ap;
static wifi_connected_callback wifi_callback;
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
{
esp_wifi_connect();
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
{
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
connecting = false;
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
{
ESP_LOGD(WIFI_LOG, "Got Ip!");
if (wifi_callback != NULL)
{
(*wifi_callback)();
}
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
connecting = false;
heap_caps_check_integrity_all(true);
}
else
{
ESP_LOGD(WIFI_LOG, "other wifi event: event base %s, event id %d", event_base, event_id);
}
}
void wifi_controller_init(void)
{
// init NVS for WIFI
@ -69,6 +101,7 @@ void wifi_controller_scan(wifi_ap_record_t *ap_info, uint16_t *ap_count)
uint16_t number = 10;
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, true));
@ -77,34 +110,24 @@ void wifi_controller_scan(wifi_ap_record_t *ap_info, uint16_t *ap_count)
ESP_ERROR_CHECK(esp_wifi_stop());
}
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
esp_err_t wifi_controller_connect(wifi_config_t wifi_config, wifi_connected_callback callback)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
if (connecting)
{
esp_wifi_connect();
return ESP_ERR_NOT_SUPPORTED;
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
{
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
{
ESP_LOGD(WIFI_LOG, "got IP");
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
heap_caps_check_integrity_all(true);
}
}
esp_err_t wifi_controller_connect(wifi_config_t wifi_config)
{
connecting = true;
wifi_callback = callback;
if (!initialized)
{
wifi_controller_init();
}
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
@ -116,6 +139,7 @@ esp_err_t wifi_controller_connect(wifi_config_t wifi_config)
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
vEventGroupDelete(s_wifi_event_group);
if (bits & WIFI_CONNECTED_BIT)
@ -135,16 +159,25 @@ esp_err_t wifi_controller_connect(wifi_config_t wifi_config)
}
}
esp_err_t wifi_controller_reconnect(void)
esp_err_t wifi_controller_reconnect(wifi_connected_callback callback)
{
if (connecting)
{
return ESP_ERR_NOT_SUPPORTED;
}
connecting = true;
wifi_callback = callback;
if (!initialized)
{
wifi_controller_init();
}
esp_wifi_stop();
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
@ -155,6 +188,7 @@ esp_err_t wifi_controller_reconnect(void)
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
vEventGroupDelete(s_wifi_event_group);
if (bits & WIFI_CONNECTED_BIT)
@ -174,6 +208,15 @@ esp_err_t wifi_controller_reconnect(void)
}
}
esp_err_t wifi_controller_disconnect(void)
{
if (esp_wifi_sta_get_ap_info(&current_wifi_ap) == ESP_OK)
{
ESP_ERROR_CHECK(esp_wifi_disconnect());
}
return ESP_OK;
}
wifi_ap_record_t *wifi_controller_connection(void)
{
if (esp_wifi_sta_get_ap_info(&current_wifi_ap) == ESP_OK)

View File

@ -19,6 +19,11 @@
#define WIFI_LOG "wifi-controller" // TAG for Logging
/**
* @brief callback function after successfull wifi connect
*/
typedef void (*wifi_connected_callback)(void);
/**
* @brief scan for WiFis
*
@ -31,22 +36,31 @@ void wifi_controller_scan(wifi_ap_record_t ap_info[], uint16_t *ap_count);
* @brief connect to wifi ap
*
* @param[in] wifi_config config of wifi to connect
* @param[in] callback callback function after connection
*
* @return
* esp_err_t connection status
*/
esp_err_t wifi_controller_connect(wifi_config_t wifi_config);
esp_err_t wifi_controller_connect(wifi_config_t wifi_config, wifi_connected_callback callback);
/**
* @brief reconnect to previous wifi
*
* @param[in] callback callback function after connection
*
* @return
* esp_err_t connection status
*/
esp_err_t wifi_controller_reconnect(void);
esp_err_t wifi_controller_reconnect(wifi_connected_callback callback);
/**
* @brief reconnect to previous wifi
* @brief disconnect wifi
*
*/
esp_err_t wifi_controller_disconnect(void);
/**
* @brief get current wifi connection
*
* @return
* wifi_ap_record_t pointer to current wifi connection, NULL if not connected

9
main/Kconfig.projbuild Normal file
View File

@ -0,0 +1,9 @@
menu "WiFi STA"
config WIFI_STA_SSID
string "Wifi SSID"
config WIFI_STA_PASSWORD
string "Wifi Password"
endmenu

View File

@ -17,6 +17,7 @@
#include <time.h>
#include <sys/time.h>
#include "esp_system.h"
#include "esp_sntp.h"
#include "esp_log.h"
#include "ena.h"
@ -25,7 +26,7 @@
#include "ena-exposure.h"
#include "ena-bluetooth-advertise.h"
#include "ena-bluetooth-scan.h"
#include "ena-cwa.h"
#include "ena-eke-proxy.h"
#include "ds3231.h"
#include "ssd1306.h"
#include "interface.h"
@ -33,26 +34,36 @@
#include "rtc.h"
#include "wifi-controller.h"
#include "sdkconfig.h"
void time_sync_notification_cb(struct timeval *tv)
{
time_t time = (time_t)tv->tv_sec;
struct tm *rtc_time = gmtime(&time);
rtc_set_time(rtc_time);
settimeofday(tv, NULL);
ESP_LOGD(ENA_LOG, "NTP time:%lu %s", tv->tv_sec, asctime(rtc_time));
}
void app_main(void)
{
// debug only own LOG TAGs
esp_log_level_set("*", ESP_LOG_WARN);
esp_log_level_set(ENA_LOG, ESP_LOG_DEBUG);
esp_log_level_set(ENA_BEACON_LOG, ESP_LOG_DEBUG);
esp_log_level_set(ENA_ADVERTISE_LOG, ESP_LOG_DEBUG);
esp_log_level_set(ENA_SCAN_LOG, ESP_LOG_DEBUG);
esp_log_level_set(ENA_BEACON_LOG, ESP_LOG_INFO);
esp_log_level_set(ENA_ADVERTISE_LOG, ESP_LOG_INFO);
esp_log_level_set(ENA_SCAN_LOG, ESP_LOG_INFO);
esp_log_level_set(ENA_EXPOSURE_LOG, ESP_LOG_DEBUG);
esp_log_level_set(ENA_STORAGE_LOG, ESP_LOG_INFO);
esp_log_level_set(ENA_CWA_LOG, ESP_LOG_DEBUG);
esp_log_level_set(INTERFACE_LOG, ESP_LOG_DEBUG);
esp_log_level_set(WIFI_LOG, ESP_LOG_DEBUG);
esp_log_level_set(ENA_EKE_PROXY_LOG, ESP_LOG_DEBUG);
esp_log_level_set(INTERFACE_LOG, ESP_LOG_INFO);
esp_log_level_set(WIFI_LOG, ESP_LOG_INFO);
// start interface
interface_start();
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_set_time_sync_notification_cb(time_sync_notification_cb);
sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);
sntp_init();
// set system time from RTC
struct tm rtc_time;
@ -63,22 +74,23 @@ void app_main(void)
tv.tv_sec = curtime;
settimeofday(&tv, NULL);
// Hardcoded timezone of UTC+2 for now (consider POSIX notation!)
setenv("TZ", "UTC-2", 1);
tzset();
ena_start();
// start interface
interface_start();
// start with main interface
interface_main_start();
// start button input
button_input_start();
wifi_controller_reconnect(NULL);
while (1)
{
ena_run();
ena_cwa_run();
ena_eke_proxy_run();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}