我试图在Arduino中做多个菜单。每个菜单都有很多行。每行可能包含需要显示的变量。我无法理解如何:
a)正确定义我的菜单结构 b)将数据加载到其中 c)使用它
下面我的代码几乎我想 - 看看我的评论(寻找" //帮助"):
// Example for doing Menus for JB
#define MAX_MENU_LINES 3 // How many lines are on each of your menu screens
typedef struct menu_item_def {
byte x; byte y; // Coordinates of the start for the line of test
byte selected; // set to 1 if the menu buttons have this option selected
char *mtext; // What to say, including sprintf placeholders: eg: "STOP TIMER...% 4.2i MIN"
byte mdatatype1; // 0 means this is actual data to print. 1 means go call the supplied function to get the data when needed.
void *mdata1; // Where to get any data from for the menu (upto 2 different bits allowed per line)
byte mdatatype2;
void *mdata2;
} menu_item_type;
// I do not want to do this:-
struct MenuT {
menu_item_type mline;
} MainMenu[MAX_MENU_LINES] = {
{1,2,0, "STOP TIMER....% 5.1i MIN" ,0,(void *)85,0,0},
{1,2,1, "CHEM RATE....% 5.2f Lt/Hr" ,1,(void *)DemoData,0,0},
{1,2,0, "CHEM PUMP.... %s" ,0,(void *)"ON/OFF",0,0},
};
// HELP
/* I would prefer to do something like this:-
typedef struct menu_def {
menu_item_type mline[MAX_MENU_LINES];
} menu_type;
menu_type MainMenu = {
{1,2,0, "STOP TIMER....% 5.1i MIN" ,0,(void *)85,0,0},
{1,2,1, "CHEM RATE....% 5.2f Lt/Hr" ,0,(void *)85,0,0},
{1,2,0, "CHEM PUMP.... %s" ,0,(void *)"ON/OFF",0,0},
};
menu_type SubMenu = {
{1,2,0, "MOTOR RPM.....% 5.1i RPM" ,0,(void *)8500,0,0},
{1,2,1, "ALARM MAX....% 5.2f Lt/Hr" ,0,(void *)85,0,0},
{1,2,0, "ALARM MIN.... %s" ,0,(void *)"ON/OFF",0,0},
};
*/
#define MAX_WIDTH 256 // This is the max width of anything you need to print - increase this if your LCD is wider
char buf[MAX_WIDTH];
char *p(char *fmt, ... ){ // Helper routine for putting numbers/readings/etc into printable strings
va_list args; va_start (args, fmt );
vsnprintf(buf, MAX_WIDTH, fmt, args);
va_end (args);
return buf;
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600); // I have no LCD, so, my output is to serial (Hit Ctrl+Shit+M or go Tools->Serial Monitor to see output). Note: Seral-Monitor will reset board.
Menu(MainMenu); // HELP - I don't know how to pass whatever got defined above
}
void loop() {
// put your main code here, to run repeatedly:
// Does nothing - the setup outputted the menu already
}
int Menu( /* // HELP - I don't know what to put here to recevied a passed-in menu */ ) {
for(int i=0;i<MAX_MENU_LINES;i++) {
// Serial.println(mymenu[i].mtext); // Help - I don't know how to reference the bit I need!
};
Serial.print(p("STOP TIMER...% 4.2i MIN",5));
}
int DemoData() {
return 85;
}
很遗憾听起来像是一个n00b - 自30年前我学到这个以来的某个地方,这个细节从我的大脑中被垃圾收集: - (
答案 0 :(得分:1)
你快到了。
您的菜单数据结构是struct MenuT
个项目的数组。那么Menu()
函数的函数原型就是
int Menu(struct MenuT menu[])
{
...
}
(您可能想要添加第二个参数,以使函数中的有效元素数量可用)。
在您的代码中仍然存在一些不一致的问题(例如,在声明之前调用的函数)和一些 - 嗯 - 丑陋的部分(函数指针和使用空隙的同一结构成员上的数据)是联盟的意思,但我想你可以自己解决这个问题。
[编辑:将此添加为需求,其中附加注释更清晰]
如果您需要多个菜单,可以按如下方式设置数据结构:
struct MenuT {
int num_items; /* number of items in mline-array */
menu_item_type *mline;
};
struct menu_item_type main_menu_items[] = {
{ 1,2,0, "STOP TIMER....% 5.1i MIN" ,0, (void *) 85,0,0 },
{ 1,2,1, "CHEM RATE....% 5.2f Lt/Hr" ,1, (void *) DemoData,0,0 },
{ 1,2,0, "CHEM PUMP.... %s" ,0, (void *) "ON/OFF",0,0 },
};
struct menu_item_type other_menu_items[] = {
{ .... },
};
struct MenuT main_menu = {
3,
main_menu_items
};
struct MenuT other_menu = {
2,
other_menu_items
};
引用Menu()
中的项目就会变成
int Menu(struct MenuT *menu) {
for (int i = 0; i < menu->num_items; i++) {
Serial.println(menu->items[i].mtext);
}
}
从main()
调用时
Menu(&main_menu);
Menu(&other_menu);
答案 1 :(得分:0)
还没有答案,但是,现在修复了大部分问题。 剩下的是 - 我如何使用联合,或者如果不使用联合,我如何启动对返回数据的函数的调用。这似乎不是: -
long myint=(*(mymenu[i].mdata1))();
这是代码 - 搜索“// Help”
// Example for doing Menus for JB
#define MAX_MENU_LINES 3 // How many lines are on each of your menu screens
// This is unused - I think it's imposible to static-init using these?
union multi_data {
int d_int; // mdatatype 0
void *d_function; // 1
float d_float; // 2
};
// This is the definition of each line in a menu
typedef struct menu_item_def {
byte x; byte y; // Coordinates of the start for the line of test
byte selected; // set to 1 if the menu buttons have this option selected
char *mtext; // What to say, including sprintf placeholders: eg: "STOP TIMER...% 4.2i MIN"
byte mdatatype1; // 0 means this is actual data to print. 1 means go call the supplied function to get the data when needed.
void *mdata1; // Where to get any data from for the menu (upto 2 different bits allowed per line)
byte mdatatype2;
void *mdata2;
} menu_item_type;
// All menus are a collection of lines (exactly MAX_MENU_LINES (3 in this example) of them to be exact)
struct menu_item_def main_menu_items[MAX_MENU_LINES] = {
{ 1,2,0, "STOP TIMER....% 5.1i MIN" ,0, (void *)85,0,0 },
{ 1,2,1, "CHEM RATE....% 5.2f Lt/Hr" ,1, (void *)DemoData,0,0 },
{ 1,2,0, "CHEM PUMP.... %s" ,0, (void *)"ON/OFF",0,0 },
};
struct menu_item_def sub_menu_items[MAX_MENU_LINES] = {
{1,2,0, "MOTOR RPM.....% 5.1i RPM" ,0,(void *)8500,0,0},
{1,2,1, "ALARM MAX....% 5.2f Lt/Hr" ,0,(void *)85,0,0},
{1,2,0, "ALARM MIN.... %s" ,0,(void *)"ON/OFF",0,0},
};
#define MAX_WIDTH 256 // This is the max width of anything you need to print - increase this if your LCD is wider
char buf[MAX_WIDTH];
char *p(char *fmt, ... ){ // Helper routine for putting numbers/readings/etc into printable strings
va_list args; va_start (args, fmt );
vsnprintf(buf, MAX_WIDTH, fmt, args);
va_end (args);
return buf;
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600); // I have no LCD, so, my output is to serial (Hit Ctrl+Shit+M or go Tools->Serial Monitor to see output). Note: Seral-Monitor will reset board.
Menu(main_menu_items);
Menu(sub_menu_items);
}
void loop() {
// put your main code here, to run repeatedly:
// Does nothing - the setup outputted the menu already
}
int Menu( struct menu_item_def *mymenu ) {
for(int i=0;i<MAX_MENU_LINES;i++) {
if(mymenu[i].mdatatype1==0) { // an int
long myint=(long)mymenu[i].mdata1;
Serial.println(p(mymenu[i].mtext,42));
} else {
long myint=(*(mymenu[i].mdata1))(); // Help - gives: "JB_Menus.ino:67:37: error: 'void*' is not a pointer-to-object type"
Serial.println(p(mymenu[i].mtext,myint));
}
};
Serial.print(p("STOP TIMER...% 4.2i MIN",5));
MenuLine(5,6);
}
int MenuLine(int x, int y) {
Serial.print(x);
}
int DemoData() {
return 86;
}
答案 2 :(得分:0)
这是我最终如何做到的。感谢所有提供我需要的线索的人!
我能说的最好 - 必须使用宏 - 由于芯片的架构和使用PROGMEM的必要性,任何其他方式都是不可能的。
警告:arduino中存在各种大小和行端限制和错误:如果您正在使用它,经常且经常编译 - 无法调试宏错误,因此您必须不要让错误蔓延。
对于任何一个人挠头:PROGMEM允许你把一些东西放在FLASH中(你已经有很多)而不是把它存放在SRAM中(这是非常有限的)。当你使用arduino变量耗尽空间时,你需要做这些事情。
#include <avr/pgmspace.h> // This lets us store static variables in FLASH instead of SRAM
#define MAX_MENU_WIDTH 64 // Max number of characters on a single menu line, PLUS 1
#define SERIAL_RATE 115200 // The BAUD rate of the serial port
// This is the definition of each line in a menu. Each line uses 14 bytes of SRAM
typedef struct menu_item_def {
byte x; byte y; // Coordinates of the start for the line of test
byte selected; // set to 1 if the menu buttons have this option selected
byte mdatatype1; // This number explains how to use the following 2 mdata* as output when needed:
char *mtext; // What to say, including sprintf placeholders: eg: "STOP TIMER...% 4.2i MIN"
void *mdata1; // Refer to the "int Menu()" function for details.
void *mdata2;
void *mdata3;
void *mdata4;
} menu_item_type;
#define Pv(a,b) a ## _ ## b
#define Ev(a,b) Pv(a,b)
// The following macro makes menu definitions easy-to-type, and stores menu text in (unchangeable) FLASH and menu data in (precious but small) SRAM
// These numbers should range from 1 to MAX_MENU_LINES. Best not to edit these, or add more than 12: they're already at the absolute max size that an arduino macro can be.
#define FLASH_PART(n, m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11) \
const char Ev(n,0)[] PROGMEM=m0; \
const char Ev(n,1)[] PROGMEM=m1; \
const char Ev(n,2)[] PROGMEM=m2; \
const char Ev(n,3)[] PROGMEM=m3; \
const char Ev(n,4)[] PROGMEM=m4; \
const char Ev(n,5)[] PROGMEM=m5; \
const char Ev(n,6)[] PROGMEM=m6; \
const char Ev(n,7)[] PROGMEM=m7; \
const char Ev(n,8)[] PROGMEM=m8; \
const char Ev(n,9)[] PROGMEM=m9; \
const char Ev(n,10)[] PROGMEM=m10; \
const char Ev(n,11)[] PROGMEM=m11;
#define MAX_MENU_LINES 12 // How many lines are on each of your menu screens
#define MAKE_MENU(n, x0,y0,s0,m0,t0,f0,d0,z0,q0, x1,y1,s1,m1,t1,f1,d1,z1,q1, x2,y2,s2,m2,t2,f2,d2,z2,q2, x3,y3,s3,m3,t3,f3,d3,z3,q3, x4,y4,s4,m4,t4,f4,d4,z4,q4, x5,y5,s5,m5,t5,f5,d5,z5,q5, x6,y6,s6,m6,t6,f6,d6,z6,q6, x7,y7,s7,m7,t7,f7,d7,z7,q7, x8,y8,s8,m8,t8,f8,d8,z8,q8, x9,y9,s9,m9,t9,f9,d9,z9,q9, x10,y10,s10,m10,t10,f10,d10,z10,q10, x11,y11,s11,m11,t11,f11,d11,z11,q11) \
FLASH_PART(n, m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11) \
struct menu_item_def n[MAX_MENU_LINES] = { {x0,y0,s0,t0,(char *)Ev(n,0),f0,d0,z0,q0},{x1,y1,s1,t1,(char *)Ev(n,1),f1,d1,z1,q1},{x2,y2,s2,t2,(char *)Ev(n,2),f2,d2,z2,q2},{x3,y3,s3,t3,(char *)Ev(n,3),f3,d3,z3,q3},{x4,y4,s4,t4,(char *)Ev(n,4),f4,d4,z4,q4},{x5,y5,s5,t5,(char *)Ev(n,5),f5,d5,z5,q5},{x6,y6,s6,t6,(char *)Ev(n,6),f6,d6,z6,q6},{x7,y7,s7,t7,(char *)Ev(n,7),f7,d7,z7,q7},{x8,y8,s8,t8,(char *)Ev(n,8),f8,d8,z8,q8},{x9,y9,s9,t9,(char *)Ev(n,9),f9,d9,z9,q9},{x10,y10,s10,t10,(char *)Ev(n,10),f10,d10,z10,q10},{x11,y11,s11,t11,(char *)Ev(n,11),f11,d11,z11,q11}, };
//FINALLY - Here is the "readable" way to initialize the structures (all the 7,8,9 stuff are placeholders for future expansion):
MAKE_MENU (main_menu_items,
1,1,1, "STOP TIMER.....% 5.1i MIN" ,0, (void *)83, (void *)7, (void *)8, (void *)9,
1,2,1, "CHEM ON/OFF....%s" ,2, (void *)"ON/OFF", (void *)7, (void *)8, (void *)9,
1,3,0, "MOTOR RPM......% s RPM" ,1, (void *)ReadRPM, (void *)7, (void *)8, (void *)9,
1,4,1, "TOT CHEM RATE..% 5.2i L/Hr" ,1, (void *)DemoData, (void *)7, (void *)8, (void *)9,
1,5,0, "TOT CHEM USED..% 5.2i L/Hr" ,1, (void *)DemoData, (void *)7, (void *)8, (void *)9,
1,6,1, "CHEM 1 RATE....% 23.6i L/Hr" ,0, (void *)83, (void *)7, (void *)8, (void *)9,
1,7,1, "CHEM 2 RATE....% 16.3i L/Hr" ,0, (void *)84, (void *)7, (void *)8, (void *)9,
1,8,1, "CHEM 3 RATE....% 34.9i L/Hr" ,0, (void *)85, (void *)7, (void *)8, (void *)9,
1,9,0, "PH READ........% s PH" ,1, (void *)ReadpHVolts, (void *)7, (void *)8, (void *)9,
1,10,1, "PH: Min: %f Max: %f" ,1, (void *)ReadpHMin, (void *)7, (void *)ReadpHMax, (void *)9,
1,11,0, "BATT VOLTS.....% s V" ,1, (void *)ReadBatt, (void *)7, (void *)8, (void *)9,
1,12,0, "Msg:%s" ,1, (void *)DemoData, (void *)7, (void *)8, (void *)9
);
要使用这些东西,我只是为简单数据类型执行此操作:
long myval=(long)mymenu[i].mdata1;
这是为了调用我的函数:
long myval=(*reinterpret_cast<long (*)()>(mymenu[i].mdata1))();