diff --git a/pio/serial-plotter.py b/pio/serial-plotter.py new file mode 100755 index 000000000..5abde9c72 --- /dev/null +++ b/pio/serial-plotter.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python + +""" + serial-plotter.py - for Tasmota + + Copyright (C) 2020 Christian Baars + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Requirements: + - Python + - pip3 matplotlib + - a Tasmotadriver that plots + +Instructions: + expects serial data in the format: + 'PLOT: graphnumber value' + graph (1-4) + integer value + Code snippet example: (last value will be ignored) + AddLog_P2(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"),button_index+1, _value, Button.touch_hits[button_index]); + +Usage: + set serial config in code + ./serial-plotter.py + set output in tasmota, e.g.; TouchCal 1..4 (via Textbox) + +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation +from matplotlib.widgets import TextBox +import time +import serial +import argparse + +#default values +port = '/dev/cu.SLAB_USBtoUART' +baud = 115200 + +#command line input +parser = argparse.ArgumentParser() +parser.add_argument("--port", "-p", help="change serial port, default: " + port) +parser.add_argument("--baud", "-b", help="change baud rate, default: " + str(baud)) +args = parser.parse_args() +if args.port: + print("change serial port to %s" % args.port) + port = args.port +if args.baud: + print("change baud rate to %s" % args.baud) + baud = args.baud + + +#time range +dt = 0.01 +t = np.arange(0.0, 100, dt) + +#lists for the data +xs = [0] #counting up x +ys = [[0],[0],[0],[0]] #4 fixed graphs for now +max_y = 1 +# min_y = 0 + +fig = plt.figure('Tasmota Serial Plotter') +ax = fig.add_subplot(111, autoscale_on=True, xlim=(0, 200), ylim=(0, 20)) #fixed x scale for now, y will adapt +ax.grid() + +line1, = ax.plot([], [], color = "r", label='G 1') +line2, = ax.plot([], [], color = "g", label='G 2') +line3, = ax.plot([], [], color = "b", label='G 3') +line4, = ax.plot([], [], color = "y", label='G 4') + +time_template = 'time = %.1fs' +time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes) + +ser = serial.Serial() +ser.port = port +ser.baudrate = baud +ser.timeout = 0 #return immediately +try: + ser.open() +except: + print("Could not connect to serial with settings: " + str(ser.port) + ' at ' + str(ser.baudrate) + 'baud') + print("port available?") + exit() + +if ser.is_open==True: + print("Serial Plotter started ...:") + plt.title('connected to ' + str(ser.port) + ' at ' + str(ser.baudrate) + 'baud') +else: + print("Could not connect to serial: " + str(ser.port) + ' at ' + str(ser.baudrate) + 'baud') + plt.title('NOT connected to ' + str(ser.port) + ' at ' + str(ser.baudrate) + 'baud') + +def init(): + line1.set_data([], []) + line2.set_data([], []) + line3.set_data([], []) + line4.set_data([], []) + time_text.set_text('') + return [line1,line2,line3,line4,time_text ] #was line + + +def parse_line(data_line): + pos = data_line.find("PLOT:", 10) + if pos<0: + # print("wrong format") + return 0,0 + + raw_data = data_line[pos+6:] + val_list = raw_data.split(',') + try: + g = int(val_list[0]) + v = int(val_list[1]) + return g, v + except: + return 0,0 + +def update(num, line1, line2): + global xs, ys, max_y + + time_text.set_text(time_template % (num*dt) ) + + receive_data = str(ser.readline()) #string + + g, v = parse_line(receive_data) + if (g in range(1,5)): + # print(v,g) + if v>max_y: + max_y = v + print(max_y) + ax.set_ylim([0, max_y * 1.2]) + + idx = 0 + for y in ys: + y.append(y[-1]) + if idx == g-1: + y[-1] = v + idx = idx +1 + xs.append(xs[-1]+1) + + if len(ys[0])>200: + xs.pop() + for y in ys: + y.pop(0) + line1.set_data(xs, ys[0]) + line2.set_data(xs, ys[1]) + line3.set_data(xs, ys[2]) + line4.set_data(xs, ys[3]) + return [line1,line2,line3,line4, time_text] + +def handle_close(evt): + print('Closing serial connection') + ser.close() + print('Closed serial plotter') + +ani = animation.FuncAnimation(fig, update, None, fargs=[line1, line2], + interval=10, blit=True, init_func=init) + +ax.set_xlabel('Last 200 Samples') +ax.set_ylabel('Values') + +fig.canvas.mpl_connect('close_event', handle_close) + +def submit(text): + print (text) + ser.write(text.encode() + "\n".encode()) + +plt.subplots_adjust(bottom=0.25) +axbox = plt.axes([0.15, 0.05, 0.7, 0.075]) +text_box = TextBox(axbox, 'Send:', initial='') +text_box.on_submit(submit) + +ax.legend(loc='lower right', ncol=2) + +if ser.is_open==True: + plt.show() + diff --git a/tasmota/i18n.h b/tasmota/i18n.h index e5f3e395a..b538e705e 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -319,6 +319,11 @@ #define D_CMND_HUMOFFSET "HumOffset" #define D_CMND_GLOBAL_TEMP "GlobalTemp" #define D_CMND_GLOBAL_HUM "GlobalHum" +#ifdef ESP32 +#define D_CMND_TOUCH_CAL "TouchCal" +#define D_CMND_TOUCH_THRES "TouchThres" +#define D_CMND_TOUCH_NUM "TouchNum" +#endif //ESP32 // Commands xdrv_01_mqtt.ino #define D_CMND_MQTTLOG "MqttLog" diff --git a/tasmota/support_button.ino b/tasmota/support_button.ino index 96dfaf73a..a92a56be5 100644 --- a/tasmota/support_button.ino +++ b/tasmota/support_button.ino @@ -52,6 +52,14 @@ struct BUTTON { uint8_t adc = 99; // ADC0 button number } Button; +#ifdef ESP32 +struct TOUCH_BUTTON { + uint8_t pin_threshold = TOUCH_PIN_THRESHOLD; + uint8_t hit_threshold = TOUCH_HIT_THRESHOLD; + uint8_t calibration = 0; // Bitfield +} TOUCH_BUTTON; +#endif // ESP32 + /********************************************************************************************/ void ButtonPullupFlag(uint8 button_bit) @@ -155,15 +163,15 @@ void ButtonHandler(void) uint32_t _value = touchRead(Pin(GPIO_KEY1, button_index)); button = NOT_PRESSED; if (_value != 0){ // probably read-error - if(_value < TOUCH_PIN_THRESHOLD){ - if(++Button.touch_hits[button_index]>TOUCH_HIT_THRESHOLD){ - button = PRESSED; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Touch value: %u hits: %u"), _value, Button.touch_hits[button_index]); + if(_value < TOUCH_BUTTON.pin_threshold){ + if(++Button.touch_hits[button_index]>TOUCH_BUTTON.hit_threshold){ + if (!bitRead(TOUCH_BUTTON.calibration, button_index+1)) button = PRESSED; } } else Button.touch_hits[button_index] = 0; } else Button.touch_hits[button_index] = 0; + if (bitRead(TOUCH_BUTTON.calibration, button_index+1)) AddLog_P2(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"),button_index+1, _value, Button.touch_hits[button_index]); // button number (1..4) , value, continuous hits under threshold } else{ // Normal button button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index)); diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 9d9fbfa19..6c44391a9 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -38,7 +38,11 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix #endif // USE_DEVICE_GROUPS_SEND D_CMND_DEVGROUP_SHARE "|" D_CMND_DEVGROUPSTATUS "|" #endif // USE_DEVICE_GROUPS - D_CMND_SENSOR "|" D_CMND_DRIVER; + D_CMND_SENSOR "|" D_CMND_DRIVER +#ifdef ESP32 + "|" D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|" D_CMND_TOUCH_NUM +#endif //ESP32 + ; void (* const TasmotaCommand[])(void) PROGMEM = { &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, @@ -61,7 +65,11 @@ void (* const TasmotaCommand[])(void) PROGMEM = { #endif // USE_DEVICE_GROUPS_SEND &CmndDevGroupShare, &CmndDevGroupStatus, #endif // USE_DEVICE_GROUPS - &CmndSensor, &CmndDriver }; + &CmndSensor, &CmndDriver +#ifdef ESP32 + ,&CmndTouchCal, &CmndTouchThres, &CmndTouchNum +#endif //ESP32 + }; const char kWifiConfig[] PROGMEM = D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY; @@ -1946,3 +1954,41 @@ void CmndDriver(void) { XdrvCall(FUNC_COMMAND_DRIVER); } + +#ifdef ESP32 +void CmndTouchCal(void) +{ + if (XdrvMailbox.payload >= 0) { + if (XdrvMailbox.payload < MAX_KEYS + 1) TOUCH_BUTTON.calibration = bitSet(TOUCH_BUTTON.calibration, XdrvMailbox.payload); + if (XdrvMailbox.payload == 0) TOUCH_BUTTON.calibration = 0; + if (XdrvMailbox.payload == 255) TOUCH_BUTTON.calibration = 255; // all pinss + } + Response_P(PSTR("{\"" D_CMND_TOUCH_CAL "\": %u"), TOUCH_BUTTON.calibration); + ResponseJsonEnd(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Button Touchvalue Hits,")); +} + +void CmndTouchThres(void) +{ + if (XdrvMailbox.payload >= 0) { + if (XdrvMailbox.payload<256){ + TOUCH_BUTTON.pin_threshold = XdrvMailbox.payload; + } + } + Response_P(PSTR("{\"" D_CMND_TOUCH_THRES "\": %u"), TOUCH_BUTTON.pin_threshold); + ResponseJsonEnd(); +} + +void CmndTouchNum(void) +{ + if (XdrvMailbox.payload >= 0) { + if (XdrvMailbox.payload<32){ + TOUCH_BUTTON.hit_threshold = XdrvMailbox.payload; + } + } + Response_P(PSTR("{\"" D_CMND_TOUCH_NUM "\": %u"), TOUCH_BUTTON.hit_threshold); + ResponseJsonEnd(); + +} + +#endif //ESP32 \ No newline at end of file