Fatal exception and Disconnects WiFi Every 3 Hours or Less While Reading or Writing UART Data - serial-port

I developed with ESPHOME and WEMOS D1 Mini to get my data from the RS232 port of my UPS device. Although currently working fine, my device loses wifi connection at intervals of 3 hours or less. I should also mention that my other ESP8266 devices connected to the same wifi network are connected for days and I do not have any connection problems.
In my research on the internet, there is information that it is caused by the general wifi settings and the problem will be fixed when the wifi settings are changed, but the issues are not closed as resolved and I have encountered many similar issues.
In the AP logs, information appears as “device disconnected or terminated”.
The codes I wrote below are available.
I look forward to your comments and suggestions for solutions to my problem.
Wemos D1 Mini Nodemcu Esp8266
RS232 to TTL
Makelsan POWERPACK SE SERİSİ 1 kVA
UPS Data Read and Parse
Home Assistant Screenshot
UPS-Makelsan.h
#include "esphome.h"
class Get
{
public:
static int readline(int readch, char* buffer, int len)
{
static int pos = 0;
int rpos;
if (readch > 0)
{
switch (readch)
{
case 40: //'(': // RS232 Data is "(216.5 000.0 219.6 027 50.0 2.26 40.00 00110000". Clear "(".
case 35: //'#': // RS232 Data is "#220.0 005 024.0 50.0". Clear "#".
break;
case 13: //'\r': // Return on CR
rpos = pos;
pos = 0; // Reset position index ready for next time
return rpos;
default:
if (pos < len - 1)
{
buffer[pos++] = readch;
buffer[pos] = 0;
}
}
}
// No end of line has been found, so return -1.
return -1;
}
static float battery_capacity(float value)
{
return (value * 43.66812227074236);
}
static bool optionsValue(char value)
{
return (value == 48 ? false : (value == 49 ? true : false));
}
};
class Update
{
public:
static void binarySensor(BinarySensor* sensor, bool value)
{
//if (sensor->state != value)
//{
sensor->publish_state(value);
//}
}
static void sensor(Sensor* sensor, float value)
{
//if (sensor->state != value)
//{
sensor->publish_state(value);
//}
}
static void textSensor(TextSensor* sensor, char* value)
{
//if (sensor->state != value)
//{
sensor->publish_state(value);
//}
}
};
class MakelsanUPS : public Component, public UARTDevice, public BinarySensor, public Sensor, public TextSensor
{
public:
BinarySensor* battery_low = new BinarySensor();
BinarySensor* beeper_on = new BinarySensor();
BinarySensor* eco_mode = new BinarySensor();
BinarySensor* power_status = new BinarySensor();
BinarySensor* shutdown_active = new BinarySensor();
BinarySensor* test_in_progress = new BinarySensor();
BinarySensor* ups_failed = new BinarySensor();
BinarySensor* ups_type_is_standby = new BinarySensor();
BinarySensor* utility_fail = new BinarySensor();
Sensor* battery_capacity = new Sensor();
Sensor* battery_voltage = new Sensor();
Sensor* battery_voltage_current = new Sensor();
Sensor* internal_tempeture = new Sensor();
Sensor* output_current = new Sensor();
Sensor* output_power = new Sensor();
Sensor* raiting_current = new Sensor();
Sensor* raiting_voltage = new Sensor();
Sensor* voltage_frequency = new Sensor();
Sensor* voltage_input = new Sensor();
Sensor* voltage_input_fault = new Sensor();
Sensor* voltage_input_frequency = new Sensor();
Sensor* voltage_output = new Sensor();
TextSensor* power_type = new TextSensor();
TextSensor* ups_type = new TextSensor();
MakelsanUPS(UARTComponent* parent) : UARTDevice(parent) {}
//float get_setup_priority() const override { return esphome::setup_priority::DATA; }
void setup() override
{
// nothing to do here
}
void loop() override
{
static char buffer[50];
while (available())
{
if (Get::readline(read(), buffer, sizeof(buffer)) > 0)
{
const char* delimiter = " ";
switch (strlen(buffer))
{
case 20: //"220.0 005 024.0 50.0"
Update::sensor(raiting_voltage, atof(strtok(buffer, delimiter)));
Update::sensor(raiting_current, atof(strtok(NULL, delimiter)));
Update::sensor(battery_voltage, atof(strtok(NULL, delimiter)));
Update::sensor(voltage_frequency, atof(strtok(NULL, delimiter)));
break;
case 45: //"216.5 000.0 219.6 027 50.0 2.26 40.00 00110000"
Update::sensor(voltage_input, atof(strtok(buffer, delimiter)));
Update::sensor(voltage_input_fault, atof(strtok(NULL, delimiter)));
Update::sensor(voltage_output, atof(strtok(NULL, delimiter)));
Update::sensor(output_current, atof(strtok(NULL, delimiter)));
Update::sensor(voltage_input_frequency, atof(strtok(NULL, delimiter)));
const float battery_voltage_float = atof(strtok(NULL, delimiter));
Update::sensor(internal_tempeture, atof(strtok(NULL, delimiter)));
char other_values[9]{};
strcpy(other_values, strtok(NULL, delimiter));
Update::binarySensor(battery_low, Get::optionsValue(other_values[1]));
Update::binarySensor(beeper_on, Get::optionsValue(other_values[7]));
Update::binarySensor(eco_mode, Get::optionsValue(other_values[2]));
Update::binarySensor(utility_fail, Get::optionsValue(other_values[0]));
Update::binarySensor(shutdown_active, Get::optionsValue(other_values[6]));
Update::binarySensor(test_in_progress, Get::optionsValue(other_values[5]));
Update::binarySensor(ups_failed, Get::optionsValue(other_values[3]));
Update::binarySensor(ups_type_is_standby, Get::optionsValue(other_values[4]));
Update::sensor(battery_capacity, Get::battery_capacity(battery_voltage_float));
Update::sensor(battery_voltage_current, battery_voltage_float);
Update::binarySensor(power_status, (voltage_input->state == 0 ? false : true));
Update::sensor(output_power, (output_current->state * 6.111111));
Update::textSensor(power_type, (char*)(voltage_input->state == 0 ? "Battery Power" : "AC Utility Power"));
Update::textSensor(ups_type, (char*)(voltage_input->state == 0 ? "Normal" : "On-Line"));
break;
}
}
}
}
};
ha-ups-makelsan.yaml validate export
substitutions:
platform: ESP8266
board: d1_mini
deviceName: ha-ups-makelsan
deviceNameId: ha_ups_makelsan
deviceFriendlyName: HA Makelsan UPS
esphome:
name: ha-ups-makelsan
includes:
- common/scripts/uart/UPS-Makelsan.h
build_path: .esphome/build/ha-ups-makelsan
platformio_options: {}
libraries: []
name_add_mac_suffix: false
esp8266:
board: d1_mini
framework:
version: 3.0.2
source: ~3.30002.0
platform_version: platformio/espressif8266 # 3.2.0
restore_from_flash: false
board_flash_mode: dout
wifi:
ap:
ssid: HA Makelsan UPS Hotspot
password: ha_ups_makelsan_ML080wxvFS8z_ha_ups_makelsan
ap_timeout: 1min
domain: !secret 'wifi_domainname'
fast_connect: true
power_save_mode: NONE
reboot_timeout: 15min
output_power: 20.0
networks:
- ssid: !secret 'wifi_1_ssid'
password: !secret 'wifi_1_password'
priority: 0.0
use_address: ha-ups-makelsan.local
captive_portal: {}
web_server:
port: 80
auth:
username: !secret 'web_server_username'
password: !secret 'web_server_password'
css_url: https://esphome.io/_static/webserver-v1.min.css
js_url: https://esphome.io/_static/webserver-v1.min.js
include_internal: false
ota: true
text_sensor:
- platform: wifi_info
ip_address:
name: WiFi IP Address
icon: mdi:ip-network
id: ha_ups_makelsan_wifi_ip_address
disabled_by_default: false
entity_category: diagnostic
update_interval: 1s
ssid:
name: WiFi SSID
icon: mdi:router-wireless
id: ha_ups_makelsan_wifi_ssid
disabled_by_default: false
entity_category: diagnostic
update_interval: 1s
bssid:
name: WiFi BSSID
icon: mdi:router-wireless-settings
id: ha_ups_makelsan_wifi_bssid
disabled_by_default: false
entity_category: diagnostic
update_interval: 1s
mac_address:
name: Wifi MAC Address
icon: mdi:fingerprint
id: ha_ups_makelsan_wifi_mac_address
disabled_by_default: false
entity_category: diagnostic
- platform: custom
lambda: !lambda |-
auto ups_text_sensor = (MakelsanUPS*) id(ha_ups_makelsan_uart_data_ups);
App.register_component(ups_text_sensor);
return {
ups_text_sensor->power_type
,ups_text_sensor->ups_type
};
text_sensors:
- name: Power Type
id: ha_ups_makelsan_power_type
icon: mdi:power-socket-eu
disabled_by_default: false
- name: UPS Type
id: ha_ups_makelsan_ups_type
icon: mdi:car-battery
disabled_by_default: false
sensor:
- platform: wifi_signal
name: WiFi Signal
id: ha_ups_makelsan_wifi_signal
disabled_by_default: false
force_update: false
unit_of_measurement: dBm
accuracy_decimals: 0
device_class: signal_strength
state_class: measurement
entity_category: diagnostic
update_interval: 60s
- platform: total_daily_energy
name: Daily Energy
id: ha_ups_makelsan_daily_energy
power_id: ha_ups_makelsan_output_power
unit_of_measurement: kWh
filters:
- multiply: 0.001
disabled_by_default: false
force_update: false
device_class: energy
state_class: total_increasing
restore: true
min_save_interval: 0s
method: right
accuracy_decimals: 2
- platform: custom
lambda: !lambda |-
auto ups_sensor = (MakelsanUPS*) id(ha_ups_makelsan_uart_data_ups);
App.register_component(ups_sensor);
return {
ups_sensor->battery_capacity
,ups_sensor->battery_voltage
,ups_sensor->battery_voltage_current
,ups_sensor->internal_tempeture
,ups_sensor->output_current
,ups_sensor->output_power
,ups_sensor->raiting_current
,ups_sensor->raiting_voltage
,ups_sensor->voltage_frequency
,ups_sensor->voltage_input
,ups_sensor->voltage_input_fault
,ups_sensor->voltage_input_frequency
,ups_sensor->voltage_output
};
sensors:
- name: Battery Capacity
id: ha_ups_makelsan_battery_capacity
device_class: battery
unit_of_measurement: '%'
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Battery Voltage
id: ha_ups_makelsan_battery_voltage
unit_of_measurement: V
device_class: voltage
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Battery Voltage Current
id: ha_ups_makelsan_battery_voltage_current
unit_of_measurement: V
device_class: current
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Internal Tempeture
id: ha_ups_makelsan_internal_tempeture
unit_of_measurement: °C
device_class: temperature
accuracy_decimals: 0
disabled_by_default: false
force_update: false
- name: Output Current
id: ha_ups_makelsan_output_current
device_class: power_factor
unit_of_measurement: '%'
accuracy_decimals: 0
disabled_by_default: false
force_update: false
- name: Output Power
id: ha_ups_makelsan_output_power
device_class: power
unit_of_measurement: W
accuracy_decimals: 0
disabled_by_default: false
force_update: false
- name: Raiting Current
id: ha_ups_makelsan_raiting_current
unit_of_measurement: V
device_class: current
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Raiting Voltage
id: ha_ups_makelsan_raiting_voltage
unit_of_measurement: V
device_class: voltage
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Voltage Frequency
id: ha_ups_makelsan_voltage_frequency
icon: mdi:cosine-wave
unit_of_measurement: Hz
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Voltage Input
id: ha_ups_makelsan_voltage_input
unit_of_measurement: VAC
device_class: voltage
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Voltage Input Fault
id: ha_ups_makelsan_voltage_input_fault
unit_of_measurement: OV
device_class: voltage
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Voltage Input Frequency
icon: mdi:cosine-wave
id: ha_ups_makelsan_voltage_input_frequency
unit_of_measurement: Hz
accuracy_decimals: 1
disabled_by_default: false
force_update: false
- name: Voltage Output
id: ha_ups_makelsan_voltage_output
unit_of_measurement: VAC
device_class: voltage
accuracy_decimals: 1
disabled_by_default: false
force_update: false
ota:
password: !secret 'esphome_api_password'
safe_mode: true
port: 8266
reboot_timeout: 5min
num_attempts: 10
logger:
level: WARN
baud_rate: 0
tx_buffer_size: 512
deassert_rts_dtr: false
hardware_uart: UART0
logs: {}
esp8266_store_log_strings_in_flash: true
api:
password: !secret 'esphome_api_password'
port: 6053
reboot_timeout: 15min
uart:
- id: ha_ups_makelsan_bus
tx_pin:
number: 2
mode:
output: true
analog: false
input: false
open_drain: false
pullup: false
pulldown: false
inverted: false
rx_pin:
number: 0
mode:
input: true
analog: false
output: false
open_drain: false
pullup: false
pulldown: false
inverted: false
baud_rate: 2400
data_bits: 8
stop_bits: 1
parity: NONE
rx_buffer_size: 50
globals:
- id: ha_ups_makelsan_uart_data_queue_value
type: bool
restore_value: false
initial_value: 'true'
interval:
- interval: 1s
then:
- if:
condition:
wifi.connected: {}
then:
- if:
condition:
and:
- lambda: !lambda |-
return id(ha_ups_makelsan_uart_data_queue_value);
then:
- uart.write:
id: ha_ups_makelsan_bus
data:
- 0x46
- 0x0D
- globals.set:
id: ha_ups_makelsan_uart_data_queue_value
value: 'false'
else:
- uart.write:
id: ha_ups_makelsan_bus
data:
- 0x51
- 0x31
- 0x0D
- globals.set:
id: ha_ups_makelsan_uart_data_queue_value
value: 'true'
custom_component:
- lambda: !lambda |-
auto uart_data_ups = new MakelsanUPS(id(ha_ups_makelsan_bus));
return {uart_data_ups};
components:
- id: ha_ups_makelsan_uart_data_ups
binary_sensor:
- platform: custom
lambda: !lambda |-
auto ups_binary_sensor = (MakelsanUPS*) id(ha_ups_makelsan_uart_data_ups);
App.register_component(ups_binary_sensor);
return {
ups_binary_sensor->battery_low
,ups_binary_sensor->beeper_on
,ups_binary_sensor->eco_mode
,ups_binary_sensor->power_status
,ups_binary_sensor->shutdown_active
,ups_binary_sensor->test_in_progress
,ups_binary_sensor->ups_failed
,ups_binary_sensor->ups_type_is_standby
,ups_binary_sensor->utility_fail
};
binary_sensors:
- name: Battery Low
id: ha_ups_makelsan_battery_low
device_class: battery
disabled_by_default: false
- name: Beeper
id: ha_ups_makelsan_beeper_on
icon: mdi:bell-ring
disabled_by_default: false
- name: Eco Mode
id: ha_ups_makelsan_eco_mode
icon: mdi:leaf-circle
disabled_by_default: false
- name: Power Status
id: ha_ups_makelsan_power_status
device_class: plug
disabled_by_default: false
- name: Shutdown Active
id: ha_ups_makelsan_shutdown_active
icon: mdi:power
disabled_by_default: false
- name: Test in Progress
id: ha_ups_makelsan_test_in_progress
icon: mdi:test-tube
disabled_by_default: false
- name: UPS Failed
id: ha_ups_makelsan_ups_failed
icon: mdi:battery-alert
disabled_by_default: false
- name: UPS Type is Standby
id: ha_ups_makelsan_ups_type_is_standby
icon: mdi:power-standby
disabled_by_default: false
- name: Utility Fail
id: ha_ups_makelsan_utility_fail
icon: mdi:alert
disabled_by_default: false
switch:
- platform: uart
name: Beeper Toggle On/Off
icon: mdi:bell-ring
id: ha_ups_makelsan_beeper_toggle_onoff
data:
- 0x51
- 0x0D
disabled_by_default: false
- platform: uart
name: Test
icon: mdi:test-tube
id: ha_ups_makelsan_test
data:
- 0x54
- 0x0D
disabled_by_default: false
- platform: uart
name: Test - Cancel
icon: mdi:test-tube-off
id: ha_ups_makelsan_test_cancel
data:
- 0x43
- 0x54
- 0x0D
disabled_by_default: false
- platform: uart
name: Test - For Specified Time Period(Minutes 30)
icon: mdi:test-tube
id: ha_ups_makelsan_test_for_specified_time_period_30
data:
- 0x54
- 0x33
- 0x30
- 0x0D
disabled_by_default: false
- platform: uart
name: Test - Until Battery Low
icon: mdi:test-tube
id: ha_ups_makelsan_test_until_battery_low
data:
- 0x54
- 0x4C
- 0x0D
disabled_by_default: false
- platform: uart
name: Shutdown 5 Minutes Later
id: ha_ups_makelsan_shutdown
icon: mdi:power
data:
- 0x53
- 0x30
- 0x35
- 0x0D
disabled_by_default: false
- platform: uart
name: Shutdown 5 Minutes Later and Restore 1 Minute Later
id: ha_ups_makelsan_shutdown_restore
icon: mdi:power
data:
- 0x53
- 0x30
- 0x35
- 0x52
- 0x30
- 0x31
- 0x0D
disabled_by_default: false
- platform: uart
name: Shutdown Cancel
id: ha_ups_makelsan_shutdown_cancel
icon: mdi:alert-octagon-outline
data:
- 0x43
- 0x0D
disabled_by_default: false
time:
- platform: homeassistant
id: homeassistant_time
timezone: <+03>-3
update_interval: 15min
I caught this log.
Thanks in advance for your help.

