├── 50bee.png
├── LICENSE
├── README.md
├── helpers
├── 5ohBee.tar.gz
├── 99-USBasp.rules
└── SmartResponseXE-lib.zip
└── sketch
├── HugQuest.ino
├── RadioFunctions.h
├── SmartResponseXE.h
├── five-oh-BEE.ino
├── hexdump.ino
├── multi-chat.ino
├── multi-mode.ino
├── quest.ino
└── rssi.ino
/50bee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pdxbadgers/5ohBEE-2019/f2a227239b3fc3d9156bea2e8e7e3a9396c39c47/50bee.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 5ohBEE
2 | ======
3 |
4 | Setting up your Board
5 | ---------------------
6 | 1. udev rules: copy helpers/99-USBasp.rules to /etc/udev/rules.d/ (this assumes you are using the USBasp programmer, update your IDE accordingly)
7 | 2. Add the board to Arduino IDE: Unzip helpers/5ohBee.tar.gz in your Arduino sketch directory, restart the IDE, and set your board to "ATmega128RFA1 Dev Board" (under "Sparkfun AVR Boards")
8 | 3. Install dependencies: In the IDE, select "Sketch" -> "Include Library" -> "Add .ZIP Library..." to add helpers/SmartResponseXE-lib.zip to the project
9 | 4. ???
10 | 5. Profit! Add the contents of the sketch directory to your workspace, compile, and upload!
11 |
12 | Keymap
13 | ------
14 | The keymap is documented on lines 64 - 90 of SmartResponseXE/blob/master/SmartResponseXE.cpp. Brief hints below:
15 |
16 | Most of the ASCII-printable range is mapped.
17 |
18 | Function and Special Keys:
19 | - Left Column: 0xF0 - 0xF4 (Top to bottom)
20 | - Right Column: 0xF5 - 0xF9 (Top to bottom)
21 | - Left: 0x02
22 | - Right: 0x03
23 | - Up: 0x04
24 | - Down: 0x05
25 | - Menu: 0x01
26 |
27 | Unmapped Sym Combos:
28 | - Sym+2
29 | - Sym+3
30 | - Sym+6
31 | - Sym+Z
32 | - Sym+X
33 | - Sym+M
34 | - Sym+N
35 |
--------------------------------------------------------------------------------
/helpers/5ohBee.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pdxbadgers/5ohBEE-2019/f2a227239b3fc3d9156bea2e8e7e3a9396c39c47/helpers/5ohBee.tar.gz
--------------------------------------------------------------------------------
/helpers/99-USBasp.rules:
--------------------------------------------------------------------------------
1 | # USBasp - USB programmer for Atmel AVR controllers
2 | # Copy this file to /etc/udev/rules.d so
3 |
4 | # According to http://www.ladyada.net/make/usbtinyisp/avrdude.html
5 | # The udev examples given don't work on some systems as the SYSFS parameter is deprecated.
6 | # The following rule works on recent Ubuntu systems and should probably work on other newer Linux systems:
7 | SUBSYSTEM=="usb", ATTR{product}=="USBasp", ATTR{idProduct}=="05dc", ATTRS{idVendor}=="16c0", MODE="0666"
8 |
--------------------------------------------------------------------------------
/helpers/SmartResponseXE-lib.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pdxbadgers/5ohBEE-2019/f2a227239b3fc3d9156bea2e8e7e3a9396c39c47/helpers/SmartResponseXE-lib.zip
--------------------------------------------------------------------------------
/sketch/HugQuest.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include "RadioFunctions.h"
3 | #include
4 | #include
5 |
6 | char version[20]="HugQuest v1.5";
7 | char messages[6][26];
8 | char command[32] = "> \x00R05e\x00";
9 | char netbuff[26] = "";
10 | unsigned int curs = 2;
11 | int ncurs = 0;
12 | int row=0;
13 | uint8_t canaryOffset = 27;
14 |
15 | uint8_t rssi[32]; // yeah, what of it?
16 |
17 | unsigned long int time_loop=0;
18 | unsigned long int tokens=0;
19 |
20 | int infected=false;
21 | char outbuff[26];
22 |
23 | // Operation mode constants
24 | #define CONST_MODE_CONSOLE 0
25 | #define CONST_MODE_HEX 1
26 | #define CONST_MODE_RSSI 2
27 | #define CONST_MODE_CHAT 3
28 |
29 | // Special keyboard constants
30 | #define CONST_KEY_CLEAR 0xF8
31 | #define CONST_KEY_EXIT 0xF9
32 |
33 | // EEPROM constants
34 | #define CONST_MEM_NAME 0x0000000
35 | #define CONST_MEM_HUGS 0x0000100
36 | #define CONST_MEM_HACK 0x0000200
37 |
38 | struct global_t{
39 | int mode = CONST_MODE_CONSOLE;
40 | char command[26] = "> ";
41 | char canary[5] = "R05e";
42 | uint8_t name[256];
43 | uint16_t hexdumpaddr;
44 | uint32_t rfChannel;
45 | uint8_t rows; // screen rows
46 | uint8_t columns; // screen columns
47 | } global;
48 |
49 | void reset(){
50 | EEPROM.write(CONST_MEM_NAME,0xFF);
51 | getName();
52 | }
53 |
54 | void setup() {
55 | // put your setup code here, to run once:
56 | SRXEInit(0xe7, 0xd6, 0xa2); // initialize display
57 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0); // draw large black text at x=0,y=120, fg=3, bg=0
58 | global.rows = 6;
59 |
60 | for(int i = 0; i < global.rows; i++){
61 | strncpy(messages[i], " ",24);
62 | }
63 | resetInputBuffer();
64 | global.mode = CONST_MODE_CONSOLE;
65 | global.rfChannel = 11;
66 | rfBegin(global.rfChannel);
67 |
68 | // set up the watchdog timer
69 | wdt_enable(WDTO_8S);
70 |
71 | // set up the name in memory
72 | if(getName())
73 | {
74 |
75 | if(infected)
76 | {
77 | omghax();
78 | return;
79 | }
80 | snprintf(outbuff,24,"Welcome back %s!",global.name);
81 | submit(outbuff);
82 |
83 | snprintf(outbuff,24,"You have %lu HUG tokens.",tokens);
84 | submit(outbuff);
85 | submit("Press % or > to submit.");
86 | }
87 | }
88 |
89 | void clearScreen(){
90 | for(int i = 0; i < global.rows; i++){ submit(" "); }
91 | row = 0;
92 | }
93 |
94 | int getName()
95 | {
96 | // flash is totally empty, welcome the new user!
97 | if(EEPROM.read(CONST_MEM_NAME)==255)
98 | {
99 | clearScreen();
100 | submit(version);
101 | submit("What is your name?");
102 | submit("type 'set name '");
103 | submit("Press % or > to submit.");
104 |
105 | EEPROM.put(CONST_MEM_HUGS,(unsigned long)0);
106 | EEPROM.put(CONST_MEM_HACK,(int)0);
107 | return false;
108 | }
109 |
110 | // how many tokens we got?
111 | EEPROM.get(CONST_MEM_HUGS,tokens);
112 | EEPROM.get(CONST_MEM_HACK,infected);
113 |
114 | for(int i = 0; i < 4; i++)
115 | {
116 | char nameByte = EEPROM.read(CONST_MEM_NAME+i);
117 | //snprintf(outbuff,20,"%d %02x %03d : %c",i,nameByte,nameByte,nameByte);
118 | //submit(outbuff);
119 |
120 | if(nameByte >= 0x20 && nameByte <= 0x7A)
121 | {
122 | global.name[i] = nameByte;
123 | //break;
124 | }
125 | else
126 | {
127 | global.name[i] = 0x00;
128 | }
129 | }
130 | global.name[4]=0;
131 |
132 | return true;
133 | }
134 |
135 | int setName(char *name)
136 | {
137 | if(strlen(name)<=0||strlen(name)>3)
138 | {
139 | submit("NAME MUST BE 1-3 CHARS!");
140 | return false;
141 | }
142 |
143 | for(int i = 0; i < 4; i++)
144 | {
145 | EEPROM.write(CONST_MEM_NAME+i,name[i]);
146 | global.name[i]=name[i];
147 | if(name[i] == 0x00)
148 | {
149 | break;
150 | }
151 | }
152 |
153 | return true;
154 | }
155 |
156 | void resetInputBuffer()
157 | {
158 | memcpy(global.command, "> ",26);
159 | curs = 2;
160 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0);
161 | }
162 |
163 | void redraw()
164 | {
165 | SRXEWriteString(0,0 ," ", FONT_LARGE, 3, 0);
166 | SRXEWriteString(0,20 ," ", FONT_LARGE, 3, 0);
167 | SRXEWriteString(0,40 ," ", FONT_LARGE, 3, 0);
168 | SRXEWriteString(0,60 ," ", FONT_LARGE, 3, 0);
169 | SRXEWriteString(0,80 ," ", FONT_LARGE, 3, 0);
170 | SRXEWriteString(0,100," ", FONT_LARGE, 3, 0);
171 |
172 | SRXEWriteString(0,0 ,messages[(0+row)%global.rows], FONT_LARGE, 3, 0);
173 | SRXEWriteString(0,20 ,messages[(1+row)%global.rows], FONT_LARGE, 3, 0);
174 | SRXEWriteString(0,40 ,messages[(2+row)%global.rows], FONT_LARGE, 3, 0);
175 | SRXEWriteString(0,60 ,messages[(3+row)%global.rows], FONT_LARGE, 3, 0);
176 | SRXEWriteString(0,80 ,messages[(4+row)%global.rows], FONT_LARGE, 3, 0);
177 | SRXEWriteString(0,100,messages[(5+row)%global.rows], FONT_LARGE, 3, 0);
178 | }
179 |
180 | void handleInput(char* cmd)
181 | {
182 | if(!memcmp(cmd,"set",3))
183 | {
184 | char* item = strtok(cmd," ");
185 | if(item){
186 | item = strtok(NULL," ");
187 | if(item){
188 | if(!memcmp(item,"name",4))
189 | {
190 | item = strtok(NULL," ");
191 | if(setName(item))
192 | {
193 | snprintf(outbuff,24,"Hello '%s', welcome!",item);
194 | submit(outbuff);
195 | }
196 | //getName();
197 | }
198 | else if(!memcmp(item,"channel",7))
199 | {
200 | item = strtok(NULL," ");
201 | int newchan=atoi(item);
202 | if(newchan>=11 && newchan<=26)
203 | {
204 | global.rfChannel = newchan;
205 | rfBegin(global.rfChannel);
206 |
207 | snprintf(outbuff,24,"Radio on channel %d",global.rfChannel);
208 | submit(outbuff);
209 | }
210 | else
211 | {
212 | submit("Channel must be 11-26");
213 | }
214 | }
215 | else if(!memcmp(item,"tokens",6))
216 | {
217 | submit("No.");
218 | }
219 | else{
220 | submit("Set what? name channel");
221 | }
222 | }else{
223 | submit("Set what? name channel");
224 | }
225 | }
226 | }else if(!memcmp(cmd,"get",3)){
227 | char* item = strtok(cmd," ");
228 | if(item){
229 | item = strtok(NULL, " ");
230 | if(item){
231 | if(!memcmp(item,"name",4))
232 | {
233 | getName();
234 | submit((char*)global.name);
235 | }
236 | else if(!memcmp(item,"timer",5))
237 | {
238 | snprintf(outbuff,20,"Timer is at %lu",time_loop);
239 | submit(outbuff);
240 | }
241 | else if(!memcmp(item,"channel",7))
242 | {
243 | snprintf(outbuff,20,"Radio on channel %d",global.rfChannel);
244 | submit(outbuff);
245 | }
246 | else if(!memcmp(item,"tokens",6))
247 | {
248 | snprintf(outbuff,24,"You have %lu HUG tokens.",tokens);
249 | submit(outbuff);
250 | }
251 |
252 | else
253 | {
254 | submit("Get what?");
255 | submit("name channel tokens");
256 | }
257 | }else{
258 | submit("Get what?");
259 | submit("name channel tokens");
260 | }
261 | }
262 | }else if(!memcmp(cmd,"hex",3)){
263 | global.mode = CONST_MODE_HEX;
264 | global.hexdumpaddr = 0;
265 | // fill the first 6 lines of the screen
266 | while(global.hexdumpaddr < (6*4))
267 | {
268 | readHexLine(global.hexdumpaddr);
269 | global.hexdumpaddr += 4;
270 | }
271 | }
272 | else if(!memcmp(cmd,"rssi",4))
273 | {
274 | for(int i=0;i<30;++i)rssi[i]=0;
275 | global.mode = CONST_MODE_RSSI;
276 | SRXEPowerDown();
277 | SRXEPowerUp();
278 | }
279 | else if(!memcmp(cmd,"help",4))
280 | {
281 | submit("Basic commands:");
282 | submit(" get set hug mine rssi");
283 | if(tokens>=100)
284 | {
285 | submit(" also 'WANNAHUG'..");
286 | }
287 | }
288 | else if(!memcmp(cmd,"resetforreals",13)){reset();}
289 | else if(!memcmp(cmd,"reset",5)){submit("Try 'resetforreals'");}
290 | else if(!memcmp(cmd,"version",7)){submit(version);}
291 | else if(!memcmp(cmd,"ascii",5)){
292 | for(int x=0;x<256;++x)
293 | {
294 | snprintf(outbuff,20,"%02x %03d : %c",x,x,x);
295 | delay(1000);
296 | submit(outbuff);
297 | }
298 | }
299 | else if(!memcmp(cmd,"unlock",6))
300 | {
301 | if(tokens>=10)
302 | {
303 | tokens-=10;
304 | infected=false;
305 | EEPROM.put(CONST_MEM_HACK,infected);
306 | EEPROM.put(CONST_MEM_HUGS,tokens);
307 | submit("SYSTEM UNLOCKED");
308 | }
309 | else
310 | {
311 | submit("You need more HUGs");
312 | }
313 | }
314 | else if(!memcmp(cmd,"mine",4))
315 | {
316 | unsigned int mined = time_loop/10000;
317 |
318 | tokens = tokens+mined;
319 | EEPROM.put(CONST_MEM_HUGS,tokens);
320 |
321 | snprintf(outbuff,24,"Mined %u HUG tokens.",mined);
322 | submit(outbuff);
323 | }
324 | else if(!memcmp(cmd,"hug",3))
325 | {
326 | rfWrite('h');
327 | rfWrite('u');
328 | rfWrite('g');
329 | rfWrite('\n'); // write the last byte
330 | submit("Hug sent!!");
331 | if(tokens>0)
332 | {
333 | tokens=tokens-1;
334 | EEPROM.put(CONST_MEM_HUGS,tokens);
335 | }
336 | }
337 | else if(!memcmp(cmd,"WANNAHUG",8))
338 | {
339 | if(tokens<100)
340 | {
341 | submit("Who told you that cmd?");
342 | return;
343 | }
344 |
345 | rfWrite('h');
346 | rfWrite('a');
347 | rfWrite('x');
348 | rfWrite('\n'); // write the last byte
349 | submit("WANNAHUG sent!!");
350 | }
351 | // doesn't look like a command, so let's blast it to the chat!
352 | else
353 | {
354 | // make sure they have their name set
355 | if(!getName())return;
356 |
357 | char outputbuff[26];
358 |
359 | memset(outbuff,0,25);
360 | snprintf(outbuff,24,"%-3s:%s.",global.name,global.command+2);
361 |
362 | // transmit
363 | for(int i=0;i24)return false;
379 |
380 | memcpy(messages[row]," ",24);
381 | messages[row][24]=0;
382 | memcpy(messages[row],submission, 24);
383 | row = (row+1)%global.rows;
384 | redraw();
385 | }
386 |
387 | void checkCanary()
388 | {
389 | //if(memcmp(command+canaryOffset,"R05e",4) != 0) {
390 | if(memcmp(global.canary,"R05e",4) != 0) {
391 | for(int i = 0; i < 6; i++){ strcpy(messages[i],"OMGH@XXE!"); }
392 | //redraw();
393 | }
394 | }
395 |
396 | // readHexLine(uint32_t)
397 | // Read 4 bytes starting from the specified address and print it to the string formatted as:
398 | //
399 | void readHexLine(uint32_t addr)
400 | {
401 | byte foo[4];
402 | char bar[26];
403 | for(int j = 0; j < 4; j++){ foo[j] = EEPROM.read(addr+j); }
404 | snprintf(bar, 24, "%04x %02x%02x%02x%02x %c%c%c%c", addr, foo[0],foo[1],foo[2],foo[3], foo[0],foo[1],foo[2],foo[3]);
405 | bar[24] = 0x00;
406 | submit(bar);
407 | }
408 |
409 | void updateInputBuffer(byte k){
410 | if(k >= 0x20 && k <= 0x7A){
411 | //is it printable?
412 | if(curs <= 20)
413 | {
414 | global.command[curs]=k;
415 | curs++;
416 | }
417 |
418 | }else if(k == 0x08){
419 | // backspace?
420 | if(curs > 2){
421 | curs--;
422 | }
423 | global.command[curs] = 0x20; //space is your blank character
424 | }
425 |
426 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0);
427 | }
428 |
429 | // mode_hex_loop(byte)
430 | // This is the loop function for when the device is in hex-dump mode.
431 | // TODO:
432 | // - Enable reading backwards
433 | void mode_hex_loop(byte k){
434 | if(k){
435 | readHexLine(global.hexdumpaddr);
436 | if(global.hexdumpaddr <= (4*1024))
437 | {
438 | global.hexdumpaddr += 4;
439 | }
440 | }
441 | }
442 |
443 | // mode_console_loop(byte)
444 | // This is the loop function for when the device is in console (default) mode
445 | void mode_console_loop(byte r, byte k){
446 |
447 | if(r){
448 | if(r=='\n')
449 | {
450 | // HERE IS WHERE WE ACCEPT COMMANDS FROM THE NETWORK
451 | if(!memcmp(netbuff,"hug",3))
452 | {
453 | submit("OMG YOU'VE BEEN HUGGED!");
454 | tokens=tokens+1;
455 | EEPROM.put(CONST_MEM_HUGS,tokens);
456 | }
457 | else if(!memcmp(netbuff,"hax",3))
458 | {
459 | omghax();
460 | infected=true;
461 | EEPROM.put(CONST_MEM_HACK,infected);
462 | }
463 | else submit(netbuff);
464 | strncpy(netbuff," ",24);
465 | ncurs = 0;
466 | }
467 | else
468 | {
469 | netbuff[ncurs]=r;
470 | ncurs = (ncurs+1)%24;
471 | }
472 | }
473 |
474 |
475 | if(k){
476 | // submit on "return" (key right of 'Sym', box line box)
477 | if(k==0x0D || k==0x03)
478 | {
479 | if(curs>3)
480 | {
481 | if(infected==true)
482 | {
483 | // while infected, special case to unlock
484 | if(!memcmp(global.command+2,"unlock",6))
485 | {
486 | handleInput(global.command+2);
487 | resetInputBuffer();
488 | return;
489 | }
490 |
491 | omghax();
492 |
493 | resetInputBuffer();
494 | return;
495 | }
496 |
497 | handleInput(global.command+2);
498 | resetInputBuffer();
499 | }
500 | }else{
501 | //update input buffer
502 | updateInputBuffer(k);
503 | }
504 | }
505 | }
506 |
507 | void omghax()
508 | {
509 | clearScreen();
510 | submit("YOU HAVE BEEN INFECTED");
511 | submit("WITH ** WANNAHUG **");
512 | submit("YOU MUST PAY 10 HUG");
513 | snprintf(outbuff,24,"You have %lu HUG tokens.",tokens);
514 | submit(outbuff);
515 | submit("Type 'unlock' when ready");
516 | }
517 |
518 | // mode_rssi_loop()
519 | // This is a port of earlier RSSI code
520 | void mode_rssi_loop(){
521 |
522 | time_loop = 0;
523 |
524 | int x;
525 | int y;
526 |
527 | for(int i=11;i<=26;++i)
528 | {
529 | rfBegin(i); // change channel
530 | delay(100);
531 |
532 | y=(i-11)%8*17;
533 | if(i<19){ x = 0;}
534 | else { x = 201; }
535 |
536 | if(rfAvailable())
537 | {
538 | while(0>rfRead()){} // burn through the reads
539 | rssi[i]+=rssiRaw;
540 | }
541 |
542 | snprintf(outbuff,24,"Ch %d : %d",i,rssi[i]);
543 | SRXEWriteString(x,y ,outbuff, FONT_LARGE, 3, 0);
544 |
545 | }
546 | // point out the "back" button
547 | SRXEWriteString(360,120 ,"back", FONT_SMALL, 3, 0);
548 | }
549 |
550 | void loop() {
551 |
552 | // reset the watchdog
553 | wdt_reset();
554 |
555 | // increment the sleep timer
556 | time_loop+=1;
557 | if(time_loop>50000) // maybe a minute or so?
558 | {
559 | time_loop=0;
560 | wdt_disable();
561 | SRXESleep();
562 | wdt_enable(WDTO_8S);
563 | redraw();
564 | resetInputBuffer();
565 | }
566 |
567 |
568 | // if we hear something on the radio, build up the net buffer
569 | byte r = NULL;
570 | if (rfAvailable())
571 | {
572 | r = rfRead();
573 | }
574 |
575 | // otherwise, just take data from the keyboard
576 | byte k = SRXEGetKey();
577 | if(k){
578 | if(k == CONST_KEY_EXIT){
579 | SRXEPowerDown();
580 | SRXEPowerUp();
581 | global.mode = CONST_MODE_CONSOLE;
582 | }else if(k == CONST_KEY_CLEAR){
583 | clearScreen();
584 | k = NULL; // prevents subprograms from operating on this input
585 | }
586 | }
587 |
588 | // figure out our mode
589 | switch(global.mode)
590 | {
591 | case CONST_MODE_HEX:
592 | mode_hex_loop(k);
593 | break;
594 | case CONST_MODE_RSSI:
595 | mode_rssi_loop();
596 | break;
597 | case CONST_MODE_CONSOLE:
598 | default:
599 | mode_console_loop(r,k);
600 | }
601 | //redraw();
602 | }
603 |
--------------------------------------------------------------------------------
/sketch/RadioFunctions.h:
--------------------------------------------------------------------------------
1 | /* RadioFunctions.h
2 | A handful of sending and receiving functions for the
3 | ATmega128RFA1.
4 | by: Jim Lindblom
5 | SparkFun Electronics
6 | date: July 8, 2013
7 | license: Beerware. Use, distribut, and modify this code freely
8 | however you please. If you find it useful, you can buy me a beer
9 | some day.
10 |
11 | Functions in here:
12 | rfBegin(uint8_t channel) - Initializes the radio on a channel
13 | between 11 and 26.
14 | rfWrite(uint8_t b) - Sends a byte over the radio.
15 | rfPrint(String toPrint) - Sends a string over the radio.
16 | int rfAvailable() - Returns number of characters currently
17 | in receive buffer, ready to be read.
18 | char rfRead() - Reads a character out of the buffer.
19 |
20 | Interrupt Sub-Routines (ISRs) in here:
21 | TRX24_TX_END_vect - End of radio transfer.
22 | TRX24_RX_START_vect - Beginning of radio receive.
23 | TRX24_RX_END_vect - End of radio receive. Characters are
24 | collected into a buffer here.
25 | */
26 |
27 | #include // Required for digitalWrites, etc.
28 |
29 | // Board pin definitions.
30 | const int RX_LED = 34; // B6 - RF RX LED
31 | const int TX_LED = 35; // B7 - RF TX LED
32 | uint8_t rssiRaw; // Global variable shared between RX ISRs
33 |
34 | // A buffer to maintain data being received by radio.
35 | const int RF_BUFFER_SIZE = 127;
36 | struct ringBuffer
37 | {
38 | unsigned char buffer[RF_BUFFER_SIZE];
39 | volatile unsigned int head;
40 | volatile unsigned int tail;
41 | } radioRXBuffer;
42 |
43 |
44 | // Initialize the RFA1's low-power 2.4GHz transciever.
45 | // Sets up the state machine, and gets the radio into
46 | // the RX_ON state. Interrupts are enabled for RX
47 | // begin and end, as well as TX end.
48 | uint8_t rfBegin(uint8_t channel)
49 | {
50 | for (int i=0; i<128; i++)
51 | {
52 | radioRXBuffer.buffer[i] = 0;
53 | }
54 | radioRXBuffer.tail = 0;
55 | radioRXBuffer.head = 0;
56 |
57 | // Setup RX/TX LEDs: These are pins B6/34 (RX) and B7/35 (TX).
58 | pinMode(RX_LED, OUTPUT);
59 | digitalWrite(RX_LED, LOW);
60 | pinMode(TX_LED, OUTPUT);
61 | digitalWrite(TX_LED, LOW);
62 |
63 | // Transceiver Pin Register -- TRXPR.
64 | // This register can be used to reset the transceiver, without
65 | // resetting the MCU.
66 | TRXPR |= (1< 26)) channel = 11;
98 | PHY_CC_CCA = (PHY_CC_CCA & 0xE0) | channel; // Set the channel to 11
99 |
100 | // Finally, we'll enter into the RX_ON state. Now waiting for radio RX's, unless
101 | // we go into a transmitting state.
102 | TRX_STATE = (TRX_STATE & 0xE0) | RX_ON; // Default to receiver
103 |
104 | rssiRaw=0;
105 | return 1;
106 | }
107 |
108 | // This function sends a string of characters out of the radio.
109 | // Given a string, it'll format a frame, and send it out.
110 | void rfPrint(String toPrint)
111 | {
112 | uint8_t frame[127]; // We'll need to turn the string into an arry
113 | int length = toPrint.length(); // Get the length of the string
114 | for (int i=0; i
2 | #include "RadioFunctions.h"
3 |
4 | char messages[6][24];
5 | char command[24] = "> ";
6 | char netbuff[24] = "";
7 | unsigned int curs = 2;
8 | int ncurs = 0;
9 | int row=0;
10 |
11 | void setup() {
12 | // put your setup code here, to run once:
13 |
14 | SRXEInit(0xe7, 0xd6, 0xa2); // initialize display
15 | SRXEWriteString(0,120,command, FONT_LARGE, 3, 0); // draw large black text at x=0,y=120, fg=3, bg=0
16 |
17 | strcpy(messages[0], "");
18 | strcpy(messages[1], "");
19 | strcpy(messages[2], "");
20 | strcpy(messages[3], "");
21 | strcpy(messages[4], "");
22 | strcpy(messages[5], "");
23 |
24 | rfBegin(11);
25 |
26 | }
27 |
28 | void redraw()
29 | {
30 | SRXEWriteString(0,0 ,messages[(0+row)%6], FONT_LARGE, 3, 0);
31 | SRXEWriteString(0,20 ,messages[(1+row)%6], FONT_LARGE, 3, 0);
32 | SRXEWriteString(0,40 ,messages[(2+row)%6], FONT_LARGE, 3, 0);
33 | SRXEWriteString(0,60 ,messages[(3+row)%6], FONT_LARGE, 3, 0);
34 | SRXEWriteString(0,80 ,messages[(4+row)%6], FONT_LARGE, 3, 0);
35 | SRXEWriteString(0,100,messages[(5+row)%6], FONT_LARGE, 3, 0);
36 |
37 | SRXEWriteString(0,120,command, FONT_LARGE, 3, 0);
38 | }
39 |
40 | void submit(char* submission)
41 | {
42 | strcpy(messages[row]," ");
43 | strcpy(messages[row],submission);
44 | row = (row+1)%6;
45 |
46 | redraw();
47 | }
48 |
49 | void loop() {
50 |
51 | // if we hear something on the radio, build up the net buffer
52 | if (rfAvailable())
53 | {
54 | byte n = rfRead();
55 | if(n)
56 | {
57 | if(n==3)
58 | {
59 | submit(netbuff);
60 | strcpy(netbuff, " ");
61 | ncurs = 0;
62 | }
63 | else
64 | {
65 | netbuff[ncurs]=n;
66 | ncurs = (ncurs+1)%24;
67 | }
68 | }
69 | }
70 |
71 | // otherwise, just take data from the keyboard
72 | byte k = SRXEGetKey();
73 | if(k)
74 | {
75 | // submit on "return" (key right of 'Sym', box line box)
76 | if(k==0x0D)
77 | {
78 | if(curs>3)
79 | {
80 | submit(command+2);
81 |
82 | // transmit
83 | for(int i=2;i ");
91 | curs = 2;
92 | }
93 | else
94 | {
95 | //sprintf(command,"> %d",k);
96 | //sprintf(command,"> %02X",k);
97 |
98 | if(k >= 0x20 && k <= 0x7A){
99 | //is it printable?
100 | command[curs]=k;
101 | //curs = (curs+1)%24;
102 | if(curs < 22) {
103 | curs++;
104 | }
105 | }else if(k == 0x08){
106 | // backspace?
107 | if(curs > 2){
108 | curs--;
109 | }
110 | command[curs] = 0x20; //space is your blank character
111 | }
112 | }
113 | }
114 | redraw();
115 | }
116 |
--------------------------------------------------------------------------------
/sketch/hexdump.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | char messages[6][24];
4 | char command[24] = "> ";
5 | uint32_t addr = 0x00;
6 | int row = 0;
7 |
8 | void redraw()
9 | {
10 | SRXEWriteString(0,0 ,messages[(0+row)%6], FONT_LARGE, 3, 0);
11 | SRXEWriteString(0,20 ,messages[(1+row)%6], FONT_LARGE, 3, 0);
12 | SRXEWriteString(0,40 ,messages[(2+row)%6], FONT_LARGE, 3, 0);
13 | SRXEWriteString(0,60 ,messages[(3+row)%6], FONT_LARGE, 3, 0);
14 | SRXEWriteString(0,80 ,messages[(4+row)%6], FONT_LARGE, 3, 0);
15 | SRXEWriteString(0,100,messages[(5+row)%6], FONT_LARGE, 3, 0);
16 | SRXEWriteString(0,120,command, FONT_LARGE, 3, 0);
17 | }
18 |
19 | void readLine(uint32_t addr)
20 | {
21 | //uint8_t foo[5];
22 | byte foo[4];
23 | //memset(foo,0x00,5);
24 | //char foo;
25 | snprintf(command, 24, "> 0x%x", addr);
26 | for(int i = 0; i < 5; i++){ strcpy(messages[i],messages[i+1]); }
27 | for(int j = 0; j < 4; j++){ foo[j] = EEPROM.read(addr+j); }
28 | //foo = EEPROM.read(addr);
29 | snprintf(messages[5], 24, "%04x %02x%02x%02x%02x %c%c%c%c", addr, foo[0],foo[1],foo[2],foo[3], foo[0],foo[1],foo[2],foo[3]);
30 | redraw();
31 | }
32 |
33 | void setup() {
34 | // put your setup code here, to run once:
35 | SRXEInit(0xe7, 0xd6, 0xa2); // initialize display
36 | SRXEWriteString(0,120,command, FONT_LARGE, 3, 0); // draw large black text at x=0,y=120, fg=3, bg=0
37 | for(int i = 0; i < 4*8; i++)
38 | {
39 | /*EEPROM.write(n*i+0,0xde);
40 | EEPROM.write(n*i+1,0xad);
41 | EEPROM.write(n*i+2,0xbe);
42 | EEPROM.write(n*i+3,0xef);*/
43 | EEPROM.write(i,'A');
44 | //delay(500);
45 | }
46 | while(addr < (6*4))
47 | {
48 | readLine(addr);
49 | addr += 4;
50 | }
51 | }
52 |
53 | void loop() {
54 | // put your main code here, to run repeatedly:
55 | byte k = SRXEGetKey();
56 | if(k)
57 | {
58 | readLine(addr);
59 | if(addr <= (4*1024))
60 | {
61 | addr += 4;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/sketch/multi-chat.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include "RadioFunctions.h"
3 | #include
4 |
5 | char messages[6][24];
6 | char command[32] = "> \x00R05e\x00";
7 | char netbuff[24] = "";
8 | unsigned int curs = 2;
9 | int ncurs = 0;
10 | int row=0;
11 | uint8_t canaryOffset = 27;
12 |
13 | // Operation mode constants
14 | #define CONST_MODE_CONSOLE 0
15 | #define CONST_MODE_HEX 1
16 | #define CONST_MODE_RSSI 2
17 | #define CONST_MODE_CHAT 3
18 |
19 | // Special keyboard constants
20 | #define CONST_KEY_CLEAR 0xF8
21 | #define CONST_KEY_EXIT 0xF9
22 |
23 | // EEPROM constants
24 | #define CONST_MEM_NAME 0x00000000
25 |
26 | struct global_t{
27 | int mode = CONST_MODE_CONSOLE;
28 | char command[26] = "> ";
29 | char canary[5] = "R05e";
30 | uint8_t name[256];
31 | uint16_t hexdumpaddr;
32 | uint32_t rfChannel;
33 | uint8_t rows; // screen rows
34 | uint8_t columns; // screen columns
35 | } global;
36 |
37 | void reset(){
38 | //EEPROM.write(CONST_MEM_NAME,0xFF);
39 | setName("");
40 | readName();
41 | }
42 |
43 | void setup() {
44 | // put your setup code here, to run once:
45 | SRXEInit(0xe7, 0xd6, 0xa2); // initialize display
46 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0); // draw large black text at x=0,y=120, fg=3, bg=0
47 | global.rows = 6;
48 |
49 | for(int i = 0; i < global.rows; i++){
50 | strcpy(messages[i], "");
51 | }
52 | resetInputBuffer();
53 | global.mode = CONST_MODE_CONSOLE;
54 | global.rfChannel = 11;
55 |
56 | rfBegin(global.rfChannel);
57 |
58 | // Device is ready to go; check if this was a reboot or new use
59 | strcpy((char*)global.name, "uninitialized");
60 | //setName("Bob"); // uncomment this for testing
61 | //reset();
62 | readName();
63 | if(global.name[0]==NULL){
64 | // device is unininitialized
65 | submit("Hello!");
66 | submit("What is your name?");
67 | // TODO: fall into limited mode waiting for username and write to EEPROM
68 | }
69 | }
70 |
71 | void clearScreen(){
72 | for(int i = 0; i < global.rows; i++){ submit(" "); }
73 | row = 0;
74 | }
75 |
76 | void readName()
77 | {
78 | for(int i = 0; i < 256; i++)
79 | {
80 | char nameByte = EEPROM.read(CONST_MEM_NAME+i);
81 | if(nameByte == 0xFF || nameByte == 0x00){
82 | global.name[i] = 0x00;
83 | break;
84 | }
85 | global.name[i] = nameByte;
86 | }
87 | }
88 |
89 | void setName(char *name)
90 | {
91 | for(int i = 0; i < 256; i++)
92 | {
93 | if(name[i] == 0x20)
94 | {
95 | name[i] = 0x00; // no spaces
96 | }
97 |
98 | EEPROM.write(CONST_MEM_NAME+i,name[i]);
99 | if(name[i] == 0x00)
100 | {
101 | break;
102 | }
103 | }
104 | }
105 |
106 | void resetInputBuffer()
107 | {
108 | memcpy(global.command, "> ",26);
109 | curs = 2;
110 | }
111 |
112 | void redraw()
113 | {
114 | SRXEWriteString(0,0 ,messages[(0+row)%global.rows], FONT_LARGE, 3, 0);
115 | SRXEWriteString(0,20 ,messages[(1+row)%global.rows], FONT_LARGE, 3, 0);
116 | SRXEWriteString(0,40 ,messages[(2+row)%global.rows], FONT_LARGE, 3, 0);
117 | SRXEWriteString(0,60 ,messages[(3+row)%global.rows], FONT_LARGE, 3, 0);
118 | SRXEWriteString(0,80 ,messages[(4+row)%global.rows], FONT_LARGE, 3, 0);
119 | SRXEWriteString(0,100,messages[(5+row)%global.rows], FONT_LARGE, 3, 0);
120 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0);
121 | }
122 |
123 | void handleInput(char* cmd)
124 | {
125 | if(!memcmp(cmd,"set",3))
126 | {
127 | char* item = strtok(cmd," ");
128 | if(item){
129 | item = strtok(NULL," ");
130 | if(item){
131 | if(!memcmp(item,"name",4)){
132 | item = strtok(NULL," ");
133 | setName(item);
134 | readName();
135 | }else{
136 | submit("Item not recognized");
137 | }
138 | }else{
139 | submit("Malformed");
140 | }
141 | }
142 | }else if(!memcmp(cmd,"read",4)){
143 | char* item = strtok(cmd," ");
144 | if(item){
145 | item = strtok(NULL, " ");
146 | if(item){
147 | if(!memcmp(item,"name",4)){
148 | submit((char*)global.name);
149 | }else{
150 | submit("Item not recognized");
151 | }
152 | }else{
153 | submit("Malformed");
154 | }
155 | }
156 | }else if(!memcmp(cmd,"hex",3)){
157 | global.mode = CONST_MODE_HEX;
158 | global.hexdumpaddr = 0;
159 | // fill the first 6 lines of the screen
160 | while(global.hexdumpaddr < (6*4))
161 | {
162 | readHexLine(global.hexdumpaddr);
163 | global.hexdumpaddr += 4;
164 | }
165 | }else if(!memcmp(cmd,"rssi",4)){
166 | global.mode = CONST_MODE_RSSI;
167 | }else if(!memcmp(cmd,"chat",4)){
168 | global.mode = CONST_MODE_CHAT;
169 | global.rfChannel = 11;
170 | rfBegin(global.rfChannel);
171 | }else if(!memcmp(cmd,"help",4)){
172 | submit("there is no help");
173 | }else{
174 | submit(cmd);
175 | }
176 | }
177 |
178 | // submit(char*)
179 | // Prints the designated string to the screen buffer and forces a redraw
180 | void submit(char* submission)
181 | {
182 | strcpy(messages[row]," ");
183 | strncpy(messages[row],submission, 24);
184 | row = (row+1)%global.rows;
185 | //checkCanary();
186 | resetInputBuffer();
187 | redraw();
188 | }
189 |
190 | void checkCanary()
191 | {
192 | //if(memcmp(command+canaryOffset,"R05e",4) != 0) {
193 | if(memcmp(global.canary,"R05e",4) != 0) {
194 | for(int i = 0; i < 6; i++){ strcpy(messages[i],"OMGH@XXE!"); }
195 | //redraw();
196 | }
197 | }
198 |
199 | // readHexLine(uint32_t)
200 | // Read 4 bytes starting from the specified address and print it to the string formatted as:
201 | //
202 | void readHexLine(uint32_t addr)
203 | {
204 | byte foo[4];
205 | char bar[26];
206 | for(int j = 0; j < 4; j++){ foo[j] = EEPROM.read(addr+j); }
207 | snprintf(bar, 24, "%04x %02x%02x%02x%02x %c%c%c%c", addr, foo[0],foo[1],foo[2],foo[3], foo[0],foo[1],foo[2],foo[3]);
208 | bar[24] = 0x00;
209 | submit(bar);
210 | }
211 |
212 | void updateInputBuffer(byte k){
213 | if(k >= 0x20 && k <= 0x7A){
214 | //is it printable?
215 | global.command[curs]=k;
216 | //curs = (curs+1)%24;
217 | if(curs < 31){ //22) {
218 | curs++;
219 | }
220 | }else if(k == 0x08){
221 | // backspace?
222 | if(curs > 2){
223 | curs--;
224 | }
225 | global.command[curs] = 0x20; //space is your blank character
226 | }
227 | }
228 |
229 | // mode_hex_loop(byte)
230 | // This is the loop function for when the device is in hex-dump mode.
231 | // TODO:
232 | // - Enable reading backwards
233 | void mode_hex_loop(byte k){
234 | if(k){
235 | readHexLine(global.hexdumpaddr);
236 | if(global.hexdumpaddr <= (4*1024))
237 | {
238 | global.hexdumpaddr += 4;
239 | }
240 | }
241 | }
242 |
243 | // mode_console_loop(byte)
244 | // This is the loop function for when the device is in console (default) mode
245 | void mode_console_loop(byte k){
246 | if(k){
247 | // submit on "return" (key right of 'Sym', box line box)
248 | if(k==0x0D)
249 | {
250 | if(curs>3)
251 | {
252 | handleInput(global.command+2);
253 | resetInputBuffer();
254 | }
255 | }else{
256 | //update input buffer
257 | updateInputBuffer(k);
258 | }
259 | }
260 | }
261 |
262 | // mode_chat_loop(byte,byte)
263 | // First byte is input from the radio, second byte is from the keyboard. Update the screen accordingly.
264 | void mode_chat_loop(byte r,byte k){
265 | if(r){
266 | if(r==3)
267 | {
268 | submit(netbuff);
269 | strcpy(netbuff, " ");
270 | ncurs = 0;
271 | }
272 | else
273 | {
274 | netbuff[ncurs]=r;
275 | ncurs = (ncurs+1)%24;
276 | }
277 | }
278 |
279 | if(k)
280 | {
281 | // submit on "return" (key right of 'Sym', box line box)
282 | if(k==0x0D)
283 | {
284 | if(curs>2)
285 | {
286 | // transmit
287 | for(int i=2;irfRead()){} // burn through the reads
322 | }
323 | snprintf(buf,24,"Ch %d: %d ",i,rssiRaw);
324 |
325 | global.rfChannel++;
326 | i = global.rfChannel;
327 |
328 | rfBegin(i);
329 | delay(100);
330 |
331 | y=(i-11)%8*17;
332 | if(i<19){ x = 0;}
333 | else { x = 201; }
334 |
335 | if(rfAvailable())
336 | {
337 | while(0>rfRead()){} // burn through the reads
338 | }
339 | snprintf(buf2,12,"Ch %d: %d",i,rssiRaw);
340 | strncat(buf,buf2,24);
341 | submit(buf);
342 |
343 | global.rfChannel++;
344 | if(global.rfChannel > 26){
345 | global.rfChannel = 11;
346 | }
347 | }
348 |
349 | void loop() {
350 | // if we hear something on the radio, build up the net buffer
351 | byte n = NULL;
352 | if (rfAvailable())
353 | {
354 | n = rfRead();
355 | }
356 |
357 | // otherwise, just take data from the keyboard
358 | byte k = SRXEGetKey();
359 | if(k){
360 | if(k == CONST_KEY_EXIT){
361 | global.mode = CONST_MODE_CONSOLE;
362 | }else if(k == CONST_KEY_CLEAR){
363 | clearScreen();
364 | k = NULL; // prevents subprograms from operating on this input
365 | }
366 | }
367 |
368 | // figure out our mode
369 | switch(global.mode)
370 | {
371 | case CONST_MODE_HEX:
372 | mode_hex_loop(k);
373 | break;
374 | case CONST_MODE_RSSI:
375 | mode_rssi_loop();
376 | break;
377 | case CONST_MODE_CHAT:
378 | mode_chat_loop(n,k);
379 | break;
380 | case CONST_MODE_CONSOLE:
381 | default:
382 | mode_console_loop(k);
383 | }
384 | redraw();
385 | }
386 |
--------------------------------------------------------------------------------
/sketch/multi-mode.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include "RadioFunctions.h"
3 | #include
4 |
5 | char messages[6][24];
6 | char command[32] = "> \x00R05e\x00";
7 | char netbuff[24] = "";
8 | unsigned int curs = 2;
9 | int ncurs = 0;
10 | int row=0;
11 | uint8_t canaryOffset = 27;
12 |
13 | // Operation mode constants
14 | #define CONST_MODE_CHAT 0
15 | #define CONST_MODE_HEX 1
16 | #define CONST_MODE_RSSI 2
17 |
18 | // Special keyboard constants
19 | #define CONST_KEY_CLEAR 0xF8
20 | #define CONST_KEY_EXIT 0xF9
21 |
22 | // EEPROM constants
23 | #define CONST_MEM_NAME 0x00000000
24 |
25 | struct global_t{
26 | int mode = CONST_MODE_CHAT;
27 | char command[26] = "> ";
28 | char canary[5] = "R05e";
29 | uint8_t name[256];
30 | uint16_t hexdumpaddr;
31 | uint32_t rfChannel;
32 | } global;
33 |
34 | void reset(){
35 | //EEPROM.write(CONST_MEM_NAME,0xFF);
36 | setName("");
37 | readName();
38 | }
39 |
40 | void setup() {
41 | // put your setup code here, to run once:
42 | SRXEInit(0xe7, 0xd6, 0xa2); // initialize display
43 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0); // draw large black text at x=0,y=120, fg=3, bg=0
44 | strcpy(messages[0], "");
45 | strcpy(messages[1], "");
46 | strcpy(messages[2], "");
47 | strcpy(messages[3], "");
48 | strcpy(messages[4], "");
49 | strcpy(messages[5], "");
50 | resetInputBuffer();
51 | global.mode = CONST_MODE_CHAT;
52 | global.rfChannel = 11;
53 | rfBegin(global.rfChannel);
54 |
55 | // Device is ready to go; check if this was a reboot or new use
56 | strcpy((char*)global.name, "uninitialized");
57 | //setName("Bob"); // uncomment this for testing
58 | //reset();
59 | readName();
60 | if(global.name[0]==NULL){
61 | // device is unininitialized
62 | submit("Hello!");
63 | submit("What is your name?");
64 | // TODO: fall into limited mode waiting for username and write to EEPROM
65 | }
66 | }
67 |
68 | void clearScreen(){
69 | for(int i = 0; i < 6; i++){ submit(" "); }
70 | row = 0;
71 | }
72 |
73 | void readName()
74 | {
75 | for(int i = 0; i < 256; i++)
76 | {
77 | char nameByte = EEPROM.read(CONST_MEM_NAME+i);
78 | if(nameByte == 0xFF || nameByte == 0x00){
79 | global.name[i] = 0x00;
80 | break;
81 | }
82 | global.name[i] = nameByte;
83 | }
84 | }
85 |
86 | void setName(char *name)
87 | {
88 | for(int i = 0; i < 256; i++)
89 | {
90 | if(name[i] == 0x20)
91 | {
92 | name[i] = 0x00; // no spaces
93 | }
94 |
95 | EEPROM.write(CONST_MEM_NAME+i,name[i]);
96 | if(name[i] == 0x00)
97 | {
98 | break;
99 | }
100 | }
101 | }
102 |
103 | void resetInputBuffer()
104 | {
105 | /*memcpy(command,">",1);
106 | memcpy(command+1," ",30);
107 | memcpy(command+27,"R05e",4);
108 | memcpy(command+31,0x00,1);
109 | memcpy(command+25,0x00,1);*/
110 |
111 | //memcpy(command, "> \x00R05e\x00",26);
112 | memcpy(global.command, "> ",26);
113 | }
114 |
115 | void redraw()
116 | {
117 | SRXEWriteString(0,0 ,messages[(0+row)%6], FONT_LARGE, 3, 0);
118 | SRXEWriteString(0,20 ,messages[(1+row)%6], FONT_LARGE, 3, 0);
119 | SRXEWriteString(0,40 ,messages[(2+row)%6], FONT_LARGE, 3, 0);
120 | SRXEWriteString(0,60 ,messages[(3+row)%6], FONT_LARGE, 3, 0);
121 | SRXEWriteString(0,80 ,messages[(4+row)%6], FONT_LARGE, 3, 0);
122 | SRXEWriteString(0,100,messages[(5+row)%6], FONT_LARGE, 3, 0);
123 |
124 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0);
125 | }
126 |
127 | void handleInput(char* cmd)
128 | {
129 | if(!memcmp(cmd,"set",3))
130 | {
131 | char* item = strtok(cmd," ");
132 | if(item){
133 | item = strtok(NULL," ");
134 | if(item){
135 | if(!memcmp(item,"name",4)){
136 | item = strtok(NULL," ");
137 | setName(item);
138 | readName();
139 | }else{
140 | submit("Item not recognized");
141 | }
142 | }else{
143 | submit("Malformed");
144 | }
145 | }
146 | }else if(!memcmp(cmd,"read",4)){
147 | char* item = strtok(cmd," ");
148 | if(item){
149 | item = strtok(NULL, " ");
150 | if(item){
151 | if(!memcmp(item,"name",4)){
152 | submit((char*)global.name);
153 | }else{
154 | submit("Item not recognized");
155 | }
156 | }else{
157 | submit("Malformed");
158 | }
159 | }
160 | }else if(!memcmp(cmd,"hex",3)){
161 | global.mode = CONST_MODE_HEX;
162 | global.hexdumpaddr = 0;
163 | // fill the first 6 lines of the screen
164 | while(global.hexdumpaddr < (6*4))
165 | {
166 | readHexLine(global.hexdumpaddr);
167 | global.hexdumpaddr += 4;
168 | }
169 | }else if(!memcmp(cmd,"rssi",4)){
170 | global.mode = CONST_MODE_RSSI;
171 | }else if(!memcmp(cmd,"help",4)){
172 | submit("there is no help");
173 | }else{
174 | submit(cmd);
175 | }
176 | }
177 |
178 | // submit(char*)
179 | // Prints the designated string to the screen buffer and forces a readraw
180 | void submit(char* submission)
181 | {
182 | strcpy(messages[row]," ");
183 | strncpy(messages[row],submission, 24);
184 | row = (row+1)%6;
185 | //checkCanary();
186 | resetInputBuffer();
187 | redraw();
188 | }
189 |
190 | void checkCanary()
191 | {
192 | //if(memcmp(command+canaryOffset,"R05e",4) != 0) {
193 | if(memcmp(global.canary,"R05e",4) != 0) {
194 | for(int i = 0; i < 6; i++){ strcpy(messages[i],"OMGH@XXE!"); }
195 | //redraw();
196 | }
197 | }
198 |
199 | // readHexLine(uint32_t)
200 | // Read 4 bytes starting from the specified address and print it to the string formatted as:
201 | //
202 | void readHexLine(uint32_t addr)
203 | {
204 | byte foo[4];
205 | char bar[26];
206 | for(int j = 0; j < 4; j++){ foo[j] = EEPROM.read(addr+j); }
207 | snprintf(bar, 24, "%04x %02x%02x%02x%02x %c%c%c%c", addr, foo[0],foo[1],foo[2],foo[3], foo[0],foo[1],foo[2],foo[3]);
208 | bar[24] = 0x00;
209 | submit(bar);
210 | }
211 |
212 | // mode_hex_loop(byte)
213 | // This is the loop function for when the device is in hex-dump mode.
214 | // TODO:
215 | // - Enable reading backwards
216 | void mode_hex_loop(byte k){
217 | if(k){
218 | readHexLine(global.hexdumpaddr);
219 | if(global.hexdumpaddr <= (4*1024))
220 | {
221 | global.hexdumpaddr += 4;
222 | }
223 | }
224 | }
225 |
226 | // mode_chat_loop(byte)
227 | // This is the loop function for when the device is in chat (default) mode
228 | void mode_chat_loop(byte k){
229 | if(k)
230 | {
231 | // submit on "return" (key right of 'Sym', box line box)
232 | if(k==0x0D)
233 | {
234 | if(curs>3)
235 | {
236 | //submit(global.command+2);
237 | handleInput(global.command+2);
238 |
239 | // transmit
240 | for(int i=2;i ",26);//canaryOffset-1);
251 | curs = 2;
252 | }
253 | else
254 | {
255 |
256 | if(k >= 0x20 && k <= 0x7A){
257 | //is it printable?
258 | global.command[curs]=k;
259 | //curs = (curs+1)%24;
260 | if(curs < 31){ //22) {
261 | curs++;
262 | }
263 | }else if(k == 0x08){
264 | // backspace?
265 | if(curs > 2){
266 | curs--;
267 | }
268 | global.command[curs] = 0x20; //space is your blank character
269 | }
270 | }
271 | }
272 | }
273 |
274 | // mode_rssi_loop()
275 | // This is a port of earlier RSSI code
276 | void mode_rssi_loop(){
277 | int x;
278 | int y;
279 | char buf[24];
280 | char buf2[12];
281 | int i = global.rfChannel;
282 |
283 | rfBegin(i);
284 | delay(100);
285 |
286 | y=(i-11)%8*17;
287 | if(i<19){ x = 0;}
288 | else { x = 201; }
289 |
290 | if(rfAvailable())
291 | {
292 | while(0>rfRead()){} // burn through the reads
293 | //snprintf(buf,24,"Ch %d : %d ",i,rssiRaw);
294 | //submit(buf);
295 | }
296 | //else
297 | //{
298 | snprintf(buf,24,"Ch %d: %d ",i,rssiRaw);
299 | // submit(buf);
300 | //}
301 |
302 | global.rfChannel++;
303 | i = global.rfChannel;
304 |
305 | rfBegin(i);
306 | delay(100);
307 |
308 | y=(i-11)%8*17;
309 | if(i<19){ x = 0;}
310 | else { x = 201; }
311 |
312 | if(rfAvailable())
313 | {
314 | while(0>rfRead()){} // burn through the reads
315 | }
316 | snprintf(buf2,12,"Ch %d: %d",i,rssiRaw);
317 | strncat(buf,buf2,24);
318 | submit(buf);
319 |
320 | global.rfChannel++;
321 | if(global.rfChannel > 26){
322 | global.rfChannel = 11;
323 | }
324 | }
325 |
326 | void loop() {
327 | // if we hear something on the radio, build up the net buffer
328 | if (rfAvailable())
329 | {
330 | byte n = rfRead();
331 | if(n)
332 | {
333 | if(n==3)
334 | {
335 | submit(netbuff);
336 | strcpy(netbuff, " ");
337 | ncurs = 0;
338 | }
339 | else
340 | {
341 | netbuff[ncurs]=n;
342 | ncurs = (ncurs+1)%24;
343 | }
344 | }
345 | }
346 |
347 | // otherwise, just take data from the keyboard
348 | byte k = SRXEGetKey();
349 | if(k){
350 | if(k == CONST_KEY_EXIT){
351 | global.mode = CONST_MODE_CHAT;
352 | }else if(k == CONST_KEY_CLEAR){
353 | clearScreen();
354 | k = NULL; // prevents subprograms from operating on this input
355 | }
356 | }
357 |
358 | // figure out our mode
359 | switch(global.mode)
360 | {
361 | case CONST_MODE_HEX:
362 | mode_hex_loop(k);
363 | break;
364 | case CONST_MODE_RSSI:
365 | mode_rssi_loop();
366 | break;
367 | case CONST_MODE_CHAT:
368 | default:
369 | mode_chat_loop(k);
370 | }
371 | redraw();
372 | }
373 |
--------------------------------------------------------------------------------
/sketch/quest.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include "RadioFunctions.h"
3 | #include
4 |
5 | char messages[6][24];
6 | char command[32] = "> \x00R05e\x00";
7 | char netbuff[24] = "";
8 | unsigned int curs = 2;
9 | int ncurs = 0;
10 | int row=0;
11 | uint8_t canaryOffset = 27;
12 |
13 | #define NAME_ADDR 0x00000000
14 |
15 | struct global_t{
16 | char command[26] = "> ";
17 | char canary[5] = "R05e";
18 | uint8_t name[256];
19 | } global;
20 |
21 | void setup() {
22 | // put your setup code here, to run once:
23 |
24 | SRXEInit(0xe7, 0xd6, 0xa2); // initialize display
25 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0); // draw large black text at x=0,y=120, fg=3, bg=0
26 | strcpy((char*)global.name, "uninitialized");
27 |
28 | //setName("Bob");
29 | readName();
30 | strcpy(messages[0], "Hi there...");
31 | //strcpy(messages[1], "");
32 | memcpy(messages[1], global.name, 24);
33 | strcpy(messages[2], "");
34 | strcpy(messages[3], "");
35 | strcpy(messages[4], "");
36 | strcpy(messages[5], "");
37 | resetInputBuffer();
38 | rfBegin(11);
39 | }
40 |
41 | void readName()
42 | {
43 | //SRXEFlashRead(NAME_ADDR,global.name,256);
44 | for(int i = 0; i < 256; i++)
45 | {
46 | char nameByte = EEPROM.read(NAME_ADDR+i);
47 | if(nameByte == 0x00){
48 | break;
49 | }
50 | global.name[i] = nameByte;
51 | }
52 | }
53 |
54 | void setName(char *name)
55 | {
56 | /*uint8_t foo[256];
57 | memset(foo,0x00,256);
58 | strcpy((char*)foo,name);
59 | bool result = SRXEFlashWritePage(NAME_ADDR,foo);
60 | delay(100);*/
61 | memset(global.name,0x00,256);
62 | for(int i = 0; i < 256; i++)
63 | {
64 | if(name[i] == 0x00)
65 | {
66 | break;
67 | }
68 | EEPROM.write(NAME_ADDR+i,name[i]);
69 | }
70 | //return(result);
71 | }
72 |
73 | void resetInputBuffer()
74 | {
75 | /*memcpy(command,">",1);
76 | memcpy(command+1," ",30);
77 | memcpy(command+27,"R05e",4);
78 | memcpy(command+31,0x00,1);
79 | memcpy(command+25,0x00,1);*/
80 |
81 | //memcpy(command, "> \x00R05e\x00",26);
82 | memcpy(global.command, "> ",26);
83 | }
84 |
85 | void redraw()
86 | {
87 | SRXEWriteString(0,0 ,messages[(0+row)%6], FONT_LARGE, 3, 0);
88 | SRXEWriteString(0,20 ,messages[(1+row)%6], FONT_LARGE, 3, 0);
89 | SRXEWriteString(0,40 ,messages[(2+row)%6], FONT_LARGE, 3, 0);
90 | SRXEWriteString(0,60 ,messages[(3+row)%6], FONT_LARGE, 3, 0);
91 | SRXEWriteString(0,80 ,messages[(4+row)%6], FONT_LARGE, 3, 0);
92 | SRXEWriteString(0,100,messages[(5+row)%6], FONT_LARGE, 3, 0);
93 |
94 | SRXEWriteString(0,120,global.command, FONT_LARGE, 3, 0);
95 | }
96 |
97 | void submit(char* submission)
98 | {
99 | strcpy(messages[row]," ");
100 | strncpy(messages[row],submission, 26);
101 | row = (row+1)%6;
102 | checkCanary();
103 | setName(submission);
104 | readName();
105 | strncpy(messages[1],(char*)global.name, 26);
106 | resetInputBuffer();
107 | redraw();
108 | }
109 |
110 | void checkCanary()
111 | {
112 | //if(memcmp(command+canaryOffset,"R05e",4) != 0) {
113 | if(memcmp(global.canary,"R05e",4) != 0) {
114 | for(int i = 0; i < 6; i++){ strcpy(messages[i],"OMGH@XXE!"); }
115 | //redraw();
116 | }
117 | }
118 |
119 | void loop() {
120 |
121 | // if we hear something on the radio, build up the net buffer
122 | if (rfAvailable())
123 | {
124 | byte n = rfRead();
125 | if(n)
126 | {
127 | if(n==3)
128 | {
129 | submit(netbuff);
130 | strcpy(netbuff, " ");
131 | ncurs = 0;
132 | }
133 | else
134 | {
135 | netbuff[ncurs]=n;
136 | ncurs = (ncurs+1)%24;
137 | }
138 | }
139 | }
140 |
141 | // otherwise, just take data from the keyboard
142 | byte k = SRXEGetKey();
143 | if(k)
144 | {
145 | // submit on "return" (key right of 'Sym', box line box)
146 | if(k==0x0D)
147 | {
148 | if(curs>3)
149 | {
150 | submit(global.command+2);
151 |
152 | // transmit
153 | for(int i=2;i ",26);//canaryOffset-1);
164 | //memcpy(command, "> \x00R05e\x00",26);//canaryOffset-1);
165 | curs = 2;
166 | /*if(memcmp(command+60,"R05e",4) == 0) {
167 | memcpy(command, "> ",26);//canaryOffset-1);
168 | curs = 2;
169 | }else{
170 | //strcpy(command, "OMGH@XXE!");
171 | submit("OMGH@XXE!");
172 | }*/
173 | }
174 | else
175 | {
176 | //sprintf(command,"> %d",k);
177 | //sprintf(command,"> %02X",k);
178 |
179 | if(k >= 0x20 && k <= 0x7A){
180 | //is it printable?
181 | global.command[curs]=k;
182 | //curs = (curs+1)%24;
183 | if(curs < 31){ //22) {
184 | curs++;
185 | }
186 | }else if(k == 0x08){
187 | // backspace?
188 | if(curs > 2){
189 | curs--;
190 | }
191 | global.command[curs] = 0x20; //space is your blank character
192 | }
193 | }
194 | }
195 | redraw();
196 | }
197 |
--------------------------------------------------------------------------------
/sketch/rssi.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include "RadioFunctions.h"
3 |
4 | char messages[6][24];
5 | char command[24] = "> ";
6 | char netbuff[24] = "";
7 | int curs = 2;
8 | int ncurs = 0;
9 | int row=0;
10 |
11 | void setup() {
12 | // put your setup code here, to run once:
13 |
14 | SRXEInit(0xe7, 0xd6, 0xa2); // initialize display
15 | //SRXEWriteString(0,120,command, FONT_LARGE, 3, 0); // draw large black text at x=0,y=120, fg=3, bg=0
16 |
17 | strcpy(messages[0], "");
18 | strcpy(messages[1], "");
19 | strcpy(messages[2], "");
20 | strcpy(messages[3], "");
21 | strcpy(messages[4], "");
22 | strcpy(messages[5], "");
23 |
24 | //rfBegin(24);
25 |
26 | }
27 |
28 | void redraw()
29 | {
30 | SRXEWriteString(0,0 ,messages[(0+row)%6], FONT_LARGE, 3, 0);
31 | SRXEWriteString(0,20 ,messages[(1+row)%6], FONT_LARGE, 3, 0);
32 | SRXEWriteString(0,40 ,messages[(2+row)%6], FONT_LARGE, 3, 0);
33 | SRXEWriteString(0,60 ,messages[(3+row)%6], FONT_LARGE, 3, 0);
34 | SRXEWriteString(0,80 ,messages[(4+row)%6], FONT_LARGE, 3, 0);
35 | SRXEWriteString(0,100,messages[(5+row)%6], FONT_LARGE, 3, 0);
36 |
37 | SRXEWriteString(0,120,command, FONT_LARGE, 3, 0);
38 | }
39 |
40 | void submit(char* submission)
41 | {
42 | strcpy(messages[row]," ");
43 | strcpy(messages[row],submission);
44 | row = (row+1)%6;
45 |
46 | redraw();
47 | }
48 |
49 | void loop() {
50 |
51 | int x;
52 | int y;
53 |
54 | for(int i=11;i<=26;++i)
55 | {
56 | rfBegin(i);
57 | delay(100);
58 |
59 | y=(i-11)%8*17;
60 | if(i<19){ x = 0;}
61 | else { x = 201; }
62 |
63 | if(rfAvailable())
64 | {
65 | while(0>rfRead()){} // burn through the reads
66 | sprintf(command,"Ch %d : %d ",i,rssiRaw);
67 | SRXEWriteString(x,y ,command, FONT_LARGE, 3, 0);
68 |
69 | }
70 | else
71 | {
72 | sprintf(command,"Ch %d : %d ",i,rssiRaw);
73 | SRXEWriteString(x,y ,command, FONT_LARGE, 3, 0);
74 | }
75 |
76 | }
77 |
78 |
79 | }
80 |
--------------------------------------------------------------------------------