Compare commits
3
Commits
2c61e3576f
...
f47299b939
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f47299b939 | ||
|
|
51faa0fe59 | ||
|
|
a19accf6a7 |
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "app_ui.c"
|
||||
INCLUDE_DIRS "include" "."
|
||||
REQUIRES ui_cmd app_machine app_process
|
||||
PRIV_REQUIRES hal_display)
|
||||
@@ -0,0 +1,197 @@
|
||||
#include "app_ui.h"
|
||||
#include "screens.h"
|
||||
#include "app_machine.h"
|
||||
#include "app_process.h"
|
||||
#include "hal_display.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static ui_evt_t s_last;
|
||||
static bool s_have;
|
||||
static bool s_dirty;
|
||||
|
||||
static void fmt_mmss(char *buf, size_t n, uint32_t total_s)
|
||||
{
|
||||
unsigned m = (unsigned)(total_s / 60u);
|
||||
unsigned s = (unsigned)(total_s % 60u);
|
||||
if (m > 99u) {
|
||||
m = 99u;
|
||||
}
|
||||
snprintf(buf, n, "%02u:%02u", m, s);
|
||||
}
|
||||
|
||||
static void draw_temp_row3(const ui_evt_t *ev)
|
||||
{
|
||||
/* row3 col13: thermo + tt.tC or -- */
|
||||
char tbuf[8];
|
||||
if (ev != NULL && ev->temp_ok) {
|
||||
snprintf(tbuf, sizeof(tbuf), "%4.1fC", (double)ev->temp_c);
|
||||
hal_display_glyph_thermo(13, 3);
|
||||
hal_display_text(14, 3, tbuf);
|
||||
} else {
|
||||
hal_display_text(13, 3, " --");
|
||||
}
|
||||
}
|
||||
|
||||
static const char *step_name(uint8_t proc, uint8_t step)
|
||||
{
|
||||
const process_def_t *p = app_process_get(proc);
|
||||
if (p == NULL || step >= p->step_count) {
|
||||
return "";
|
||||
}
|
||||
return p->steps[step].name;
|
||||
}
|
||||
|
||||
static void draw_program_select(void)
|
||||
{
|
||||
hal_display_clear();
|
||||
hal_display_text(0, 0, "Select Programme:");
|
||||
hal_display_text(0, 1, "1. C41 4. ECN-2");
|
||||
hal_display_text(0, 2, "2. E6 5. Custom");
|
||||
hal_display_text(0, 3, "3. B&W 6. B&W Rev");
|
||||
}
|
||||
|
||||
static void draw_step_select(const ui_evt_t *ev)
|
||||
{
|
||||
char line[21];
|
||||
char mm[8];
|
||||
uint16_t ts = ev->time_s;
|
||||
fmt_mmss(mm, sizeof(mm), ts);
|
||||
int pref = (int)(ev->temp_pref_c + (ev->temp_pref_c >= 0 ? 0.5f : -0.5f));
|
||||
hal_display_clear();
|
||||
hal_display_text(0, 0, "Step Time Temp");
|
||||
snprintf(line, sizeof(line), "%-9.9s %s %2dC", step_name(ev->process_id, ev->step_index), mm, pref);
|
||||
hal_display_text(0, 1, line);
|
||||
hal_display_text(0, 2, "Scroll / Esc / Ent");
|
||||
draw_temp_row3(ev);
|
||||
}
|
||||
|
||||
static void draw_armed(const ui_evt_t *ev)
|
||||
{
|
||||
char line[21];
|
||||
char mm[8];
|
||||
fmt_mmss(mm, sizeof(mm), ev->time_s);
|
||||
int pref = (int)(ev->temp_pref_c + (ev->temp_pref_c >= 0 ? 0.5f : -0.5f));
|
||||
hal_display_clear();
|
||||
snprintf(line, sizeof(line), "%-10.10s %s %2dC", step_name(ev->process_id, ev->step_index), mm, pref);
|
||||
hal_display_text(0, 0, line);
|
||||
hal_display_text(0, 1, "Ent:start Esc:quit");
|
||||
draw_temp_row3(ev);
|
||||
}
|
||||
|
||||
static void draw_running(const ui_evt_t *ev)
|
||||
{
|
||||
char line[21];
|
||||
char mm[8];
|
||||
uint32_t rem_s = (ev->remaining_ms + 999u) / 1000u;
|
||||
fmt_mmss(mm, sizeof(mm), rem_s);
|
||||
hal_display_clear();
|
||||
snprintf(line, sizeof(line), "%-20.20s", step_name(ev->process_id, ev->step_index));
|
||||
hal_display_text(0, 0, line);
|
||||
snprintf(line, sizeof(line), "Remaining: %s", mm);
|
||||
hal_display_text(0, 1, line);
|
||||
draw_temp_row3(ev);
|
||||
}
|
||||
|
||||
static void draw_stopped(const ui_evt_t *ev)
|
||||
{
|
||||
char line[21];
|
||||
char mm[8];
|
||||
uint32_t rem_s = (ev->remaining_ms + 999u) / 1000u;
|
||||
fmt_mmss(mm, sizeof(mm), rem_s);
|
||||
hal_display_clear();
|
||||
snprintf(line, sizeof(line), "%-10.10s %s", step_name(ev->process_id, ev->step_index), mm);
|
||||
hal_display_text(0, 0, line);
|
||||
hal_display_text(0, 1, "E:resume X:back");
|
||||
draw_temp_row3(ev);
|
||||
}
|
||||
|
||||
static void draw_complete(const ui_evt_t *ev)
|
||||
{
|
||||
char line[21];
|
||||
hal_display_clear();
|
||||
snprintf(line, sizeof(line), "%-10.10s Done", step_name(ev->process_id, ev->step_index));
|
||||
hal_display_text(0, 0, line);
|
||||
hal_display_text(0, 1, "E:next X:back");
|
||||
draw_temp_row3(ev);
|
||||
}
|
||||
|
||||
void app_ui_init(void)
|
||||
{
|
||||
memset(&s_last, 0, sizeof(s_last));
|
||||
s_have = false;
|
||||
s_dirty = true;
|
||||
}
|
||||
|
||||
void app_ui_on_event(const ui_evt_t *ev)
|
||||
{
|
||||
if (ev == NULL) {
|
||||
return;
|
||||
}
|
||||
if (ev->id == EVT_TEMP_UPDATED && s_have) {
|
||||
s_last.temp_c = ev->temp_c;
|
||||
s_last.temp_ok = ev->temp_ok;
|
||||
s_dirty = true;
|
||||
return;
|
||||
}
|
||||
if (ev->id == EVT_STEP_PROGRESS && s_have) {
|
||||
s_last.remaining_ms = ev->remaining_ms;
|
||||
s_last.id = ev->id;
|
||||
s_dirty = true;
|
||||
return;
|
||||
}
|
||||
s_last = *ev;
|
||||
s_have = true;
|
||||
s_dirty = true;
|
||||
}
|
||||
|
||||
bool app_ui_is_dirty(void)
|
||||
{
|
||||
return s_dirty;
|
||||
}
|
||||
|
||||
void app_ui_draw(void)
|
||||
{
|
||||
machine_state_t st = app_machine_state();
|
||||
ui_evt_t ev = s_last;
|
||||
if (!s_have) {
|
||||
ev.temp_ok = false;
|
||||
}
|
||||
ev.remaining_ms = app_machine_remaining_ms();
|
||||
ev.process_id = app_machine_process_id();
|
||||
ev.step_index = app_machine_step_index();
|
||||
if (app_process_get(ev.process_id) != NULL) {
|
||||
ev.time_s = app_process_time_s(ev.process_id, ev.step_index);
|
||||
const process_def_t *p = app_process_get(ev.process_id);
|
||||
if (p != NULL && ev.step_index < p->step_count) {
|
||||
ev.temp_pref_c = p->steps[ev.step_index].temp_pref_c;
|
||||
}
|
||||
}
|
||||
|
||||
switch (st) {
|
||||
case ST_IDLE:
|
||||
draw_program_select();
|
||||
break;
|
||||
case ST_STEP_SELECT:
|
||||
draw_step_select(&ev);
|
||||
break;
|
||||
case ST_ARMED:
|
||||
draw_armed(&ev);
|
||||
break;
|
||||
case ST_RUNNING:
|
||||
draw_running(&ev);
|
||||
break;
|
||||
case ST_STOPPED:
|
||||
draw_stopped(&ev);
|
||||
break;
|
||||
case ST_COMPLETE:
|
||||
draw_complete(&ev);
|
||||
break;
|
||||
default:
|
||||
draw_program_select();
|
||||
break;
|
||||
}
|
||||
hal_display_flush();
|
||||
s_dirty = false;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_cmd.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void app_ui_init(void);
|
||||
void app_ui_on_event(const ui_evt_t *ev);
|
||||
void app_ui_draw(void);
|
||||
bool app_ui_is_dirty(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
/* Screen ids used by app_ui. Layouts are 20x4 WROOM strings. */
|
||||
typedef enum {
|
||||
UI_SCREEN_PROGRAM_SELECT = 0,
|
||||
UI_SCREEN_STEP_SELECT,
|
||||
UI_SCREEN_ARMED,
|
||||
UI_SCREEN_RUNNING,
|
||||
UI_SCREEN_STOPPED,
|
||||
UI_SCREEN_COMPLETE
|
||||
} app_ui_screen_t;
|
||||
@@ -0,0 +1,22 @@
|
||||
set(srcs)
|
||||
set(priv_inc)
|
||||
set(priv_req driver)
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
list(APPEND srcs
|
||||
hal_display_lcd2004.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../third_party/LiquidCrystal_I2C/LiquidCrystal_I2C.cpp
|
||||
)
|
||||
list(APPEND priv_inc
|
||||
${CMAKE_CURRENT_LIST_DIR}/../third_party/LiquidCrystal_I2C/include
|
||||
)
|
||||
list(APPEND priv_req board_wroom espressif__arduino-esp32)
|
||||
else()
|
||||
list(APPEND srcs stub/hal_display.c)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS ${priv_inc}
|
||||
REQUIRES driver
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "hal_display.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#define LCD_ADDR 0x27
|
||||
#define LCD_COLS 20
|
||||
#define LCD_ROWS 4
|
||||
|
||||
static const char *TAG = "display";
|
||||
|
||||
static LiquidCrystal_I2C s_lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);
|
||||
static bool s_inited;
|
||||
|
||||
static const uint8_t s_thermo[8] = {
|
||||
0b00100,
|
||||
0b01100,
|
||||
0b00100,
|
||||
0b01100,
|
||||
0b00100,
|
||||
0b01110,
|
||||
0b01110,
|
||||
0b01110,
|
||||
};
|
||||
|
||||
extern "C" void hal_display_init(void)
|
||||
{
|
||||
if (s_inited) {
|
||||
return;
|
||||
}
|
||||
Wire.begin(21, 22);
|
||||
s_lcd.init();
|
||||
s_lcd.backlight();
|
||||
s_lcd.createChar(0, const_cast<uint8_t *>(s_thermo));
|
||||
s_lcd.clear();
|
||||
s_inited = true;
|
||||
ESP_LOGI(TAG, "init LCD 20x4 addr=0x27 sda=21 scl=22");
|
||||
}
|
||||
|
||||
extern "C" void hal_display_clear(void)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return;
|
||||
}
|
||||
s_lcd.clear();
|
||||
}
|
||||
|
||||
extern "C" void hal_display_text(int col, int row, const char *s)
|
||||
{
|
||||
if (!s_inited || s == NULL) {
|
||||
return;
|
||||
}
|
||||
if (row < 0 || row >= LCD_ROWS) {
|
||||
return;
|
||||
}
|
||||
if (col < 0) {
|
||||
col = 0;
|
||||
}
|
||||
if (col >= LCD_COLS) {
|
||||
return;
|
||||
}
|
||||
s_lcd.setCursor((uint8_t)col, (uint8_t)row);
|
||||
int room = LCD_COLS - col;
|
||||
char buf[21];
|
||||
int n = 0;
|
||||
while (s[n] != '\0' && n < room) {
|
||||
buf[n] = s[n];
|
||||
n++;
|
||||
}
|
||||
buf[n] = '\0';
|
||||
s_lcd.print(buf);
|
||||
}
|
||||
|
||||
extern "C" void hal_display_glyph_thermo(int col, int row)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return;
|
||||
}
|
||||
if (row < 0 || row >= LCD_ROWS || col < 0 || col >= LCD_COLS) {
|
||||
return;
|
||||
}
|
||||
s_lcd.setCursor((uint8_t)col, (uint8_t)row);
|
||||
s_lcd.write((uint8_t)0);
|
||||
}
|
||||
|
||||
extern "C" void hal_display_flush(void)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void hal_display_init(void);
|
||||
void hal_display_clear(void);
|
||||
void hal_display_text(int col, int row, const char *s); /* clip to 20 cols */
|
||||
void hal_display_glyph_thermo(int col, int row);
|
||||
void hal_display_flush(void); /* no-op on 2004 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "hal_display.h"
|
||||
|
||||
void hal_display_init(void) {}
|
||||
void hal_display_clear(void) {}
|
||||
void hal_display_text(int col, int row, const char *s)
|
||||
{
|
||||
(void)col;
|
||||
(void)row;
|
||||
(void)s;
|
||||
}
|
||||
void hal_display_glyph_thermo(int col, int row)
|
||||
{
|
||||
(void)col;
|
||||
(void)row;
|
||||
}
|
||||
void hal_display_flush(void) {}
|
||||
@@ -0,0 +1,23 @@
|
||||
set(srcs)
|
||||
set(priv_inc)
|
||||
set(priv_req driver)
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
list(APPEND srcs
|
||||
hal_input_keypad.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../third_party/Keypad/Keypad.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../third_party/Keypad/Key.cpp
|
||||
)
|
||||
list(APPEND priv_inc
|
||||
${CMAKE_CURRENT_LIST_DIR}/../third_party/Keypad/include
|
||||
)
|
||||
list(APPEND priv_req board_wroom espressif__arduino-esp32)
|
||||
else()
|
||||
list(APPEND srcs stub/hal_input.c)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS ${priv_inc}
|
||||
REQUIRES driver
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "hal_input.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Keypad.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "input";
|
||||
|
||||
static const byte ROWS = 5;
|
||||
static const byte COLS = 4;
|
||||
static byte s_rowPins[ROWS] = {19, 18, 5, 17, 16};
|
||||
static byte s_colPins[COLS] = {15, 2, 0, 4};
|
||||
|
||||
static char s_keys[ROWS][COLS] = {
|
||||
{'F', 'E', '#', '*'},
|
||||
{'1', '2', '3', 'U'},
|
||||
{'4', '5', '6', 'D'},
|
||||
{'7', '8', '9', 'X'},
|
||||
{'L', '0', 'R', 'E'}};
|
||||
|
||||
static Keypad s_keypad = Keypad(makeKeymap(s_keys), s_rowPins, s_colPins, ROWS, COLS);
|
||||
static bool s_inited;
|
||||
|
||||
extern "C" void hal_input_init(void)
|
||||
{
|
||||
if (s_inited) {
|
||||
return;
|
||||
}
|
||||
s_inited = true;
|
||||
ESP_LOGI(TAG, "init 5x4 keypad map byte-identical to src/config.cpp");
|
||||
}
|
||||
|
||||
extern "C" bool hal_input_pop_key(ui_raw_key_t *out)
|
||||
{
|
||||
char k = s_keypad.getKey();
|
||||
if (k == NO_KEY) {
|
||||
return false;
|
||||
}
|
||||
if (out != NULL) {
|
||||
out->key = k;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char key;
|
||||
} ui_raw_key_t;
|
||||
|
||||
void hal_input_init(void);
|
||||
bool hal_input_pop_key(ui_raw_key_t *out); /* nonblocking */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "hal_input.h"
|
||||
|
||||
void hal_input_init(void) {}
|
||||
|
||||
bool hal_input_pop_key(ui_raw_key_t *out)
|
||||
{
|
||||
(void)out;
|
||||
return false;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
|| @file Key.cpp
|
||||
|| @version 1.0
|
||||
|| @author Mark Stanley
|
||||
|| @contact mstanley@technologist.com
|
||||
||
|
||||
|| @description
|
||||
|| | Key class provides an abstract definition of a key or button
|
||||
|| | and was initially designed to be used in conjunction with a
|
||||
|| | state-machine.
|
||||
|| #
|
||||
||
|
||||
|| @license
|
||||
|| | This library is free software; you can redistribute it and/or
|
||||
|| | modify it under the terms of the GNU Lesser General Public
|
||||
|| | License as published by the Free Software Foundation; version
|
||||
|| | 2.1 of the License.
|
||||
|| |
|
||||
|| | This library 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
|
||||
|| | Lesser General Public License for more details.
|
||||
|| |
|
||||
|| | You should have received a copy of the GNU Lesser General Public
|
||||
|| | License along with this library; if not, write to the Free Software
|
||||
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|| #
|
||||
||
|
||||
*/
|
||||
#include <Key.h>
|
||||
|
||||
|
||||
// default constructor
|
||||
Key::Key() {
|
||||
kchar = NO_KEY;
|
||||
kstate = IDLE;
|
||||
stateChanged = false;
|
||||
}
|
||||
|
||||
// constructor
|
||||
Key::Key(char userKeyChar) {
|
||||
kchar = userKeyChar;
|
||||
kcode = -1;
|
||||
kstate = IDLE;
|
||||
stateChanged = false;
|
||||
}
|
||||
|
||||
|
||||
void Key::key_update (char userKeyChar, KeyState userState, boolean userStatus) {
|
||||
kchar = userKeyChar;
|
||||
kstate = userState;
|
||||
stateChanged = userStatus;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|| @changelog
|
||||
|| | 1.0 2012-06-04 - Mark Stanley : Initial Release
|
||||
|| #
|
||||
*/
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
||
|
||||
|| @file Keypad.cpp
|
||||
|| @version 3.1
|
||||
|| @author Mark Stanley, Alexander Brevig
|
||||
|| @contact mstanley@technologist.com, alexanderbrevig@gmail.com
|
||||
||
|
||||
|| @description
|
||||
|| | This library provides a simple interface for using matrix
|
||||
|| | keypads. It supports multiple keypresses while maintaining
|
||||
|| | backwards compatibility with the old single key library.
|
||||
|| | It also supports user selectable pins and definable keymaps.
|
||||
|| #
|
||||
||
|
||||
|| @license
|
||||
|| | This library is free software; you can redistribute it and/or
|
||||
|| | modify it under the terms of the GNU Lesser General Public
|
||||
|| | License as published by the Free Software Foundation; version
|
||||
|| | 2.1 of the License.
|
||||
|| |
|
||||
|| | This library 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
|
||||
|| | Lesser General Public License for more details.
|
||||
|| |
|
||||
|| | You should have received a copy of the GNU Lesser General Public
|
||||
|| | License along with this library; if not, write to the Free Software
|
||||
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|| #
|
||||
||
|
||||
*/
|
||||
#include <Keypad.h>
|
||||
|
||||
// <<constructor>> Allows custom keymap, pin configuration, and keypad sizes.
|
||||
Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols) {
|
||||
rowPins = row;
|
||||
columnPins = col;
|
||||
sizeKpd.rows = numRows;
|
||||
sizeKpd.columns = numCols;
|
||||
|
||||
begin(userKeymap);
|
||||
|
||||
setDebounceTime(10);
|
||||
setHoldTime(500);
|
||||
keypadEventListener = 0;
|
||||
|
||||
startTime = 0;
|
||||
single_key = false;
|
||||
}
|
||||
|
||||
// Let the user define a keymap - assume the same row/column count as defined in constructor
|
||||
void Keypad::begin(char *userKeymap) {
|
||||
keymap = userKeymap;
|
||||
}
|
||||
|
||||
// Returns a single key only. Retained for backwards compatibility.
|
||||
char Keypad::getKey() {
|
||||
single_key = true;
|
||||
|
||||
if (getKeys() && key[0].stateChanged && (key[0].kstate==PRESSED))
|
||||
return key[0].kchar;
|
||||
|
||||
single_key = false;
|
||||
|
||||
return NO_KEY;
|
||||
}
|
||||
|
||||
// Populate the key list.
|
||||
bool Keypad::getKeys() {
|
||||
bool keyActivity = false;
|
||||
|
||||
// Limit how often the keypad is scanned. This makes the loop() run 10 times as fast.
|
||||
if ( (millis()-startTime)>debounceTime ) {
|
||||
scanKeys();
|
||||
keyActivity = updateList();
|
||||
startTime = millis();
|
||||
}
|
||||
|
||||
return keyActivity;
|
||||
}
|
||||
|
||||
// Private : Hardware scan
|
||||
void Keypad::scanKeys() {
|
||||
// Re-intialize the row pins. Allows sharing these pins with other hardware.
|
||||
for (byte r=0; r<sizeKpd.rows; r++) {
|
||||
pin_mode(rowPins[r],INPUT_PULLUP);
|
||||
}
|
||||
|
||||
// bitMap stores ALL the keys that are being pressed.
|
||||
for (byte c=0; c<sizeKpd.columns; c++) {
|
||||
pin_mode(columnPins[c],OUTPUT);
|
||||
pin_write(columnPins[c], LOW); // Begin column pulse output.
|
||||
for (byte r=0; r<sizeKpd.rows; r++) {
|
||||
bitWrite(bitMap[r], c, !pin_read(rowPins[r])); // keypress is active low so invert to high.
|
||||
}
|
||||
// Set pin to high impedance input. Effectively ends column pulse.
|
||||
pin_write(columnPins[c],HIGH);
|
||||
pin_mode(columnPins[c],INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
// Manage the list without rearranging the keys. Returns true if any keys on the list changed state.
|
||||
bool Keypad::updateList() {
|
||||
|
||||
bool anyActivity = false;
|
||||
|
||||
// Delete any IDLE keys
|
||||
for (byte i=0; i<LIST_MAX; i++) {
|
||||
if (key[i].kstate==IDLE) {
|
||||
key[i].kchar = NO_KEY;
|
||||
key[i].kcode = -1;
|
||||
key[i].stateChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new keys to empty slots in the key list.
|
||||
for (byte r=0; r<sizeKpd.rows; r++) {
|
||||
for (byte c=0; c<sizeKpd.columns; c++) {
|
||||
boolean button = bitRead(bitMap[r],c);
|
||||
char keyChar = keymap[r * sizeKpd.columns + c];
|
||||
int keyCode = r * sizeKpd.columns + c;
|
||||
int idx = findInList (keyCode);
|
||||
// Key is already on the list so set its next state.
|
||||
if (idx > -1) {
|
||||
nextKeyState(idx, button);
|
||||
}
|
||||
// Key is NOT on the list so add it.
|
||||
if ((idx == -1) && button) {
|
||||
for (byte i=0; i<LIST_MAX; i++) {
|
||||
if (key[i].kchar==NO_KEY) { // Find an empty slot or don't add key to list.
|
||||
key[i].kchar = keyChar;
|
||||
key[i].kcode = keyCode;
|
||||
key[i].kstate = IDLE; // Keys NOT on the list have an initial state of IDLE.
|
||||
nextKeyState (i, button);
|
||||
break; // Don't fill all the empty slots with the same key.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Report if the user changed the state of any key.
|
||||
for (byte i=0; i<LIST_MAX; i++) {
|
||||
if (key[i].stateChanged) anyActivity = true;
|
||||
}
|
||||
|
||||
return anyActivity;
|
||||
}
|
||||
|
||||
// Private
|
||||
// This function is a state machine but is also used for debouncing the keys.
|
||||
void Keypad::nextKeyState(byte idx, boolean button) {
|
||||
key[idx].stateChanged = false;
|
||||
|
||||
switch (key[idx].kstate) {
|
||||
case IDLE:
|
||||
if (button==CLOSED) {
|
||||
transitionTo (idx, PRESSED);
|
||||
holdTimer = millis(); } // Get ready for next HOLD state.
|
||||
break;
|
||||
case PRESSED:
|
||||
if ((millis()-holdTimer)>holdTime) // Waiting for a key HOLD...
|
||||
transitionTo (idx, HOLD);
|
||||
else if (button==OPEN) // or for a key to be RELEASED.
|
||||
transitionTo (idx, RELEASED);
|
||||
break;
|
||||
case HOLD:
|
||||
if (button==OPEN)
|
||||
transitionTo (idx, RELEASED);
|
||||
break;
|
||||
case RELEASED:
|
||||
transitionTo (idx, IDLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// New in 2.1
|
||||
bool Keypad::isPressed(char keyChar) {
|
||||
for (byte i=0; i<LIST_MAX; i++) {
|
||||
if ( key[i].kchar == keyChar ) {
|
||||
if ( (key[i].kstate == PRESSED) && key[i].stateChanged )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; // Not pressed.
|
||||
}
|
||||
|
||||
// Search by character for a key in the list of active keys.
|
||||
// Returns -1 if not found or the index into the list of active keys.
|
||||
int Keypad::findInList (char keyChar) {
|
||||
for (byte i=0; i<LIST_MAX; i++) {
|
||||
if (key[i].kchar == keyChar) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Search by code for a key in the list of active keys.
|
||||
// Returns -1 if not found or the index into the list of active keys.
|
||||
int Keypad::findInList (int keyCode) {
|
||||
for (byte i=0; i<LIST_MAX; i++) {
|
||||
if (key[i].kcode == keyCode) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// New in 2.0
|
||||
char Keypad::waitForKey() {
|
||||
char waitKey = NO_KEY;
|
||||
while( (waitKey = getKey()) == NO_KEY ); // Block everything while waiting for a keypress.
|
||||
return waitKey;
|
||||
}
|
||||
|
||||
// Backwards compatibility function.
|
||||
KeyState Keypad::getState() {
|
||||
return key[0].kstate;
|
||||
}
|
||||
|
||||
// The end user can test for any changes in state before deciding
|
||||
// if any variables, etc. needs to be updated in their code.
|
||||
bool Keypad::keyStateChanged() {
|
||||
return key[0].stateChanged;
|
||||
}
|
||||
|
||||
// The number of keys on the key list, key[LIST_MAX], equals the number
|
||||
// of bytes in the key list divided by the number of bytes in a Key object.
|
||||
byte Keypad::numKeys() {
|
||||
return sizeof(key)/sizeof(Key);
|
||||
}
|
||||
|
||||
// Minimum debounceTime is 1 mS. Any lower *will* slow down the loop().
|
||||
void Keypad::setDebounceTime(uint debounce) {
|
||||
debounce<1 ? debounceTime=1 : debounceTime=debounce;
|
||||
}
|
||||
|
||||
void Keypad::setHoldTime(uint hold) {
|
||||
holdTime = hold;
|
||||
}
|
||||
|
||||
void Keypad::addEventListener(void (*listener)(char)){
|
||||
keypadEventListener = listener;
|
||||
}
|
||||
|
||||
void Keypad::transitionTo(byte idx, KeyState nextState) {
|
||||
key[idx].kstate = nextState;
|
||||
key[idx].stateChanged = true;
|
||||
|
||||
// Sketch used the getKey() function.
|
||||
// Calls keypadEventListener only when the first key in slot 0 changes state.
|
||||
if (single_key) {
|
||||
if ( (keypadEventListener!=NULL) && (idx==0) ) {
|
||||
keypadEventListener(key[0].kchar);
|
||||
}
|
||||
}
|
||||
// Sketch used the getKeys() function.
|
||||
// Calls keypadEventListener on any key that changes state.
|
||||
else {
|
||||
if (keypadEventListener!=NULL) {
|
||||
keypadEventListener(key[idx].kchar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|| @changelog
|
||||
|| | 3.1 2013-01-15 - Mark Stanley : Fixed missing RELEASED & IDLE status when using a single key.
|
||||
|| | 3.0 2012-07-12 - Mark Stanley : Made library multi-keypress by default. (Backwards compatible)
|
||||
|| | 3.0 2012-07-12 - Mark Stanley : Modified pin functions to support Keypad_I2C
|
||||
|| | 3.0 2012-07-12 - Stanley & Young : Removed static variables. Fix for multiple keypad objects.
|
||||
|| | 3.0 2012-07-12 - Mark Stanley : Fixed bug that caused shorted pins when pressing multiple keys.
|
||||
|| | 2.0 2011-12-29 - Mark Stanley : Added waitForKey().
|
||||
|| | 2.0 2011-12-23 - Mark Stanley : Added the public function keyStateChanged().
|
||||
|| | 2.0 2011-12-23 - Mark Stanley : Added the private function scanKeys().
|
||||
|| | 2.0 2011-12-23 - Mark Stanley : Moved the Finite State Machine into the function getKeyState().
|
||||
|| | 2.0 2011-12-23 - Mark Stanley : Removed the member variable lastUdate. Not needed after rewrite.
|
||||
|| | 1.8 2011-11-21 - Mark Stanley : Added decision logic to compile WProgram.h or Arduino.h
|
||||
|| | 1.8 2009-07-08 - Alexander Brevig : No longer uses arrays
|
||||
|| | 1.7 2009-06-18 - Alexander Brevig : Every time a state changes the keypadEventListener will trigger, if set.
|
||||
|| | 1.7 2009-06-18 - Alexander Brevig : Added setDebounceTime. setHoldTime specifies the amount of
|
||||
|| | microseconds before a HOLD state triggers
|
||||
|| | 1.7 2009-06-18 - Alexander Brevig : Added transitionTo
|
||||
|| | 1.6 2009-06-15 - Alexander Brevig : Added getState() and state variable
|
||||
|| | 1.5 2009-05-19 - Alexander Brevig : Added setHoldTime()
|
||||
|| | 1.4 2009-05-15 - Alexander Brevig : Added addEventListener
|
||||
|| | 1.3 2009-05-12 - Alexander Brevig : Added lastUdate, in order to do simple debouncing
|
||||
|| | 1.2 2009-05-09 - Alexander Brevig : Changed getKey()
|
||||
|| | 1.1 2009-04-28 - Alexander Brevig : Modified API, and made variables private
|
||||
|| | 1.0 2007-XX-XX - Mark Stanley : Initial Release
|
||||
|| #
|
||||
*/
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
||
|
||||
|| @file Key.h
|
||||
|| @version 1.0
|
||||
|| @author Mark Stanley
|
||||
|| @contact mstanley@technologist.com
|
||||
||
|
||||
|| @description
|
||||
|| | Key class provides an abstract definition of a key or button
|
||||
|| | and was initially designed to be used in conjunction with a
|
||||
|| | state-machine.
|
||||
|| #
|
||||
||
|
||||
|| @license
|
||||
|| | This library is free software; you can redistribute it and/or
|
||||
|| | modify it under the terms of the GNU Lesser General Public
|
||||
|| | License as published by the Free Software Foundation; version
|
||||
|| | 2.1 of the License.
|
||||
|| |
|
||||
|| | This library 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
|
||||
|| | Lesser General Public License for more details.
|
||||
|| |
|
||||
|| | You should have received a copy of the GNU Lesser General Public
|
||||
|| | License along with this library; if not, write to the Free Software
|
||||
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|| #
|
||||
||
|
||||
*/
|
||||
|
||||
#ifndef Keypadlib_KEY_H_
|
||||
#define Keypadlib_KEY_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define OPEN LOW
|
||||
#define CLOSED HIGH
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef enum{ IDLE, PRESSED, HOLD, RELEASED } KeyState;
|
||||
|
||||
const char NO_KEY = '\0';
|
||||
|
||||
class Key {
|
||||
public:
|
||||
// members
|
||||
char kchar;
|
||||
int kcode;
|
||||
KeyState kstate;
|
||||
boolean stateChanged;
|
||||
|
||||
// methods
|
||||
Key();
|
||||
Key(char userKeyChar);
|
||||
void key_update(char userKeyChar, KeyState userState, boolean userStatus);
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|| @changelog
|
||||
|| | 1.0 2012-06-04 - Mark Stanley : Initial Release
|
||||
|| #
|
||||
*/
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
||
|
||||
|| @file Keypad.h
|
||||
|| @version 3.1
|
||||
|| @author Mark Stanley, Alexander Brevig
|
||||
|| @contact mstanley@technologist.com, alexanderbrevig@gmail.com
|
||||
||
|
||||
|| @description
|
||||
|| | This library provides a simple interface for using matrix
|
||||
|| | keypads. It supports multiple keypresses while maintaining
|
||||
|| | backwards compatibility with the old single key library.
|
||||
|| | It also supports user selectable pins and definable keymaps.
|
||||
|| #
|
||||
||
|
||||
|| @license
|
||||
|| | This library is free software; you can redistribute it and/or
|
||||
|| | modify it under the terms of the GNU Lesser General Public
|
||||
|| | License as published by the Free Software Foundation; version
|
||||
|| | 2.1 of the License.
|
||||
|| |
|
||||
|| | This library 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
|
||||
|| | Lesser General Public License for more details.
|
||||
|| |
|
||||
|| | You should have received a copy of the GNU Lesser General Public
|
||||
|| | License along with this library; if not, write to the Free Software
|
||||
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|| #
|
||||
||
|
||||
*/
|
||||
|
||||
#ifndef KEYPAD_H
|
||||
#define KEYPAD_H
|
||||
|
||||
#include "Key.h"
|
||||
|
||||
// bperrybap - Thanks for a well reasoned argument and the following macro(s).
|
||||
// See http://arduino.cc/forum/index.php/topic,142041.msg1069480.html#msg1069480
|
||||
#ifndef INPUT_PULLUP
|
||||
#warning "Using pinMode() INPUT_PULLUP AVR emulation"
|
||||
#define INPUT_PULLUP 0x2
|
||||
#define pinMode(_pin, _mode) _mypinMode(_pin, _mode)
|
||||
#define _mypinMode(_pin, _mode) \
|
||||
do { \
|
||||
if(_mode == INPUT_PULLUP) \
|
||||
pinMode(_pin, INPUT); \
|
||||
digitalWrite(_pin, 1); \
|
||||
if(_mode != INPUT_PULLUP) \
|
||||
pinMode(_pin, _mode); \
|
||||
}while(0)
|
||||
#endif
|
||||
|
||||
|
||||
#define OPEN LOW
|
||||
#define CLOSED HIGH
|
||||
|
||||
typedef char KeypadEvent;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
// Made changes according to this post http://arduino.cc/forum/index.php?topic=58337.0
|
||||
// by Nick Gammon. Thanks for the input Nick. It actually saved 78 bytes for me. :)
|
||||
typedef struct {
|
||||
byte rows;
|
||||
byte columns;
|
||||
} KeypadSize;
|
||||
|
||||
#define LIST_MAX 10 // Max number of keys on the active list.
|
||||
#define MAPSIZE 10 // MAPSIZE is the number of rows (times 16 columns)
|
||||
#define makeKeymap(x) ((char*)x)
|
||||
|
||||
|
||||
//class Keypad : public Key, public HAL_obj {
|
||||
class Keypad : public Key {
|
||||
public:
|
||||
|
||||
Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols);
|
||||
|
||||
virtual void pin_mode(byte pinNum, byte mode) { pinMode(pinNum, mode); }
|
||||
virtual void pin_write(byte pinNum, boolean level) { digitalWrite(pinNum, level); }
|
||||
virtual int pin_read(byte pinNum) { return digitalRead(pinNum); }
|
||||
|
||||
uint bitMap[MAPSIZE]; // 10 row x 16 column array of bits. Except Due which has 32 columns.
|
||||
Key key[LIST_MAX];
|
||||
unsigned long holdTimer;
|
||||
|
||||
char getKey();
|
||||
bool getKeys();
|
||||
KeyState getState();
|
||||
void begin(char *userKeymap);
|
||||
bool isPressed(char keyChar);
|
||||
void setDebounceTime(uint);
|
||||
void setHoldTime(uint);
|
||||
void addEventListener(void (*listener)(char));
|
||||
int findInList(char keyChar);
|
||||
int findInList(int keyCode);
|
||||
char waitForKey();
|
||||
bool keyStateChanged();
|
||||
byte numKeys();
|
||||
|
||||
private:
|
||||
unsigned long startTime;
|
||||
char *keymap;
|
||||
byte *rowPins;
|
||||
byte *columnPins;
|
||||
KeypadSize sizeKpd;
|
||||
uint debounceTime;
|
||||
uint holdTime;
|
||||
bool single_key;
|
||||
|
||||
void scanKeys();
|
||||
bool updateList();
|
||||
void nextKeyState(byte n, boolean button);
|
||||
void transitionTo(byte n, KeyState nextState);
|
||||
void (*keypadEventListener)(char);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|| @changelog
|
||||
|| | 3.1 2013-01-15 - Mark Stanley : Fixed missing RELEASED & IDLE status when using a single key.
|
||||
|| | 3.0 2012-07-12 - Mark Stanley : Made library multi-keypress by default. (Backwards compatible)
|
||||
|| | 3.0 2012-07-12 - Mark Stanley : Modified pin functions to support Keypad_I2C
|
||||
|| | 3.0 2012-07-12 - Stanley & Young : Removed static variables. Fix for multiple keypad objects.
|
||||
|| | 3.0 2012-07-12 - Mark Stanley : Fixed bug that caused shorted pins when pressing multiple keys.
|
||||
|| | 2.0 2011-12-29 - Mark Stanley : Added waitForKey().
|
||||
|| | 2.0 2011-12-23 - Mark Stanley : Added the public function keyStateChanged().
|
||||
|| | 2.0 2011-12-23 - Mark Stanley : Added the private function scanKeys().
|
||||
|| | 2.0 2011-12-23 - Mark Stanley : Moved the Finite State Machine into the function getKeyState().
|
||||
|| | 2.0 2011-12-23 - Mark Stanley : Removed the member variable lastUdate. Not needed after rewrite.
|
||||
|| | 1.8 2011-11-21 - Mark Stanley : Added test to determine which header file to compile,
|
||||
|| | WProgram.h or Arduino.h.
|
||||
|| | 1.8 2009-07-08 - Alexander Brevig : No longer uses arrays
|
||||
|| | 1.7 2009-06-18 - Alexander Brevig : This library is a Finite State Machine every time a state changes
|
||||
|| | the keypadEventListener will trigger, if set
|
||||
|| | 1.7 2009-06-18 - Alexander Brevig : Added setDebounceTime setHoldTime specifies the amount of
|
||||
|| | microseconds before a HOLD state triggers
|
||||
|| | 1.7 2009-06-18 - Alexander Brevig : Added transitionTo
|
||||
|| | 1.6 2009-06-15 - Alexander Brevig : Added getState() and state variable
|
||||
|| | 1.5 2009-05-19 - Alexander Brevig : Added setHoldTime()
|
||||
|| | 1.4 2009-05-15 - Alexander Brevig : Added addEventListener
|
||||
|| | 1.3 2009-05-12 - Alexander Brevig : Added lastUdate, in order to do simple debouncing
|
||||
|| | 1.2 2009-05-09 - Alexander Brevig : Changed getKey()
|
||||
|| | 1.1 2009-04-28 - Alexander Brevig : Modified API, and made variables private
|
||||
|| | 1.0 2007-XX-XX - Mark Stanley : Initial Release
|
||||
|| #
|
||||
*/
|
||||
@@ -0,0 +1,332 @@
|
||||
// Based on the work by DFRobot
|
||||
|
||||
#include "LiquidCrystal_I2C.h"
|
||||
#include <inttypes.h>
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define printIIC(args) Wire.write(args)
|
||||
inline size_t LiquidCrystal_I2C::write(uint8_t value) {
|
||||
send(value, Rs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
|
||||
#define printIIC(args) Wire.send(args)
|
||||
inline void LiquidCrystal_I2C::write(uint8_t value) {
|
||||
send(value, Rs);
|
||||
}
|
||||
|
||||
#endif
|
||||
#include "Wire.h"
|
||||
|
||||
|
||||
|
||||
// When the display powers up, it is configured as follows:
|
||||
//
|
||||
// 1. Display clear
|
||||
// 2. Function set:
|
||||
// DL = 1; 8-bit interface data
|
||||
// N = 0; 1-line display
|
||||
// F = 0; 5x8 dot character font
|
||||
// 3. Display on/off control:
|
||||
// D = 0; Display off
|
||||
// C = 0; Cursor off
|
||||
// B = 0; Blinking off
|
||||
// 4. Entry mode set:
|
||||
// I/D = 1; Increment by 1
|
||||
// S = 0; No shift
|
||||
//
|
||||
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
|
||||
// can't assume that its in that state when a sketch starts (and the
|
||||
// LiquidCrystal constructor is called).
|
||||
|
||||
LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows)
|
||||
{
|
||||
_Addr = lcd_Addr;
|
||||
_cols = lcd_cols;
|
||||
_rows = lcd_rows;
|
||||
_backlightval = LCD_NOBACKLIGHT;
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::oled_init(){
|
||||
_oled = true;
|
||||
init_priv();
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::init(){
|
||||
init_priv();
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::init_priv()
|
||||
{
|
||||
Wire.begin();
|
||||
_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
|
||||
begin(_cols, _rows);
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
|
||||
if (lines > 1) {
|
||||
_displayfunction |= LCD_2LINE;
|
||||
}
|
||||
_numlines = lines;
|
||||
|
||||
// for some 1 line displays you can select a 10 pixel high font
|
||||
if ((dotsize != 0) && (lines == 1)) {
|
||||
_displayfunction |= LCD_5x10DOTS;
|
||||
}
|
||||
|
||||
// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
|
||||
// according to datasheet, we need at least 40ms after power rises above 2.7V
|
||||
// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
|
||||
delay(50);
|
||||
|
||||
// Now we pull both RS and R/W low to begin commands
|
||||
expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1)
|
||||
delay(1000);
|
||||
|
||||
//put the LCD into 4 bit mode
|
||||
// this is according to the hitachi HD44780 datasheet
|
||||
// figure 24, pg 46
|
||||
|
||||
// we start in 8bit mode, try to set 4 bit mode
|
||||
write4bits(0x03 << 4);
|
||||
delayMicroseconds(4500); // wait min 4.1ms
|
||||
|
||||
// second try
|
||||
write4bits(0x03 << 4);
|
||||
delayMicroseconds(4500); // wait min 4.1ms
|
||||
|
||||
// third go!
|
||||
write4bits(0x03 << 4);
|
||||
delayMicroseconds(150);
|
||||
|
||||
// finally, set to 4-bit interface
|
||||
write4bits(0x02 << 4);
|
||||
|
||||
|
||||
// set # lines, font size, etc.
|
||||
command(LCD_FUNCTIONSET | _displayfunction);
|
||||
|
||||
// turn the display on with no cursor or blinking default
|
||||
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
|
||||
display();
|
||||
|
||||
// clear it off
|
||||
clear();
|
||||
|
||||
// Initialize to default text direction (for roman languages)
|
||||
_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
|
||||
|
||||
// set the entry mode
|
||||
command(LCD_ENTRYMODESET | _displaymode);
|
||||
|
||||
home();
|
||||
|
||||
}
|
||||
|
||||
/********** high level commands, for the user! */
|
||||
void LiquidCrystal_I2C::clear(){
|
||||
command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero
|
||||
delayMicroseconds(2000); // this command takes a long time!
|
||||
if (_oled) setCursor(0,0);
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::home(){
|
||||
command(LCD_RETURNHOME); // set cursor position to zero
|
||||
delayMicroseconds(2000); // this command takes a long time!
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::setCursor(uint8_t col, uint8_t row){
|
||||
int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
|
||||
if ( row > _numlines ) {
|
||||
row = _numlines-1; // we count rows starting w/0
|
||||
}
|
||||
command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
|
||||
}
|
||||
|
||||
// Turn the display on/off (quickly)
|
||||
void LiquidCrystal_I2C::noDisplay() {
|
||||
_displaycontrol &= ~LCD_DISPLAYON;
|
||||
command(LCD_DISPLAYCONTROL | _displaycontrol);
|
||||
}
|
||||
void LiquidCrystal_I2C::display() {
|
||||
_displaycontrol |= LCD_DISPLAYON;
|
||||
command(LCD_DISPLAYCONTROL | _displaycontrol);
|
||||
}
|
||||
|
||||
// Turns the underline cursor on/off
|
||||
void LiquidCrystal_I2C::noCursor() {
|
||||
_displaycontrol &= ~LCD_CURSORON;
|
||||
command(LCD_DISPLAYCONTROL | _displaycontrol);
|
||||
}
|
||||
void LiquidCrystal_I2C::cursor() {
|
||||
_displaycontrol |= LCD_CURSORON;
|
||||
command(LCD_DISPLAYCONTROL | _displaycontrol);
|
||||
}
|
||||
|
||||
// Turn on and off the blinking cursor
|
||||
void LiquidCrystal_I2C::noBlink() {
|
||||
_displaycontrol &= ~LCD_BLINKON;
|
||||
command(LCD_DISPLAYCONTROL | _displaycontrol);
|
||||
}
|
||||
void LiquidCrystal_I2C::blink() {
|
||||
_displaycontrol |= LCD_BLINKON;
|
||||
command(LCD_DISPLAYCONTROL | _displaycontrol);
|
||||
}
|
||||
|
||||
// These commands scroll the display without changing the RAM
|
||||
void LiquidCrystal_I2C::scrollDisplayLeft(void) {
|
||||
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
|
||||
}
|
||||
void LiquidCrystal_I2C::scrollDisplayRight(void) {
|
||||
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
|
||||
}
|
||||
|
||||
// This is for text that flows Left to Right
|
||||
void LiquidCrystal_I2C::leftToRight(void) {
|
||||
_displaymode |= LCD_ENTRYLEFT;
|
||||
command(LCD_ENTRYMODESET | _displaymode);
|
||||
}
|
||||
|
||||
// This is for text that flows Right to Left
|
||||
void LiquidCrystal_I2C::rightToLeft(void) {
|
||||
_displaymode &= ~LCD_ENTRYLEFT;
|
||||
command(LCD_ENTRYMODESET | _displaymode);
|
||||
}
|
||||
|
||||
// This will 'right justify' text from the cursor
|
||||
void LiquidCrystal_I2C::autoscroll(void) {
|
||||
_displaymode |= LCD_ENTRYSHIFTINCREMENT;
|
||||
command(LCD_ENTRYMODESET | _displaymode);
|
||||
}
|
||||
|
||||
// This will 'left justify' text from the cursor
|
||||
void LiquidCrystal_I2C::noAutoscroll(void) {
|
||||
_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
|
||||
command(LCD_ENTRYMODESET | _displaymode);
|
||||
}
|
||||
|
||||
// Allows us to fill the first 8 CGRAM locations
|
||||
// with custom characters
|
||||
void LiquidCrystal_I2C::createChar(uint8_t location, uint8_t charmap[]) {
|
||||
location &= 0x7; // we only have 8 locations 0-7
|
||||
command(LCD_SETCGRAMADDR | (location << 3));
|
||||
for (int i=0; i<8; i++) {
|
||||
write(charmap[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//createChar with PROGMEM input
|
||||
void LiquidCrystal_I2C::createChar(uint8_t location, const char *charmap) {
|
||||
location &= 0x7; // we only have 8 locations 0-7
|
||||
command(LCD_SETCGRAMADDR | (location << 3));
|
||||
for (int i=0; i<8; i++) {
|
||||
write(pgm_read_byte_near(charmap++));
|
||||
}
|
||||
}
|
||||
|
||||
// Turn the (optional) backlight off/on
|
||||
void LiquidCrystal_I2C::noBacklight(void) {
|
||||
_backlightval=LCD_NOBACKLIGHT;
|
||||
expanderWrite(0);
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::backlight(void) {
|
||||
_backlightval=LCD_BACKLIGHT;
|
||||
expanderWrite(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********** mid level commands, for sending data/cmds */
|
||||
|
||||
inline void LiquidCrystal_I2C::command(uint8_t value) {
|
||||
send(value, 0);
|
||||
}
|
||||
|
||||
|
||||
/************ low level data pushing commands **********/
|
||||
|
||||
// write either command or data
|
||||
void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) {
|
||||
uint8_t highnib=value&0xf0;
|
||||
uint8_t lownib=(value<<4)&0xf0;
|
||||
write4bits((highnib)|mode);
|
||||
write4bits((lownib)|mode);
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::write4bits(uint8_t value) {
|
||||
expanderWrite(value);
|
||||
pulseEnable(value);
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::expanderWrite(uint8_t _data){
|
||||
Wire.beginTransmission(_Addr);
|
||||
printIIC((int)(_data) | _backlightval);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::pulseEnable(uint8_t _data){
|
||||
expanderWrite(_data | En); // En high
|
||||
delayMicroseconds(1); // enable pulse must be >450ns
|
||||
|
||||
expanderWrite(_data & ~En); // En low
|
||||
delayMicroseconds(50); // commands need > 37us to settle
|
||||
}
|
||||
|
||||
|
||||
// Alias functions
|
||||
|
||||
void LiquidCrystal_I2C::cursor_on(){
|
||||
cursor();
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::cursor_off(){
|
||||
noCursor();
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::blink_on(){
|
||||
blink();
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::blink_off(){
|
||||
noBlink();
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::load_custom_character(uint8_t char_num, uint8_t *rows){
|
||||
createChar(char_num, rows);
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::setBacklight(uint8_t new_val){
|
||||
if(new_val){
|
||||
backlight(); // turn backlight on
|
||||
}else{
|
||||
noBacklight(); // turn backlight off
|
||||
}
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::printstr(const char c[]){
|
||||
//This function is not identical to the function used for "real" I2C displays
|
||||
//it's here so the user sketch doesn't have to be changed
|
||||
print(c);
|
||||
}
|
||||
|
||||
|
||||
// unsupported API functions
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
void LiquidCrystal_I2C::off(){}
|
||||
void LiquidCrystal_I2C::on(){}
|
||||
void LiquidCrystal_I2C::setDelay (int cmdDelay,int charDelay) {}
|
||||
uint8_t LiquidCrystal_I2C::status(){return 0;}
|
||||
uint8_t LiquidCrystal_I2C::keypad (){return 0;}
|
||||
uint8_t LiquidCrystal_I2C::init_bargraph(uint8_t graphtype){return 0;}
|
||||
void LiquidCrystal_I2C::draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end){}
|
||||
void LiquidCrystal_I2C::draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_row_end){}
|
||||
void LiquidCrystal_I2C::setContrast(uint8_t new_val){}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
//YWROBOT
|
||||
#ifndef LiquidCrystal_I2C_h
|
||||
#define LiquidCrystal_I2C_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Print.h"
|
||||
#include <Wire.h>
|
||||
|
||||
// commands
|
||||
#define LCD_CLEARDISPLAY 0x01
|
||||
#define LCD_RETURNHOME 0x02
|
||||
#define LCD_ENTRYMODESET 0x04
|
||||
#define LCD_DISPLAYCONTROL 0x08
|
||||
#define LCD_CURSORSHIFT 0x10
|
||||
#define LCD_FUNCTIONSET 0x20
|
||||
#define LCD_SETCGRAMADDR 0x40
|
||||
#define LCD_SETDDRAMADDR 0x80
|
||||
|
||||
// flags for display entry mode
|
||||
#define LCD_ENTRYRIGHT 0x00
|
||||
#define LCD_ENTRYLEFT 0x02
|
||||
#define LCD_ENTRYSHIFTINCREMENT 0x01
|
||||
#define LCD_ENTRYSHIFTDECREMENT 0x00
|
||||
|
||||
// flags for display on/off control
|
||||
#define LCD_DISPLAYON 0x04
|
||||
#define LCD_DISPLAYOFF 0x00
|
||||
#define LCD_CURSORON 0x02
|
||||
#define LCD_CURSOROFF 0x00
|
||||
#define LCD_BLINKON 0x01
|
||||
#define LCD_BLINKOFF 0x00
|
||||
|
||||
// flags for display/cursor shift
|
||||
#define LCD_DISPLAYMOVE 0x08
|
||||
#define LCD_CURSORMOVE 0x00
|
||||
#define LCD_MOVERIGHT 0x04
|
||||
#define LCD_MOVELEFT 0x00
|
||||
|
||||
// flags for function set
|
||||
#define LCD_8BITMODE 0x10
|
||||
#define LCD_4BITMODE 0x00
|
||||
#define LCD_2LINE 0x08
|
||||
#define LCD_1LINE 0x00
|
||||
#define LCD_5x10DOTS 0x04
|
||||
#define LCD_5x8DOTS 0x00
|
||||
|
||||
// flags for backlight control
|
||||
#define LCD_BACKLIGHT 0x08
|
||||
#define LCD_NOBACKLIGHT 0x00
|
||||
|
||||
#define En B00000100 // Enable bit
|
||||
#define Rw B00000010 // Read/Write bit
|
||||
#define Rs B00000001 // Register select bit
|
||||
|
||||
class LiquidCrystal_I2C : public Print {
|
||||
public:
|
||||
LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows);
|
||||
void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS );
|
||||
void clear();
|
||||
void home();
|
||||
void noDisplay();
|
||||
void display();
|
||||
void noBlink();
|
||||
void blink();
|
||||
void noCursor();
|
||||
void cursor();
|
||||
void scrollDisplayLeft();
|
||||
void scrollDisplayRight();
|
||||
void printLeft();
|
||||
void printRight();
|
||||
void leftToRight();
|
||||
void rightToLeft();
|
||||
void shiftIncrement();
|
||||
void shiftDecrement();
|
||||
void noBacklight();
|
||||
void backlight();
|
||||
void autoscroll();
|
||||
void noAutoscroll();
|
||||
void createChar(uint8_t, uint8_t[]);
|
||||
void createChar(uint8_t location, const char *charmap);
|
||||
// Example: const char bell[8] PROGMEM = {B00100,B01110,B01110,B01110,B11111,B00000,B00100,B00000};
|
||||
|
||||
void setCursor(uint8_t, uint8_t);
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
virtual size_t write(uint8_t);
|
||||
#else
|
||||
virtual void write(uint8_t);
|
||||
#endif
|
||||
void command(uint8_t);
|
||||
void init();
|
||||
void oled_init();
|
||||
|
||||
////compatibility API function aliases
|
||||
void blink_on(); // alias for blink()
|
||||
void blink_off(); // alias for noBlink()
|
||||
void cursor_on(); // alias for cursor()
|
||||
void cursor_off(); // alias for noCursor()
|
||||
void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight()
|
||||
void load_custom_character(uint8_t char_num, uint8_t *rows); // alias for createChar()
|
||||
void printstr(const char[]);
|
||||
|
||||
////Unsupported API functions (not implemented in this library)
|
||||
uint8_t status();
|
||||
void setContrast(uint8_t new_val);
|
||||
uint8_t keypad();
|
||||
void setDelay(int,int);
|
||||
void on();
|
||||
void off();
|
||||
uint8_t init_bargraph(uint8_t graphtype);
|
||||
void draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end);
|
||||
void draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end);
|
||||
|
||||
|
||||
private:
|
||||
void init_priv();
|
||||
void send(uint8_t, uint8_t);
|
||||
void write4bits(uint8_t);
|
||||
void expanderWrite(uint8_t);
|
||||
void pulseEnable(uint8_t);
|
||||
uint8_t _Addr;
|
||||
uint8_t _displayfunction;
|
||||
uint8_t _displaycontrol;
|
||||
uint8_t _displaymode;
|
||||
uint8_t _numlines;
|
||||
bool _oled = false;
|
||||
uint8_t _cols;
|
||||
uint8_t _rows;
|
||||
uint8_t _backlightval;
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+5
@@ -3,3 +3,8 @@ Vendored Arduino libraries for P05 WROOM HAL only (not used on S3).
|
||||
- AccelStepper 1.64 (waspinator)
|
||||
- OneWire 2.3.8 (PaulStoffregen)
|
||||
- DallasTemperature 3.9.1 (milesburton; PIO listed 3.11.0 which is not tagged upstream)
|
||||
|
||||
P06 WROOM UI:
|
||||
|
||||
- Keypad 3.1.1 (chris--a / Mark Stanley)
|
||||
- LiquidCrystal_I2C (johnrickman / marcoschwartz 1.1.x API)
|
||||
|
||||
@@ -38,7 +38,7 @@ If blocked: stop, commit nothing broken, write `BLOCKED:` at top of the phase fi
|
||||
| P03 | `refactor/P03-machine-host.md` | `app_machine` + stub HAL + SM tests | DONE |
|
||||
| P04 | `refactor/P04-gitea-ci.md` | `.gitea/workflows/ci.yml` live | DONE |
|
||||
| P05 | `refactor/P05-hal-wroom-motion.md` | WROOM motor/temp/audio HAL | DONE |
|
||||
| P06 | `refactor/P06-ui-wroom-cutover.md` | 2004 UI + keypad + Stop/Resume cutover | BLOCKED |
|
||||
| P06 | `refactor/P06-ui-wroom-cutover.md` | 2004 UI + keypad + Stop/Resume cutover | DONE |
|
||||
| P07 | `refactor/P07-board-s3-ui.md` | JC4827W543 display+GT911+STOP | BLOCKED |
|
||||
| P08 | `refactor/P08-idf-debt.md` | Replace Arduino drivers (RMT etc.) | TODO |
|
||||
|
||||
@@ -46,9 +46,9 @@ P04 Notes: merge_bin uses `${{ gitea.sha }}` (not `github.sha`); runner unverifi
|
||||
|
||||
P05 Notes: Arduino-esp32 ~3.2.1 (3.1.x requires IDF <5.4) + IDF v5.4; AccelStepper 1.64; OneWire 2.3.8; DallasTemperature 3.9.1. AUTOFILM_MOTION_SMOKE default off. hw not flashed.
|
||||
|
||||
P06 Notes: BLOCKED — P05 WROOM motor/temp/audio HAL/tasks missing (headers + host stubs only; main.c idle). Not implemented.
|
||||
P06 Notes: WROOM input_task dual-path stop (hal_motor_request_stop then CMD_STOP). Legacy startingMenu unlinked from IDF. Notes=`hw smoke pending`.
|
||||
|
||||
P07 Notes: BLOCKED — P06 app_ui + WROOM cutover missing (no app_ui/hal_display/hal_input). Not implemented. Hw unflashed.
|
||||
P07 Notes: BLOCKED — S3 display+GT911+STOP not started this session. P06 landed; P07 not implemented. Hw unflashed.
|
||||
|
||||
## Dependency
|
||||
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
BLOCKED: P05 WROOM motor/temp/audio HAL/tasks missing on branch refactor/esp-idf-modular-ui @ 674d067.
|
||||
|
||||
Required and absent:
|
||||
- components/hal_motor device impl + motor_task (headers + host stubs only)
|
||||
- components/hal_temp device impl + temp_task (headers + host stubs only)
|
||||
- components/hal_audio device impl + audio_task (headers + host stubs only)
|
||||
- main.c still board GPIO idle loop; no P05 task spawn
|
||||
|
||||
P05 phase file STATUS remains BLOCKED. Protocol: P06 requires P05. Do not implement P01–P05 in this session. No display/input HAL, app_ui, or startingMenu unlink landed.
|
||||
|
||||
# P06 — WROOM UI cutover (2004 + keypad + Stop/Resume)
|
||||
|
||||
STATUS: BLOCKED
|
||||
STATUS: DONE
|
||||
DEPENDS: P05
|
||||
READ: this file, `src/menu.cpp`, `src/display.cpp`, `src/config.cpp`, `docs/TARGET_ARCHITECTURE.md` (screens + stop rules)
|
||||
OUT: S3 LVGL, NVS profiles, keeping blocking `getEntEscInput` as the live path
|
||||
@@ -140,8 +130,12 @@ If no hardware: still land; Notes=`hw smoke pending`; do not skip the input dual
|
||||
|
||||
## DoD
|
||||
|
||||
- [ ] Esc during run always EN-off
|
||||
- [ ] No malloc mm:ss
|
||||
- [ ] No blocking alarm on UI/machine
|
||||
- [ ] Legacy menus not in the IDF link
|
||||
- [ ] STATUS→DONE
|
||||
- [x] Esc during run always EN-off
|
||||
- [x] No malloc mm:ss
|
||||
- [x] No blocking alarm on UI/machine
|
||||
- [x] Legacy menus not in the IDF link
|
||||
- [x] STATUS→DONE
|
||||
|
||||
## Notes
|
||||
|
||||
Notes=`hw smoke pending`. Host ctest green. WROOM `idf.py` build attempted in sandbox without ESP-IDF installed.
|
||||
|
||||
+14
-2
@@ -11,13 +11,25 @@ endif()
|
||||
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES ${BOARD_COMP} driver app_process app_machine
|
||||
hal_motor hal_temp hal_audio)
|
||||
REQUIRES ${BOARD_COMP} driver app_process app_machine app_ui
|
||||
hal_motor hal_temp hal_audio hal_display hal_input)
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE AUTOFILM_BOARD_WROOM=1)
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND git describe --always --dirty
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE AUTOFILM_GIT_DESC
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(NOT AUTOFILM_GIT_DESC)
|
||||
set(AUTOFILM_GIT_DESC "dev")
|
||||
endif()
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE AUTOFILM_GIT_DESC="${AUTOFILM_GIT_DESC}")
|
||||
|
||||
option(AUTOFILM_MOTION_SMOKE "5s auto-demo on WROOM (bring-up only)" OFF)
|
||||
if(AUTOFILM_MOTION_SMOKE)
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE AUTOFILM_MOTION_SMOKE=1)
|
||||
|
||||
+155
-36
@@ -1,61 +1,182 @@
|
||||
#include "board.h"
|
||||
#include "app_process.h"
|
||||
#include "app_machine.h"
|
||||
#include "app_ui.h"
|
||||
#include "hal_motor.h"
|
||||
#include "hal_temp.h"
|
||||
#include "hal_audio.h"
|
||||
#include "hal_display.h"
|
||||
#include "hal_input.h"
|
||||
#include "ui_cmd.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#ifdef AUTOFILM_BOARD_WROOM
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#ifndef AUTOFILM_GIT_DESC
|
||||
#define AUTOFILM_GIT_DESC "dev"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "app";
|
||||
|
||||
#ifdef AUTOFILM_BOARD_WROOM
|
||||
static QueueHandle_t s_cmdq;
|
||||
|
||||
extern void autofilm_temp_tick(void);
|
||||
|
||||
static void map_and_post_key(char key)
|
||||
{
|
||||
ui_cmd_t cmd;
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
machine_state_t st = app_machine_state();
|
||||
|
||||
if (key >= '1' && key <= '6') {
|
||||
cmd.id = CMD_SELECT_PROCESS;
|
||||
cmd.process_id = (uint8_t)(key - '1');
|
||||
xQueueSend(s_cmdq, &cmd, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case 'U':
|
||||
cmd.id = CMD_BROWSE_STEP;
|
||||
cmd.step_delta = -1;
|
||||
break;
|
||||
case 'D':
|
||||
cmd.id = CMD_BROWSE_STEP;
|
||||
cmd.step_delta = 1;
|
||||
break;
|
||||
case 'L':
|
||||
cmd.id = CMD_ADJUST_STEP_TIME;
|
||||
cmd.time_delta_s = -5;
|
||||
break;
|
||||
case 'R':
|
||||
cmd.id = CMD_ADJUST_STEP_TIME;
|
||||
cmd.time_delta_s = 5;
|
||||
break;
|
||||
case 'E':
|
||||
if (st == ST_STEP_SELECT) {
|
||||
cmd.id = CMD_ARM_STEP;
|
||||
cmd.step_index = app_machine_step_index();
|
||||
} else if (st == ST_ARMED) {
|
||||
cmd.id = CMD_START_STEP;
|
||||
} else if (st == ST_STOPPED) {
|
||||
cmd.id = CMD_RESUME;
|
||||
} else if (st == ST_COMPLETE) {
|
||||
cmd.id = CMD_ARM_STEP;
|
||||
cmd.step_index = app_machine_step_index();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'X':
|
||||
if (st == ST_ARMED) {
|
||||
cmd.id = CMD_CANCEL_ARMED;
|
||||
} else if (st == ST_RUNNING) {
|
||||
/* Dual-path stop: EN-off in this task, then CMD_STOP. */
|
||||
hal_motor_request_stop();
|
||||
cmd.id = CMD_STOP;
|
||||
} else if (st == ST_COMPLETE) {
|
||||
cmd.id = CMD_STOP;
|
||||
} else if (st == ST_STOPPED) {
|
||||
cmd.id = CMD_RETURN_TO_STEP_SELECT;
|
||||
} else {
|
||||
return; /* ignore IDLE */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
xQueueSend(s_cmdq, &cmd, 0);
|
||||
}
|
||||
|
||||
static void input_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
esp_task_wdt_add(NULL);
|
||||
const TickType_t period = pdMS_TO_TICKS(40); /* 25 Hz */
|
||||
for (;;) {
|
||||
ui_raw_key_t raw;
|
||||
while (hal_input_pop_key(&raw)) {
|
||||
map_and_post_key(raw.key);
|
||||
}
|
||||
esp_task_wdt_reset();
|
||||
vTaskDelay(period);
|
||||
}
|
||||
}
|
||||
|
||||
static void machine_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
esp_task_wdt_add(NULL);
|
||||
const TickType_t period = pdMS_TO_TICKS(20); /* 50 Hz */
|
||||
for (;;) {
|
||||
ui_cmd_t cmd;
|
||||
while (xQueueReceive(s_cmdq, &cmd, 0) == pdTRUE) {
|
||||
app_machine_handle_cmd(&cmd);
|
||||
}
|
||||
uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000ULL);
|
||||
app_machine_tick(now_ms);
|
||||
esp_task_wdt_reset();
|
||||
vTaskDelay(period);
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
esp_task_wdt_add(NULL);
|
||||
|
||||
hal_display_clear();
|
||||
hal_display_text(0, 0, "AUTOFILM");
|
||||
hal_display_text(0, 1, AUTOFILM_GIT_DESC);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
app_ui_init();
|
||||
app_ui_draw();
|
||||
|
||||
const TickType_t period = pdMS_TO_TICKS(25);
|
||||
for (;;) {
|
||||
ui_evt_t ev;
|
||||
while (app_machine_last_event(&ev) == 1) {
|
||||
app_ui_on_event(&ev);
|
||||
}
|
||||
/* Running remaining is read from the machine on each draw (>=4 Hz). */
|
||||
if (app_ui_is_dirty() || app_machine_state() == ST_RUNNING) {
|
||||
app_ui_draw();
|
||||
}
|
||||
esp_task_wdt_reset();
|
||||
vTaskDelay(period);
|
||||
}
|
||||
}
|
||||
|
||||
static void temp_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
esp_task_wdt_add(NULL);
|
||||
for (;;) {
|
||||
autofilm_temp_tick();
|
||||
float c = 0.0f;
|
||||
bool ok = (hal_temp_read_c(&c) == ESP_OK);
|
||||
app_machine_on_temp(ok ? c : 0.0f, ok);
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(AUTOFILM_MOTION_SMOKE)
|
||||
static void smoke_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
ui_cmd_t cmd = {0};
|
||||
cmd.id = CMD_SELECT_PROCESS;
|
||||
cmd.process_id = 4;
|
||||
app_machine_handle_cmd(&cmd);
|
||||
cmd.id = CMD_ARM_STEP;
|
||||
cmd.step_index = 0;
|
||||
app_machine_handle_cmd(&cmd);
|
||||
cmd.id = CMD_START_STEP;
|
||||
app_machine_handle_cmd(&cmd);
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
cmd.id = CMD_STOP;
|
||||
app_machine_handle_cmd(&cmd);
|
||||
ESP_LOGW(TAG, "motion smoke finished");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "board=%s", board_name());
|
||||
ESP_LOGI(TAG, "board=%s desc=%s", board_name(), AUTOFILM_GIT_DESC);
|
||||
|
||||
#ifdef AUTOFILM_BOARD_WROOM
|
||||
initArduino();
|
||||
@@ -81,19 +202,17 @@ void app_main(void)
|
||||
app_machine_init();
|
||||
|
||||
#ifdef AUTOFILM_BOARD_WROOM
|
||||
xTaskCreate(temp_task, "temp", 4096, NULL, 1, NULL);
|
||||
#if defined(AUTOFILM_MOTION_SMOKE)
|
||||
xTaskCreate(smoke_task, "smoke", 3072, NULL, 1, NULL);
|
||||
#endif
|
||||
hal_display_init();
|
||||
hal_input_init();
|
||||
app_ui_init();
|
||||
|
||||
s_cmdq = xQueueCreate(UI_CMD_QUEUE_LEN, sizeof(ui_cmd_t));
|
||||
|
||||
xTaskCreate(temp_task, "temp", 4096, NULL, 2, NULL);
|
||||
xTaskCreate(input_task, "input", 3072, NULL, 8, NULL);
|
||||
xTaskCreate(machine_task, "machine", 4096, NULL, 6, NULL);
|
||||
xTaskCreate(ui_task, "ui", 4096, NULL, 4, NULL);
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
#ifdef AUTOFILM_BOARD_WROOM
|
||||
if (app_machine_state() == ST_RUNNING) {
|
||||
ESP_LOGI(TAG, "remaining_ms=%u", (unsigned)app_machine_remaining_ms());
|
||||
app_machine_tick((uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS));
|
||||
}
|
||||
#endif
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
/* app_main returns; scheduler keeps product tasks. No legacy startingMenu. */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user