Related

Binding loop detected for property "text"

My implementation is to keep track of whether I have placed the dot already •. I am trying to acheive the following: A • D • G • J • M • P • S • V • Z. The property binding for check, somehow it doesn't want to go back to 0. I also tried using a local var, but it will always be 0 or 1. How can I resolve this? I get the following error: Binding loop detected for property "text"
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: root
visible: true
anchors.fill: parent
property string letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
property int check: 0
readonly property real dx: 20
function midpoint(idx) {
return 20 + idx * root.dx;
}
function newLetters(index) {
if(root.letters[index] === "A"){
root.check = 0
return true
}
if(root.letters[index] === "D"){
root.check = 0
return true
}
if(root.letters[index] === "G"){
root.check = 0
return true
}
if(root.letters[index] === "J"){
root.check = 0
return true
}
if(root.letters[index] === "M"){
root.check = 0
return true
}
if(root.letters[index] === "P"){
root.check = 0
return true
}
if(root.letters[index] === "S"){
root.check = 0
return true
}
if(root.letters[index] === "V"){
root.check = 0
return true
}
if(root.letters[index] === "Z"){
root.check = 0
return true
}
else {
root.check = root.check + 1
}
}
Repeater {
model: 26
Text {
anchors.horizontalCenter: parent.left
anchors.horizontalCenterOffset: root.midpoint(index)
anchors.verticalCenter: parent.verticalCenter
text: root.newLetters(index) ? root.letters[index] : (root.check === 0 ? " • " : "")
}
}
}
}
Yes, by changing the root.check variable, the QQmlEngine will re-evaluate all bindings dependent on, including the newLetters function itself, thus leading to a binding loop.
I have been watching your recent questions (wondering what the end-goal is) and I think you should alter your newLetters function, to return the actual wanted text instead of the condition:
function newLetters(index) {
if(index < 24) //below Y, everything is regular
{
if(index % 3 == 0)
return root.letters[index]
if(index % 3 == 1)
return " ° "
}
else if(index == 24) //Y
{
return ""
}
else if(index == 25) //Z
{
return root.letters[index]
}
return ""
}

I tried to do BLE with ESP32, but the notifyCallback doesn't work

This text has been translated, so it may be poorly written.
Hello, I am trying to use M5stack to get the acceleration and ECG of my Polar OH1+, but the notifyCallback is not working.
I found some code in Python that did something similar, so I followed the same procedure to make the connection.
https://github.com/pareeknikhil/biofeedback/blob/master/Polar%20Device%20Data%20Stream/Accelerometer/main.py
According to this, I found out that this is the procedure to follow.
Read the value of pmd control uuid
Write to the pmd control uuid
Read the pmd data uuid
The following program tries to achieve that with M5stack.
//===== header file & define & global variable =====
#include"BLEDevice.h"
boolean doConnect = false;
volatile boolean isConnected = false;
boolean doScan = false;
BLEUUID pmd_serviceUUID ("FB005C80-02E7-F387-1CAD-8ACD2D8DF0C8");
BLEUUID pmd_dataUUID ("FB005C82-02E7-F387-1CAD-8ACD2D8DF0C8");
BLEUUID pmd_ctrlUUID ("FB005C81-02E7-F387-1CAD-8ACD2D8DF0C8");
BLEAdvertisedDevice* myDevice;
BLEClient* pClient;
String SensorName = "Polar OH1 87C4C425"; // SDから読み取る
//===========================================
//===== class & function ====================
class MyClientCallback: public BLEClientCallbacks{
void onConnect(BLEClient* pclient){ }
void onDisconnect(BLEClient* pclient){
isConnected = false;
Serial.println("onDisconnetct");
}
};
// BLEデバイスを検索する
class MyAdvertisedDeviceCallback: public BLEAdvertisedDeviceCallbacks{
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.println(advertisedDevice.toString().c_str());
// 指定デバイスなら接続する
if(SensorName.equals(advertisedDevice.getName().c_str())){
Serial.print("Connect BLE device : ");
Serial.println(advertisedDevice.toString().c_str());
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
}
}
};
void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
Serial.print("Notify callback for characteristic ");
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
Serial.print(" of data length ");
Serial.println(length);
Serial.print("data: ");
for (int i = 0; i <= length - 1; i++) {
Serial.print(String(*(pData + i), HEX));
Serial.print(" ");
}
Serial.println();
}
bool connectToServer(){
Serial.print("connection to : ");
Serial.println(myDevice->getAddress().toString().c_str());
pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback() );
pClient->connect(myDevice);
Serial.println(" - Created to server");
BLERemoteService* pRemoteService = pClient->getService(pmd_serviceUUID);
if (pRemoteService == nullptr) {
Serial.println("Failed to find our service UUID: ");
Serial.println(pmd_serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.print(" - Found service ( ");
Serial.print(pmd_serviceUUID.toString().c_str());
Serial.println(" )");
static BLERemoteCharacteristic* pControlCharacteristic;
pControlCharacteristic = pRemoteService->getCharacteristic(pmd_ctrlUUID);
if( pControlCharacteristic == nullptr ){
Serial.print("Failed to find out characteristic UUID : ");
Serial.println(pmd_ctrlUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println("characteristics");
std::map<uint16_t, BLERemoteCharacteristic*>* mapCharacteristics = pRemoteService->getCharacteristicsByHandle();
for (std::map<uint16_t, BLERemoteCharacteristic*>::iterator i = mapCharacteristics->begin(); i != mapCharacteristics->end(); ++i) {
Serial.print(" - characteristic UUID : ");
Serial.print(i->second->getUUID().toString().c_str());
Serial.print(" Broadcast:");
Serial.print(i->second->canBroadcast()?'O':'X');
Serial.print(" Read:");
Serial.print(i->second->canRead()?'O':'X');
Serial.print(" WriteNoResponse:");
Serial.print(i->second->canWriteNoResponse()?'O':'X');
Serial.print(" Write:");
Serial.print(i->second->canWrite()?'O':'X');
Serial.print(" Notify:");
Serial.print(i->second->canNotify()?'O':'X');
Serial.print(" Indicate:");
Serial.print(i->second->canIndicate()?'O':'X');
Serial.println();
}
static BLERemoteCharacteristic* pDataCharacteristic;
pDataCharacteristic = pRemoteService->getCharacteristic(pmd_dataUUID);
if( pDataCharacteristic == nullptr ){
Serial.print("Failed to find out characteristic UUID : ");
Serial.println(pmd_dataUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.print(" - Add Notify ( ");
Serial.print(pmd_dataUUID.toString().c_str());
Serial.println(" )");
if(pDataCharacteristic->canNotify()){
std::string value = pControlCharacteristic->readValue();
uint8_t data[14] = {0x02,
0x02,
0x00,
0x01,
0xC8,
0x00,
0x01,
0x01,
0x10,
0x00,
0x02,
0x01,
0x08,
0x00,};
pControlCharacteristic->writeValue(data,14,false);
Serial.println(" - Set value");
Serial.println(" - Can Notify");
pDataCharacteristic->registerForNotify(notifyCallback);
}
isConnected = true;
return true;
}
//===========================================
//===== setting =============================
void setup() {
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
BLEDevice::init("");
static BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallback());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
}
//===========================================
//===== main ================================
void loop(){
if(doConnect==true){
if(connectToServer()){
Serial.println("now connected to BLE Server.");
}else{
Serial.println("faild to connect to the server.");
}
doConnect = false;
}
if( isConnected == false && doScan == true ) BLEDevice::getScan()->start(0);
delay(1000);
}
//===========================================
When this was done, the serial monitor display looked like the following.
Starting Arduino BLE Client application...
Name: , Address: 5a:f3:e5:97:72:be, manufacturer data: 060001092006319b0f7cab7c18b3ad1f11d4f6475cf638678bd51cf02d
Name: , Address: 33:20:7d:41:97:52, serviceUUID: 0000fd6f-0000-1000-8000-00805f9b34fb
Name: , Address: 11:27:f2:c5:92:98, manufacturer data: 0600010920029ac7ae5b723ad210a6450c28780429ca56a82bae79a076
Name: Polar OH1 87C4C425, Address: a0:9e:1a:87:c4:c4, manufacturer data: 6b00720851a77b02000000007a01053b005d, serviceUUID: 0000180d-0000-1000-8000-00805f9b34fb, serviceUUID: 0000feee-0000-1000-8000-00805f9b34fb
Connect BLE device : Name: Polar OH1 87C4C425, Address: a0:9e:1a:87:c4:c4, manufacturer data: 6b00720851a77b02000000007a01053b005d, serviceUUID: 0000180d-0000-1000-8000-00805f9b34fb, serviceUUID: 0000feee-0000-1000-8000-00805f9b34fb
connection to : a0:9e:1a:87:c4:c4
- Created client
- Created to server
- Found service ( fb005c80-02e7-f387-1cad-8acd2d8df0c8 )
characteristics
- characteristic UUID : fb005c81-02e7-f387-1cad-8acd2d8df0c8 Broadcast:X Read:O WriteNoResponse:X Write:O Notify:X Indicate:O
- characteristic UUID : fb005c82-02e7-f387-1cad-8acd2d8df0c8 Broadcast:X Read:X WriteNoResponse:X Write:X Notify:O Indicate:X
- Add Notify ( fb005c82-02e7-f387-1cad-8acd2d8df0c8 )
- Set value
- Can Notify
now connected to BLE Server.
It even shows Can Notify, but notyfiCallback does not work. Can you please tell me why it is not working?
Also, the byte sequence that is written to pmd control is supposed to be based on this page.
https://github.com/polarofficial/polar-ble-sdk/blob/master/technical_documentation/Polar_Measurement_Data_Specification.pdf
Also, this is the output of the serial monitor when the CoreDebugLebel of the ESP32 is set to Debug in the ArduinoIDE.
Starting Arduino BLE Client application...
[D][BLEAdvertisedDevice.cpp:472] setRSSI(): - setRSSI(): rssi: -83
[D][BLEAdvertisedDevice.cpp:292] parseAdvertisement(): Type: 0xff (), length: 29, data: 0600010920021a14c867a55d256f3c35b8286b3c90bfe1fa95ce255ccb
[D][BLEAdvertisedDevice.cpp:449] setManufacturerData(): - manufacturer data: 0600010920021a14c867a55d256f3c35b8286b3c90bfe1fa95ce255ccb
Name: , Address: 7e:0a:d2:c6:94:26, manufacturer data: 0600010920021a14c867a55d256f3c35b8286b3c90bfe1fa95ce255ccb
[D][BLEAdvertisedDevice.cpp:472] setRSSI(): - setRSSI(): rssi: -84
[D][BLEAdvertisedDevice.cpp:292] parseAdvertisement(): Type: 0xff (), length: 29, data: 06000109200670be05b68e63a90d3ca0a091e9c4982a95f8f08888583f
[D][BLEAdvertisedDevice.cpp:449] setManufacturerData(): - manufacturer data: 06000109200670be05b68e63a90d3ca0a091e9c4982a95f8f08888583f
Name: , Address: 71:56:ce:5b:12:af, manufacturer data: 06000109200670be05b68e63a90d3ca0a091e9c4982a95f8f08888583f
[D][BLEAdvertisedDevice.cpp:472] setRSSI(): - setRSSI(): rssi: -40
[D][BLEAdvertisedDevice.cpp:292] parseAdvertisement(): Type: 0x01 (), length: 1, data: 04
[D][BLEAdvertisedDevice.cpp:292] parseAdvertisement(): Type: 0xff (), length: 15, data: 6b00720851a77b02000000003f0043
[D][BLEAdvertisedDevice.cpp:449] setManufacturerData(): - manufacturer data: 6b00720851a77b02000000003f0043
[D][BLEAdvertisedDevice.cpp:292] parseAdvertisement(): Type: 0x09 (), length: 18, data: 506f6c6172204f4831203837433443343235
[D][BLEAdvertisedDevice.cpp:461] setName(): - setName(): name: Polar OH1 87C4C425
Name: Polar OH1 87C4C425, Address: a0:9e:1a:87:c4:c4, manufacturer data: 6b00720851a77b02000000003f0043
Connect BLE device : Name: Polar OH1 87C4C425, Address: a0:9e:1a:87:c4:c4, manufacturer data: 6b00720851a77b02000000003f0043
connection to : a0:9e:1a:87:c4:c4
- Created client
[I][BLEDevice.cpp:614] addPeerDevice(): add conn_id: 0, GATT role: client
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
- Found service ( fb005c80-02e7-f387-1cad-8acd2d8df0c8 )
[D][BLERemoteService.cpp:193] retrieveCharacteristics(): Found a characteristic: Handle: 63, UUID: fb005c81-02e7-f387-1cad-8acd2d8df0c8
[D][BLERemoteCharacteristic.cpp:293] retrieveDescriptors(): Found a descriptor: Handle: 64, UUID: 00002902-0000-1000-8000-00805f9b34fb
[D][BLERemoteService.cpp:193] retrieveCharacteristics(): Found a characteristic: Handle: 66, UUID: fb005c82-02e7-f387-1cad-8acd2d8df0c8
[D][BLERemoteCharacteristic.cpp:293] retrieveDescriptors(): Found a descriptor: Handle: 67, UUID: 00002902-0000-1000-8000-00805f9b34fb
characteristics
- characteristic UUID : fb005c81-02e7-f387-1cad-8acd2d8df0c8 Broadcast:X Read:O WriteNoResponse:X Write:O Notify:X Indicate:O
- characteristic UUID : fb005c82-02e7-f387-1cad-8acd2d8df0c8 Broadcast:X Read:X WriteNoResponse:X Write:X Notify:O Indicate:X
- Add Notify ( fb005c82-02e7-f387-1cad-8acd2d8df0c8 )
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
- Can Notify
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEDevice.cpp:148] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
[D][BLEClient.cpp:177] gattClientEventHandler(): gattClientEventHandler [esp_gatt_if: 4] ... Unknown
now connected to BLE Server.
The specification you provided shows the correct flow of messages needed to start streaming. You have to start by requesting the streaming settings first.
For requesting the ppi settings just send 0x01 0x03 to the P=MD control point.
While looking at the source code of the polar-ble-sdk I found this in BlePMDClient.java (I added comments to the relevant parts):
public static class PpiData {
public static class PPSample {
public final int hr;
public final int ppInMs;
public final int ppErrorEstimate;
public final int blockerBit;
public final int skinContactStatus;
public final int skinContactSupported;
public PPSample(#NonNull byte[] data) {
// Convert data[0] to heart rate using a bitwise AND operation
// with 0xFF as long
hr = (int) ((long) data[0] & 0xFFL);
// Convert data[0] and data[1] to time between peaks in ms using
// BleUtils.convertArrayToUnsignedLong
ppInMs = (int) BleUtils.convertArrayToUnsignedLong(data, 1, 2);
// Same for the error estimate
ppErrorEstimate = (int) BleUtils.convertArrayToUnsignedLong(data, 3, 2);
// The last byte contains multiple flags that get read by
// bitshifting. First bit is the blockerBit, second the
// skinContactStatus, third skinContactSupported
blockerBit = data[5] & 0x01;
skinContactStatus = (data[5] & 0x02) >> 1;
skinContactSupported = (data[5] & 0x04) >> 2;
}
}
public final List<PPSample> ppSamples = new ArrayList<>();
public final long timeStamp;
public PpiData(#NonNull byte[] data, long timeStamp) {
int offset = 0;
this.timeStamp = timeStamp;
while (offset < data.length) {
final int finalOffset = offset;
ppSamples.add(new PPSample(Arrays.copyOfRange(data, finalOffset, finalOffset + 6)));
offset += 6;
}
}
}
Now we know how to convert the 6 bytes of the data frame, but the data you receive has mostly 40 bytes and looks like this:
03 00 00 00 00 00 00 00 00 00 00 34 02 1e 00 07 00 f2 01 1e 00 07 00 05 03 1e 00 07 00 63 03 1e 00 06 00 8e 03 1e 00 06
The conversion from the raw data to the different data frames happens in Line 888 of the same file.
The first 10 bytes of the received data is extracted here:
PmdMeasurementType type = PmdMeasurementType.fromId(data[0]);
final long timeStamp = BleUtils.convertArrayToUnsignedLong(data, 1, 8);
final long frameType = BleUtils.convertArrayToUnsignedLong(data, 9, 1);
final byte[] content = new byte[data.length - 10];
System.arraycopy(data, 10, content, 0, content.length);
Looking at the data you received we now know the following:
Type
Data
Description
PmdMeasurementType
03
The type of data, here it is the same as the requested. 3 = PPI
Timestamp
00 00 00 00 00 00 00 00
The timestamp, somehow 0 here
Frametype
00
Type of data that follows
content
00 34 02 1e 00 07 00 f2 01 1e 00 07 00 05 03 1e 00 07 00 63 03 1e 00 06 00 8e 03 1e 00 06
The rest of the raw data is called content and gets used further
Since we are looking at PPI data the following case of the switch-case applies:
case PPI:
if (frameType == 0) {
RxUtils.emitNext(ppiObservers, object -> object.onNext(new PpiData(content, timeStamp)));
} else {
BleLogger.w(TAG, "Unknown PPI frame type received");
}
break;
Only a frametype of zero is allowed here and this is exactly what you received. You sometimes received 40 bytes and other times only 16. Since the first 10 are not part of the actual data we can now assume that the 40 bytes contain 5 PPI measurements (5 * 6 bytes = 30 bytes) and the 16 bytes contain only one measurement.
If we take a look at the first PPI measurement in the dataset you received we can now read the following:
Raw data
Type
Value
00
Heart rate
0
34 02
Peak to peak in ms
564 ms
1e 00
Peak to peak error estimate
30
07 -> 00000111
Error bit, skin contact status bit, skin contact status supported bit
Error: true, skin contact: true, skin contact supported: true
Since you do not have a heart rate reading and the error bit is set I would assume something went wrong, maybe you did not wear the device while testing.

Mbed Cortex-m hardfault when sending data via TCP

I have a TCPSocket* object which holds a connection to a client. This object is passed to another object to send data back to the client:
uint32_t count = 10;
char* message = new char[4];
message[0] = count & 0xff;
message[1] = (count >> 8) & 0xff;
message[2] = (count >> 16) & 0xff;
message[3] = (count >> 24) & 0xff;
client->send(&message, 4);
When this part of the program is called, the following appears on the serial line, and no data is received by the client:
++ MbedOS Fault Handler ++
FaultType: HardFault
Context:
R0 : 00000000
R1 : 10008000
R2 : 00000004
R3 : 2007C000
R4 : 10000914
R5 : 00000000
R6 : 00000000
R7 : 10004330
R8 : 10004320
R9 : FFFFF435
R10 : 00000000
R11 : 00000000
R12 : 00012AC1
SP : 10002AF0
LR : 0000D1A1
PC : 00005938
xPSR : 21000000
PSP : 10002AD0
MSP : 10007FD8
CPUID: 412FC230
HFSR : 40000000
MMFSR: 00000000
BFSR : 00000082
UFSR : 00000000
DFSR : 0000000A
AFSR : 00000000
BFAR : 10008010
Mode : Thread
Priv : Privileged
Stack: PSP
-- MbedOS Fault Handler --
++ MbedOS Error Info ++
Error Status: 0x80FF013D Code: 317 Module: 255
Error Message: Fault exception
Location: 0xD337
Error Value: 0x5938
Current Thread: main Id: 0x10002B48 Entry: 0xD7D7 StackSize: 0x1000 StackMem: 0x10001B48 SP: 0x10007F88
For more info, visit: https://armmbed.github.io/mbedos-error/?error=0x80FF013D
-- MbedOS Error Info --
Everything is in one thread so I cant see what could be causing this.
These are the relevant parts of the program:
main:
// Network interface
EthernetInterface net;
TCPSocket listener; //listens for incoming connection requests
TCPSocket* client;
CommandProcessor commandProcessor(client);
int main() {
int remaining;
int rcount;
char *p;
char *buffer = new char[16];
nsapi_size_or_error_t result;
int n = net.set_network("192.168.1.103","255.255.255.0","192.168.1.2");
pc.printf("\n Success? %d\n", n);
net.connect();
listener.open(&net);
listener.bind(3045);
listener.listen(1);
client = listener.accept(NULL);
client->set_timeout(1000);
led1 = 1;
while(1) {
int remaining = 16;
int rcount = 0;
p = buffer;
while (remaining > 0 && 0 < (result = client->recv(p, remaining))) {
p += result;
rcount += result;
remaining -= result;
}
if (remaining == 0) //full message received
{
commandProcessor.process(buffer);
}
}
}
CommandProcessor:
CommandProcessor::CommandProcessor(TCPSocket* client)
{
this->client = client;
}
void CommandProcessor::process(char* message)
{
switch(message[0]) { //Command is first byte of message
case 0x3: {
uint32_t count = 10 ;
char* message = new char[4];
message[0] = count & 0xff;
message[1] = (count >> 8) & 0xff;
message[2] = (count >> 16) & 0xff;
message[3] = (count >> 24) & 0xff;
client->send(message, 4);
}
}
}
commandProcessor's client is NULL when you call commandProcessor.process(buffer).
Why don’t you create an instance of CommandProcessor after you get a pointer to a socket from accept().
CommandProcessor* commandProcessor;
client = listener.accept(NULL);
commandProcessor = new CommandProcessor(client);
commandProcessor->process(buffer);
Alternatively, you can set client with a function like this.
void CommandProcessor::setClient(TCPSocket* client) {
this->client = client;
}
usage:
client = listener.accept(NULL);
commandProcessor.setClient(client);

Is it possible to control two 7 seg displays with analog output from Arduino?

I am playing around with arduino and i am trying to build stuff.
While adding more and more to my project i found some difficulties. I run out of digital pins due to my LCD and i would like to control 2 7 seg displays to project some numbers from a temperature sensor.
i have created this schematic to show you where i am at :
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 10, 8, 7, 4);
// Temperature Pins
const int analogIn = A1;
int RawValue= 0;
double Voltage = 0;
double tempC = 0;
double tempF = 0;
//LDR Pins
int LDR_sensor = A0;
int LDR_value = 0;
//array to save Seven Seg pin configuration of numbers
int num_array[10][7] = { { 1,1,1,1,1,1,0 }, // 0
{ 0,1,1,0,0,0,0 }, // 1
{ 1,1,0,1,1,0,1 }, // 2
{ 1,1,1,1,0,0,1 }, // 3
{ 0,1,1,0,0,1,1 }, // 4
{ 1,0,1,1,0,1,1 }, // 5
{ 1,0,1,1,1,1,1 }, // 6
{ 1,1,1,0,0,0,0 }, // 7
{ 1,1,1,1,1,1,1 }, // 8
{ 1,1,1,0,0,1,1 }}; // 9
int first_digit = 0 ;
int second_digit = 0 ;
//function header
void Num_Write(int);
void setup() {
Serial.begin(9600);
// set up the LCD's number of columns and rows:
// lcd.begin(16, 2);
// lcd.clear();
// delay(2000);
// set pin modes
------------Here i should place the analog pins for the multiplexer
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
// lcd.setCursor(0, 1);
//Read values for the temperature sensor
RawValue = analogRead(analogIn);
Voltage = (RawValue / 1023.0) * 5000; // 5000 to get millivots.
tempC = (Voltage-500) * 0.1; // 500 is the offset
// Serial print the values for debug purposes
Serial.print("Raw Value = " ); // shows pre-scaled value
Serial.print(RawValue);
Serial.print("\t milli volts = "); // shows the voltage measured
Serial.print(Voltage,0); //
Serial.print("\t Temperature in C = ");
Serial.println(tempC);
Serial.println(int(tempC));
first_digit = int(tempC) / 10 ;
Serial.print("The first digit is : " );
Serial.print(first_digit); // Get the first number for the seven seg display
second_digit = int(tempC) - (first_digit * 10) ;
Serial.print("\tThe second digit is : ");
Serial.println(second_digit);
Serial.println("\n");
// Read values for the LDR sensor
LDR_value = analogRead(LDR_sensor);
//Print values to the LCD screen
// lcd.setCursor(0, 0);
// lcd.print("The value of LDR is :");
// lcd.setCursor(0, 1);
// lcd.print(LDR_value) ;
delay(5000);
// lcd.print("") ;
}
So my question is if it is possible to output 2 different numbers (first_digit and second_digit) on the 7seg displays. I got some ideas from this thread . I think that the schematic is ok though.

Qt Quick Controls 2 on-screen Number pad on spinbox

In a touchpanel application, is it possible to edit the integer value of a qml SpinBox, from QtQuickControls 2.0, in such a way that a numeric keypad appears on-screen and one can enter the exact value?
Or do you have any idea on how to embed this predefined number pad in a customized spinbox, that should pop-up when the user taps on the integer number?
The numpad can be set to be invisible and put on top of everything, then you can have a function to enable its visibility and set it's target. When you are done with typing the number, you set the target value to the numpad value, and hide it again. This way you can target different spinboxes.
As of the actual method to request the keypad, you can put a MouseArea to fill the spinbox on top of it. Or you can make the mouse area narrower, so that the plus/minus buttons of the spinbox are still clickable.
Also keep in mind that numpad you have linked is not actually functional.
Thanks for your suggestion. I came up with a first quick solution based on your idea and the number pad from the example section. I post it here, just in case it helps someone else as starting point. I am a QML beginner, so happy to get any improvements or corrections.
See attached screenshot of the numeric virtual touchpad appearing when clicking on the spinbox number
SpinBox {
id: boxCommPos
x: 50
y: 50
z: 0
width: 200
height: 50
to: 360
editable: false
contentItem: TextInput {
z: 1
text: parent.textFromValue(parent.value, parent.locale)
font: parent.font
//color: "#21be2b"
//selectionColor: "#21be2b"
//selectedTextColor: "#ffffff"
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
readOnly: !parent.editable
validator: parent.validator
inputMethodHints: Qt.ImhFormattedNumbersOnly
}
Label {
id: commUnits
x: 125
y: 0
z: 3
text: qsTr("º")
font.pointSize: 16
anchors.right: parent.right
anchors.rightMargin: 45
}
MouseArea {
id: padCommPos
x: 40
y: 0
z: 2
width: parent.width-x-x
height: 50
onClicked: touchpad.visible=true
}
}
NumberTouchpad {
id: touchpad
x: 470
y: -20
z: 1
scale: 0.90
visible: false
Rectangle {
id: backrect
x: -parent.x/parent.scale
z: -1
width: parent.parent.width/parent.scale
height: parent.parent.height/parent.scale
color: "#eceeea"
opacity: 0.5
MouseArea {
anchors.fill: parent
}
}
}
The file NumberPad.qml
import QtQuick 2.0
Grid {
columns: 3
columnSpacing: 32
rowSpacing: 16
signal buttonPressed
Button { text: "7" }
Button { text: "8" }
Button { text: "9" }
Button { text: "4" }
Button { text: "5" }
Button { text: "6" }
Button { text: "1" }
Button { text: "2" }
Button { text: "3" }
Button { text: "0" }
Button { text: "."; dimmable: true }
//Button { text: " " }
Button { text: "±"; color: "#6da43d"; operator: true; dimmable: true }
//Button { text: "−"; color: "#6da43d"; operator: true; dimmable: true }
//Button { text: "+"; color: "#6da43d"; operator: true; dimmable: true }
//Button { text: "√"; color: "#6da43d"; operator: true; dimmable: true }
//Button { text: "÷"; color: "#6da43d"; operator: true; dimmable: true }
//Button { text: "×"; color: "#6da43d"; operator: true; dimmable: true }
Button { text: "C"; color: "#6da43d"; operator: true }
Button { text: "✔"; color: "#6da43d"; operator: true; dimmable: true }
Button { text: "X"; color: "#6da43d"; operator: true }
}
Display.qml
// Copyright (C) 2015 The Qt Company Ltd.
import QtQuick 2.0
import QtQuick.Window 2.0
Item {
id: display
property real fontSize: Math.floor(Screen.pixelDensity * 10.0)
property string fontColor: "#000000"
property bool enteringDigits: false
property int maxDigits: (width / fontSize) + 1
property string displayedOperand
property string errorString: qsTr("ERROR")
property bool isError: displayedOperand === errorString
function displayOperator(operator)
{
listView.model.append({ "operator": operator, "operand": "" })
enteringDigits = true
listView.positionViewAtEnd()
//console.log("display",operator);
}
function newLine(operator, operand)
{
displayedOperand = displayNumber(operand)
listView.model.append({ "operator": operator, "operand": displayedOperand })
enteringDigits = false
listView.positionViewAtEnd()
//console.log("newLine",operator);
}
function appendDigit(digit)
{
if (!enteringDigits)
listView.model.append({ "operator": "", "operand": "" })
var i = listView.model.count - 1;
listView.model.get(i).operand = listView.model.get(i).operand + digit;
enteringDigits = true
listView.positionViewAtEnd()
//console.log("num is ", digit);
}
function setDigit(digit)
{
var i = listView.model.count - 1;
listView.model.get(i).operand = digit;
listView.positionViewAtEnd()
//console.log("setDigit",digit);
}
function clear()
{
displayedOperand = ""
if (enteringDigits) {
var i = listView.model.count - 1
if (i >= 0)
listView.model.remove(i)
enteringDigits = false
}
//console.log("clearing");
}
// Returns a string representation of a number that fits in
// display.maxDigits characters, trying to keep as much precision
// as possible. If the number cannot be displayed, returns an
// error string.
function displayNumber(num) {
if (typeof(num) != "number")
return errorString;
var intNum = parseInt(num);
var intLen = intNum.toString().length;
// Do not count the minus sign as a digit
var maxLen = num < 0 ? maxDigits + 1 : maxDigits;
if (num.toString().length <= maxLen) {
if (isFinite(num))
return num.toString();
return errorString;
}
// Integer part of the number is too long - try
// an exponential notation
if (intNum == num || intLen > maxLen - 3) {
var expVal = num.toExponential(maxDigits - 6).toString();
if (expVal.length <= maxLen)
return expVal;
}
// Try a float presentation with fixed number of digits
var floatStr = parseFloat(num).toFixed(maxDigits - intLen - 1).toString();
if (floatStr.length <= maxLen)
return floatStr;
return errorString;
}
Item {
id: theItem
width: parent.width
height: parent.height
Rectangle {
id: rect
x: 0
color: "#eceeea"
height: parent.height
width: display.width
}
/*Image {
anchors.right: rect.left
source: "images/paper-edge-left.png"
height: parent.height
fillMode: Image.TileVertically
}
Image {
anchors.left: rect.right
source: "images/paper-edge-right.png"
height: parent.height
fillMode: Image.TileVertically
}
Image {
id: grip
source: "images/paper-grip.png"
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
}*/
ListView {
id: listView
width: display.width
height: display.height
delegate: Item {
height: display.fontSize * 1.1
width: parent.width
Text {
id: operator
font.pixelSize: display.fontSize
color: "#6da43d"
text: model.operator
}
Text {
id: operand
y:5
font.pixelSize: display.fontSize
color: display.fontColor
anchors.right: parent.right
anchors.rightMargin: 22
text: model.operand
}
}
model: ListModel { }
}
}
}
calculator.qs
// Copyright (C) 2015 The Qt Company Ltd.
var curVal = 0
var memory = 0
var lastOp = ""
var previousOperator = ""
var digits = ""
function disabled(op) {
if (op == "✔")
display.fontColor="#000000"
if (op=="X")
return false
else if (op == "✔" && (digits.toString().search(/\./) != -1 || digits.toString().search(/-/)!= -1 || parseInt(digits)>359)) {
display.fontColor="#ff0000"
return true
}
else if (digits == "" && !((op >= "0" && op <= "9") || op == "."))
return true
else if (op == '=' && previousOperator.length != 1)
return true
else if (op == "." && digits.toString().search(/\./) != -1) {
return true
} else if (op == "√" && digits.toString().search(/-/) != -1) {
return true
} else {
return false
}
}
function digitPressed(op)
{
if (disabled(op))
return
if (digits.toString().length >= display.maxDigits)
return
if (lastOp.toString().length == 1 && ((lastOp >= "0" && lastOp <= "9") || lastOp == ".") ) {
digits = digits + op.toString()
display.appendDigit(op.toString())
} else {
digits = op
display.appendDigit(op.toString())
}
lastOp = op
}
function operatorPressed(op)
{
if (disabled(op))
return
lastOp = op
if (op == "±") {
digits = Number(digits.valueOf() * -1)
display.setDigit(display.displayNumber(digits))
return
}
if (previousOperator == "+") {
digits = Number(digits.valueOf()) + Number(curVal.valueOf())
} else if (previousOperator == "−") {
digits = Number(curVal.valueOf()) - Number(digits.valueOf())
} else if (previousOperator == "×") {
digits = Number(curVal) * Number(digits.valueOf())
} else if (previousOperator == "÷") {
digits = Number(curVal) / Number(digits.valueOf())
}
if (op == "+" || op == "−" || op == "×" || op == "÷") {
previousOperator = op
curVal = digits.valueOf()
digits = ""
display.displayOperator(previousOperator)
return
}
if (op == "=") {
display.newLine("=", digits.valueOf())
}
curVal = 0
previousOperator = ""
if (op == "1/x") {
digits = (1 / digits.valueOf()).toString()
} else if (op == "x^2") {
digits = (digits.valueOf() * digits.valueOf()).toString()
} else if (op == "Abs") {
digits = (Math.abs(digits.valueOf())).toString()
} else if (op == "Int") {
digits = (Math.floor(digits.valueOf())).toString()
} else if (op == "√") {
digits = Number(Math.sqrt(digits.valueOf()))
display.newLine("√", digits.valueOf())
} else if (op == "mc") {
memory = 0;
} else if (op == "m+") {
memory += digits.valueOf()
} else if (op == "mr") {
digits = memory.toString()
} else if (op == "m-") {
memory = digits.valueOf()
} else if (op == "backspace") {
digits = digits.toString().slice(0, -1)
display.clear()
display.appendDigit(digits)
} else if (op == "✔") {
window.visible=false
boxCommPos.value=parseInt(digits)
display.clear()
curVal = 0
memory = 0
lastOp = ""
digits = ""
} else if (op == "X") {
window.visible=false
display.clear()
curVal = 0
memory = 0
lastOp = ""
digits = ""
}
// Reset the state on 'C' operator or after
// an error occurred
if (op == "C" || display.isError) {
display.clear()
curVal = 0
memory = 0
lastOp = ""
digits = ""
}
}
Button.qml
// Copyright (C) 2015 The Qt Company Ltd.
import QtQuick 2.0
Item {
id: button
property alias text: textItem.text
property color color: "#eceeea"
property bool operator: false
property bool dimmable: false
property bool dimmed: false
width: 30
height: 50
Text {
id: textItem
font.pixelSize: 48
wrapMode: Text.WordWrap
lineHeight: 0.75
color: (dimmable && dimmed) ? Qt.darker(button.color) : button.color
Behavior on color { ColorAnimation { duration: 120; easing.type: Easing.OutElastic} }
states: [
State {
name: "pressed"
when: mouse.pressed && !dimmed
PropertyChanges {
target: textItem
color: Qt.lighter(button.color)
}
}
]
}
MouseArea {
id: mouse
anchors.fill: parent
anchors.margins: -5
onClicked: {
if (operator)
window.operatorPressed(parent.text)
else
window.digitPressed(parent.text)
}
}
function updateDimmed() {
dimmed = window.isButtonDisabled(button.text)
}
Component.onCompleted: {
numPad.buttonPressed.connect(updateDimmed)
updateDimmed()
}
}

Resources