diff --git a/README.md b/README.md index c0c93c8..7443f05 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,21 @@ Control the color of the RGB LED from the touch screen. [README](rust/rgbcontrol/README.md) +rust/esp8266at +-------------- + +A crate for communicating with WiFi using the ESP8266 using AT commands. TODO: move this to its own repository. + +[README](rust/esp8266at/README.md) + +rust/weather +------------ + +Uses the ESP8285 WiFi chip of the Maix Go to fetch weather data from +[wttr.in](https://wttr.in) and print it to the display using `k210-console`. + +[README](rust/weather/README.md) + ROM re'ing =========== diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 1c9745a..c26d64f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -6,6 +6,7 @@ members = [ "accelerometer", "uart-passthrough", "rgbcontrol", + "weather", ] [patch.crates-io] diff --git a/rust/k210-console/data/README.md b/rust/k210-console/data/README.md new file mode 100644 index 0000000..8c2743f --- /dev/null +++ b/rust/k210-console/data/README.md @@ -0,0 +1,3 @@ +Based on https://github.com/nabijaczleweli/codepage-437/blob/master/dialect-specs/cp437_wingdings/values.tsv by nabijaczleweli. + +- Changed 0xFF to WHITE SQUARE diff --git a/rust/k210-console/data/values.tsv b/rust/k210-console/data/values.tsv new file mode 100644 index 0000000..596399e --- /dev/null +++ b/rust/k210-console/data/values.tsv @@ -0,0 +1,161 @@ +cp437_wingdings Unicode Comment +0x01 0x263A WHITE SMILING FACE +0x02 0x263B BLACK SMILING FACE +0x03 0x2665 BLACK HEART SUIT +0x04 0x2666 BLACK DIAMOND SUIT +0x05 0x2663 BLACK CLUB SUIT +0x06 0x2660 BLACK SPADE SUIT +0x07 0x2022 BULLET +0x08 0x25D8 INVERSE BULLET +0x09 0x25CB WHITE CIRCLE +0x0A 0x25D9 INVERSE WHITE CIRCLE +0x0B 0x2642 MALE SIGN +0x0C 0x2640 FEMALE SIGN +0x0D 0x266A EIGHTH NOTE +0x0E 0x266B BEAMED EIGHTH NOTES +0x0F 0x263C WHITE SUN WITH RAYS +0x10 0x25BA BLACK RIGHT-POINTING POINTER +0x11 0x25C4 BLACK LEFT-POINTING POINTER +0x12 0x2195 UP DOWN ARROW +0x13 0x203C DOUBLE EXCLAMATION MARK +0x14 0x00B6 PILCROW SIGN +0x15 0x00A7 SECTION SIGN +0x16 0x25AC BLACK RECTANGLE +0x17 0x21A8 UP DOWN ARROW WITH BASE +0x18 0x2191 UPWARDS ARROW +0x19 0x2193 DOWNWARDS ARROW +0x1A 0x2192 RIGHTWARDS ARROW +0x1B 0x2190 LEFTWARDS ARROW +0x1C 0x221F RIGHT ANGLE +0x1D 0x2194 LEFT RIGHT ARROW +0x1E 0x25B2 BLACK UP-POINTING TRIANGLE +0x1F 0x25BC BLACK DOWN-POINTING TRIANGLE +0x7F 0x2302 HOUSE +0x80 0x00C7 LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00FC LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00E9 LATIN SMALL LETTER E WITH ACUTE +0x83 0x00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00E4 LATIN SMALL LETTER A WITH DIAERESIS +0x85 0x00E0 LATIN SMALL LETTER A WITH GRAVE +0x86 0x00E5 LATIN SMALL LETTER A WITH RING ABOVE +0x87 0x00E7 LATIN SMALL LETTER C WITH CEDILLA +0x88 0x00EA LATIN SMALL LETTER E WITH CIRCUMFLEX +0x89 0x00EB LATIN SMALL LETTER E WITH DIAERESIS +0x8A 0x00E8 LATIN SMALL LETTER E WITH GRAVE +0x8B 0x00EF LATIN SMALL LETTER I WITH DIAERESIS +0x8C 0x00EE LATIN SMALL LETTER I WITH CIRCUMFLEX +0x8D 0x00EC LATIN SMALL LETTER I WITH GRAVE +0x8E 0x00C4 LATIN CAPITAL LETTER A WITH DIAERESIS +0x8F 0x00C5 LATIN CAPITAL LETTER A WITH RING ABOVE +0x90 0x00C9 LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00E6 LATIN SMALL LETTER AE +0x92 0x00C6 LATIN CAPITAL LETTER AE +0x93 0x00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00F6 LATIN SMALL LETTER O WITH DIAERESIS +0x95 0x00F2 LATIN SMALL LETTER O WITH GRAVE +0x96 0x00FB LATIN SMALL LETTER U WITH CIRCUMFLEX +0x97 0x00F9 LATIN SMALL LETTER U WITH GRAVE +0x98 0x00FF LATIN SMALL LETTER Y WITH DIAERESIS +0x99 0x00D6 LATIN CAPITAL LETTER O WITH DIAERESIS +0x9A 0x00DC LATIN CAPITAL LETTER U WITH DIAERESIS +0x9B 0x00A2 CENT SIGN +0x9C 0x00A3 POUND SIGN +0x9D 0x00A5 YEN SIGN +0x9E 0x20A7 PESETA SIGN +0x9F 0x0192 LATIN SMALL LETTER F WITH HOOK +0xA0 0x00E1 LATIN SMALL LETTER A WITH ACUTE +0xA1 0x00ED LATIN SMALL LETTER I WITH ACUTE +0xA2 0x00F3 LATIN SMALL LETTER O WITH ACUTE +0xA3 0x00FA LATIN SMALL LETTER U WITH ACUTE +0xA4 0x00F1 LATIN SMALL LETTER N WITH TILDE +0xA5 0x00D1 LATIN CAPITAL LETTER N WITH TILDE +0xA6 0x00AA FEMININE ORDINAL INDICATOR +0xA7 0x00BA MASCULINE ORDINAL INDICATOR +0xA8 0x00BF INVERTED QUESTION MARK +0xA9 0x2310 REVERSED NOT SIGN +0xAA 0x00AC NOT SIGN +0xAB 0x00BD VULGAR FRACTION ONE HALF +0xAC 0x00BC VULGAR FRACTION ONE QUARTER +0xAD 0x00A1 INVERTED EXCLAMATION MARK +0xAE 0x00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAF 0x00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xB0 0x2591 LIGHT SHADE +0xB1 0x2592 MEDIUM SHADE +0xB2 0x2593 DARK SHADE +0xB3 0x2502 BOX DRAWINGS LIGHT VERTICAL +0xB4 0x2524 BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xB5 0x2561 BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xB6 0x2562 BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xB7 0x2556 BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xB8 0x2555 BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xB9 0x2563 BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xBA 0x2551 BOX DRAWINGS DOUBLE VERTICAL +0xBB 0x2557 BOX DRAWINGS DOUBLE DOWN AND LEFT +0xBC 0x255D BOX DRAWINGS DOUBLE UP AND LEFT +0xBD 0x255C BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xBE 0x255B BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xBF 0x2510 BOX DRAWINGS LIGHT DOWN AND LEFT +0xC0 0x2514 BOX DRAWINGS LIGHT UP AND RIGHT +0xC1 0x2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xC2 0x252C BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xC3 0x251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xC4 0x2500 BOX DRAWINGS LIGHT HORIZONTAL +0xC5 0x253C BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xC6 0x255E BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xC7 0x255F BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xC8 0x255A BOX DRAWINGS DOUBLE UP AND RIGHT +0xC9 0x2554 BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xCA 0x2569 BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xCB 0x2566 BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xCC 0x2560 BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xCD 0x2550 BOX DRAWINGS DOUBLE HORIZONTAL +0xCE 0x256C BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xCF 0x2567 BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xD0 0x2568 BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xD1 0x2564 BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xD2 0x2565 BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xD3 0x2559 BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xD4 0x2558 BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xD5 0x2552 BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xD6 0x2553 BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xD7 0x256B BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xD8 0x256A BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xD9 0x2518 BOX DRAWINGS LIGHT UP AND LEFT +0xDA 0x250C BOX DRAWINGS LIGHT DOWN AND RIGHT +0xDB 0x2588 FULL BLOCK +0xDC 0x2584 LOWER HALF BLOCK +0xDD 0x258C LEFT HALF BLOCK +0xDE 0x2590 RIGHT HALF BLOCK +0xDF 0x2580 UPPER HALF BLOCK +0xE0 0x03B1 GREEK SMALL LETTER ALPHA +0xE1 0x00DF LATIN SMALL LETTER SHARP S +0xE2 0x0393 GREEK CAPITAL LETTER GAMMA +0xE3 0x03C0 GREEK SMALL LETTER PI +0xE4 0x03A3 GREEK CAPITAL LETTER SIGMA +0xE5 0x03C3 GREEK SMALL LETTER SIGMA +0xE6 0x00B5 MICRO SIGN +0xE7 0x03C4 GREEK SMALL LETTER TAU +0xE8 0x03A6 GREEK CAPITAL LETTER PHI +0xE9 0x0398 GREEK CAPITAL LETTER THETA +0xEA 0x03A9 GREEK CAPITAL LETTER OMEGA +0xEB 0x03B4 GREEK SMALL LETTER DELTA +0xEC 0x221E INFINITY +0xED 0x03C6 GREEK SMALL LETTER PHI +0xEE 0x03B5 GREEK SMALL LETTER EPSILON +0xEF 0x2229 INTERSECTION +0xF0 0x2261 IDENTICAL TO +0xF1 0x00B1 PLUS-MINUS SIGN +0xF2 0x2265 GREATER-THAN OR EQUAL TO +0xF3 0x2264 LESS-THAN OR EQUAL TO +0xF4 0x2320 TOP HALF INTEGRAL +0xF5 0x2321 BOTTOM HALF INTEGRAL +0xF6 0x00F7 DIVISION SIGN +0xF7 0x2248 ALMOST EQUAL TO +0xF8 0x00B0 DEGREE SIGN +0xF9 0x2219 BULLET OPERATOR +0xFA 0x00B7 MIDDLE DOT +0xFB 0x221A SQUARE ROOT +0xFC 0x207F SUPERSCRIPT LATIN SMALL LETTER N +0xFD 0x00B2 SUPERSCRIPT TWO +0xFE 0x25A0 BLACK SQUARE +0xFF 0x25A1 WHITE SQUARE diff --git a/rust/k210-console/scripts/create_tables.py b/rust/k210-console/scripts/create_tables.py new file mode 100755 index 0000000..9ddce4d --- /dev/null +++ b/rust/k210-console/scripts/create_tables.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +import unicodedata + +mapping = [] +with open('../data/values.tsv', 'r') as f: + lines = iter(f) + next(lines) + for line in lines: + (byte, unich, name) = line.rstrip().split('\t') + byte = int(byte, 0) + unich = int(unich, 0) + mapping.append((byte, unich, name)) + +# add in ASCII +mapping.append((0x00, 0x0000, 'NUL')) +for ch in range(0x20, 0x7f): + mapping.append((ch, ch, unicodedata.name(chr(ch)))) +mapping.sort() + +fw_mapping = [None] * 256 +for byte, unich, name in mapping: + #print('%02x %05x %s' % (byte, unich, name)) + fw_mapping[byte] = unich + +def qchar(ch): + return "'\\u{%04x}'" % ch + +print('static FROM: [char; 256] = [') +for i in range(0x100): + if (i % 8)==0: + print(" ", end='') + print("%s," % qchar(fw_mapping[i]), end='') + if ((i+1) % 8)==0: + print() + else: + print(" ", end='') +print(']') + +print('pub fn to(ch: char) -> u8 {') +print(' match ch {') +for byte, unich, name in mapping: + print(' %s => 0x%02x, // %s' % (qchar(unich), byte, name)) +print(' }') +print('}') + diff --git a/rust/k210-console/src/console.rs b/rust/k210-console/src/console.rs index 33e1f0f..c9dad11 100644 --- a/rust/k210-console/src/console.rs +++ b/rust/k210-console/src/console.rs @@ -1,11 +1,16 @@ +use core::fmt; + use k210_shared::board::lcd_colors::rgb565; use crate::cp437; use crate::cp437_8x8; +use crate::palette_xterm256::PALETTE; pub const DISP_WIDTH: usize = 320; pub const DISP_HEIGHT: usize = 240; const GRID_WIDTH: usize = DISP_WIDTH / 8; const GRID_HEIGHT: usize = DISP_HEIGHT / 8; +const DEF_FG: u16 = rgb565(192, 192, 192); +const DEF_BG: u16 = rgb565(0, 0, 0); pub type ScreenImage = [u32; DISP_WIDTH * DISP_HEIGHT / 2]; @@ -18,15 +23,15 @@ pub struct Color { } impl Color { - pub fn new(r: u8, g: u8, b: u8) -> Color { + pub const fn new(r: u8, g: u8, b: u8) -> Color { Color { r, g, b, a: 255 } } - pub fn new_rgba(r: u8, g: u8, b: u8, a: u8) -> Color { + pub const fn new_rgba(r: u8, g: u8, b: u8, a: u8) -> Color { Color { r, g, b, a: a } } - pub fn from_rgba32(val: u32) -> Color { + pub const fn from_rgba32(val: u32) -> Color { Color { r: ((val >> 24) & 0xff) as u8, g: ((val >> 16) & 0xff) as u8, @@ -34,6 +39,10 @@ impl Color { a: ((val >> 0) & 0xff) as u8, } } + + pub const fn to_rgb565(&self) -> u16 { + rgb565(self.r, self.g, self.b) + } } #[derive(Copy, Clone)] @@ -71,27 +80,71 @@ pub struct Cell { ch: char, } +enum State { + Initial, + Escape, + CSI, +} + +enum Sgr { + Initial, + SpecialFg, + SpecialBg, + Fg256, + Bg256, + FgR, + BgR, + FgG, + BgG, + FgB, + BgB, +} + /** Visual attributes of console */ pub struct Console { + /** Dirty flag */ + pub dirty: bool, /** Array of character cells representing console */ cells: [Cell; GRID_WIDTH * GRID_HEIGHT], /** Cursor position */ cursor_pos: Coord, /** Cursor visible flag */ cursor_visible: bool, + /** Default foreground */ + def_fg: u16, + /** Default background */ + def_bg: u16, + /** Current foreground */ + cur_fg: u16, + /** Current background */ + cur_bg: u16, + /** Current escape state */ + state: State, + /** Current CSI parameter */ + idx: usize, + /** CSI parameters */ + num: [u16; 16], } impl Console { /** Create new, empty console */ pub fn new() -> Console { Console { + dirty: false, cells: [Cell { - fg: 0, - bg: 0, + fg: DEF_FG, + bg: DEF_BG, ch: '\x00', }; GRID_WIDTH * GRID_HEIGHT], cursor_pos: Coord::new(0, 0), cursor_visible: true, + def_fg: DEF_FG, + def_bg: DEF_BG, + cur_fg: DEF_FG, + cur_bg: DEF_BG, + state: State::Initial, + idx: 0, + num: [0; 16], } } @@ -140,11 +193,139 @@ impl Console { GRID_HEIGHT as u16 } + /** Put a char at an arbitrary position with arbitrary fg/bg color. Does not move the cursor. + * Use this to regard the console as a simple grid of cells a la libtcod. Useful for drawing + * frames and such. + */ pub fn put(&mut self, x: u16, y: u16, fg: Color, bg: Color, ch: char) { + self.dirty = true; self.cells[(y as usize) * GRID_WIDTH + (x as usize)] = Cell { fg: rgb565(fg.r, fg.g, fg.b), bg: rgb565(bg.r, bg.g, bg.b), ch, }; } + + /** Handle SGR escape sequence parameters */ + pub fn handle_sgr(&mut self) { + let mut state = Sgr::Initial; + let mut color = Color::new(0, 0, 0); + for param in &self.num[0..self.idx+1] { + match state { + Sgr::Initial => { + match param { + 0 => { self.cur_fg = self.def_fg; self.cur_bg = self.def_bg; } + 30..=37 => { self.cur_fg = Color::from_rgba32(PALETTE[(param - 30) as usize]).to_rgb565(); } + 38 => { state = Sgr::SpecialFg; } + 40..=47 => { self.cur_bg = Color::from_rgba32(PALETTE[(param - 40) as usize]).to_rgb565(); } + 48 => { state = Sgr::SpecialBg; } + 90..=97 => { self.cur_fg = Color::from_rgba32(PALETTE[8 + (param - 90) as usize]).to_rgb565(); } + 100..=107 => { self.cur_bg = Color::from_rgba32(PALETTE[8 + (param - 100) as usize]).to_rgb565(); } + _ => {} + } + } + Sgr::SpecialFg => { + match param { + 2 => { state = Sgr::FgR; } + 5 => { state = Sgr::Fg256; } + _ => { state = Sgr::Initial; } + } + } + Sgr::SpecialBg => { + match param { + 2 => { state = Sgr::BgR; } + 5 => { state = Sgr::Bg256; } + _ => { state = Sgr::Initial; } + } + } + Sgr::Fg256 => { + self.cur_fg = Color::from_rgba32(PALETTE[(param & 0xff) as usize]).to_rgb565(); + state = Sgr::Initial; + } + Sgr::Bg256 => { + self.cur_bg = Color::from_rgba32(PALETTE[(param & 0xff) as usize]).to_rgb565(); + state = Sgr::Initial; + } + Sgr::FgR => { color.r = (param & 0xff) as u8; state = Sgr::FgG; } + Sgr::FgG => { color.g = (param & 0xff) as u8; state = Sgr::FgB; } + Sgr::FgB => { color.b = (param & 0xff) as u8; state = Sgr::Initial; self.cur_fg = color.to_rgb565(); } + Sgr::BgR => { color.r = (param & 0xff) as u8; state = Sgr::BgG; } + Sgr::BgG => { color.g = (param & 0xff) as u8; state = Sgr::BgB; } + Sgr::BgB => { color.b = (param & 0xff) as u8; state = Sgr::Initial; self.cur_bg = color.to_rgb565(); } + } + } + } + + /** Put a char at current cursor position, interpreting control and escape codes. */ + pub fn putch(&mut self, ch: char) { + match self.state { + State::Initial => { + match ch { + '\r' => { self.cursor_pos.x = 0; } + '\n' => { self.cursor_pos.y += 1; self.cursor_pos.x = 0; } + '\x1b' => { self.state = State::Escape; } + ch => { + self.dirty = true; + self.cells[(self.cursor_pos.y as usize) * GRID_WIDTH + (self.cursor_pos.x as usize)] = Cell { + fg: self.cur_fg, + bg: self.cur_bg, + ch, + }; + self.cursor_pos.x += 1; + } + } + } + State::Escape => { + match ch { + '[' => { self.state = State::CSI; self.idx = 0; self.num[0] = 0; } + _ => { self.state = State::Initial; } + } + } + State::CSI => { + match ch { + '0'..='9' => { + self.num[self.idx] *= 10; + self.num[self.idx] += ((ch as u8) - b'0') as u16; + } + ';' => { + self.idx += 1; + if self.idx == self.num.len() { + // Too many arguments, ignore sequence + self.state = State::Initial; + } else { + self.num[self.idx] = 0; + } + } + 'm' => { + self.handle_sgr(); + self.state = State::Initial; + } + _ => { + self.state = State::Initial; + } + } + } + } + // wrap around + if self.cursor_pos.x == GRID_WIDTH as u16 { + self.cursor_pos.x = 0; + self.cursor_pos.y += 1; + } + if self.cursor_pos.y == GRID_HEIGHT as u16 { + self.cursor_pos.y = 0; + } + } + + /** Put a string at current cursor position, interpreting control and escape codes. */ + pub fn puts(&mut self, s: &str) { + for ch in s.chars() { + self.putch(ch); + } + } +} + +/** Formatting adoption for console */ +impl fmt::Write for Console { + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { self.puts(s); Ok(()) } + fn write_char(&mut self, c: char) -> Result<(), fmt::Error> { self.putch(c); Ok(()) } } diff --git a/rust/k210-console/src/cp437.rs b/rust/k210-console/src/cp437.rs index a238bd9..cb398b5 100644 --- a/rust/k210-console/src/cp437.rs +++ b/rust/k210-console/src/cp437.rs @@ -1,9 +1,301 @@ +/** Table from cp437 to unicode char; generated with create_tables.py */ +static FROM: [char; 256] = [ + '\u{0000}', '\u{263a}', '\u{263b}', '\u{2665}', '\u{2666}', '\u{2663}', '\u{2660}', '\u{2022}', + '\u{25d8}', '\u{25cb}', '\u{25d9}', '\u{2642}', '\u{2640}', '\u{266a}', '\u{266b}', '\u{263c}', + '\u{25ba}', '\u{25c4}', '\u{2195}', '\u{203c}', '\u{00b6}', '\u{00a7}', '\u{25ac}', '\u{21a8}', + '\u{2191}', '\u{2193}', '\u{2192}', '\u{2190}', '\u{221f}', '\u{2194}', '\u{25b2}', '\u{25bc}', + '\u{0020}', '\u{0021}', '\u{0022}', '\u{0023}', '\u{0024}', '\u{0025}', '\u{0026}', '\u{0027}', + '\u{0028}', '\u{0029}', '\u{002a}', '\u{002b}', '\u{002c}', '\u{002d}', '\u{002e}', '\u{002f}', + '\u{0030}', '\u{0031}', '\u{0032}', '\u{0033}', '\u{0034}', '\u{0035}', '\u{0036}', '\u{0037}', + '\u{0038}', '\u{0039}', '\u{003a}', '\u{003b}', '\u{003c}', '\u{003d}', '\u{003e}', '\u{003f}', + '\u{0040}', '\u{0041}', '\u{0042}', '\u{0043}', '\u{0044}', '\u{0045}', '\u{0046}', '\u{0047}', + '\u{0048}', '\u{0049}', '\u{004a}', '\u{004b}', '\u{004c}', '\u{004d}', '\u{004e}', '\u{004f}', + '\u{0050}', '\u{0051}', '\u{0052}', '\u{0053}', '\u{0054}', '\u{0055}', '\u{0056}', '\u{0057}', + '\u{0058}', '\u{0059}', '\u{005a}', '\u{005b}', '\u{005c}', '\u{005d}', '\u{005e}', '\u{005f}', + '\u{0060}', '\u{0061}', '\u{0062}', '\u{0063}', '\u{0064}', '\u{0065}', '\u{0066}', '\u{0067}', + '\u{0068}', '\u{0069}', '\u{006a}', '\u{006b}', '\u{006c}', '\u{006d}', '\u{006e}', '\u{006f}', + '\u{0070}', '\u{0071}', '\u{0072}', '\u{0073}', '\u{0074}', '\u{0075}', '\u{0076}', '\u{0077}', + '\u{0078}', '\u{0079}', '\u{007a}', '\u{007b}', '\u{007c}', '\u{007d}', '\u{007e}', '\u{2302}', + '\u{00c7}', '\u{00fc}', '\u{00e9}', '\u{00e2}', '\u{00e4}', '\u{00e0}', '\u{00e5}', '\u{00e7}', + '\u{00ea}', '\u{00eb}', '\u{00e8}', '\u{00ef}', '\u{00ee}', '\u{00ec}', '\u{00c4}', '\u{00c5}', + '\u{00c9}', '\u{00e6}', '\u{00c6}', '\u{00f4}', '\u{00f6}', '\u{00f2}', '\u{00fb}', '\u{00f9}', + '\u{00ff}', '\u{00d6}', '\u{00dc}', '\u{00a2}', '\u{00a3}', '\u{00a5}', '\u{20a7}', '\u{0192}', + '\u{00e1}', '\u{00ed}', '\u{00f3}', '\u{00fa}', '\u{00f1}', '\u{00d1}', '\u{00aa}', '\u{00ba}', + '\u{00bf}', '\u{2310}', '\u{00ac}', '\u{00bd}', '\u{00bc}', '\u{00a1}', '\u{00ab}', '\u{00bb}', + '\u{2591}', '\u{2592}', '\u{2593}', '\u{2502}', '\u{2524}', '\u{2561}', '\u{2562}', '\u{2556}', + '\u{2555}', '\u{2563}', '\u{2551}', '\u{2557}', '\u{255d}', '\u{255c}', '\u{255b}', '\u{2510}', + '\u{2514}', '\u{2534}', '\u{252c}', '\u{251c}', '\u{2500}', '\u{253c}', '\u{255e}', '\u{255f}', + '\u{255a}', '\u{2554}', '\u{2569}', '\u{2566}', '\u{2560}', '\u{2550}', '\u{256c}', '\u{2567}', + '\u{2568}', '\u{2564}', '\u{2565}', '\u{2559}', '\u{2558}', '\u{2552}', '\u{2553}', '\u{256b}', + '\u{256a}', '\u{2518}', '\u{250c}', '\u{2588}', '\u{2584}', '\u{258c}', '\u{2590}', '\u{2580}', + '\u{03b1}', '\u{00df}', '\u{0393}', '\u{03c0}', '\u{03a3}', '\u{03c3}', '\u{00b5}', '\u{03c4}', + '\u{03a6}', '\u{0398}', '\u{03a9}', '\u{03b4}', '\u{221e}', '\u{03c6}', '\u{03b5}', '\u{2229}', + '\u{2261}', '\u{00b1}', '\u{2265}', '\u{2264}', '\u{2320}', '\u{2321}', '\u{00f7}', '\u{2248}', + '\u{00b0}', '\u{2219}', '\u{00b7}', '\u{221a}', '\u{207f}', '\u{00b2}', '\u{25a0}', '\u{25a1}', +]; + pub fn from(ch: u8) -> char { - // TODO: correct mapping - ch as char + FROM[ch as usize] } pub fn to(ch: char) -> u8 { - // TODO: correct mapping - ch as u8 + match ch { + '\u{0000}' => 0x00, // NUL + '\u{263a}' => 0x01, // WHITE SMILING FACE + '\u{263b}' => 0x02, // BLACK SMILING FACE + '\u{2665}' => 0x03, // BLACK HEART SUIT + '\u{2666}' => 0x04, // BLACK DIAMOND SUIT + '\u{2663}' => 0x05, // BLACK CLUB SUIT + '\u{2660}' => 0x06, // BLACK SPADE SUIT + '\u{2022}' => 0x07, // BULLET + '\u{25d8}' => 0x08, // INVERSE BULLET + '\u{25cb}' => 0x09, // WHITE CIRCLE + '\u{25d9}' => 0x0a, // INVERSE WHITE CIRCLE + '\u{2642}' => 0x0b, // MALE SIGN + '\u{2640}' => 0x0c, // FEMALE SIGN + '\u{266a}' => 0x0d, // EIGHTH NOTE + '\u{266b}' => 0x0e, // BEAMED EIGHTH NOTES + '\u{263c}' => 0x0f, // WHITE SUN WITH RAYS + '\u{25ba}' => 0x10, // BLACK RIGHT-POINTING POINTER + '\u{25c4}' => 0x11, // BLACK LEFT-POINTING POINTER + '\u{2195}' => 0x12, // UP DOWN ARROW + '\u{203c}' => 0x13, // DOUBLE EXCLAMATION MARK + '\u{00b6}' => 0x14, // PILCROW SIGN + '\u{00a7}' => 0x15, // SECTION SIGN + '\u{25ac}' => 0x16, // BLACK RECTANGLE + '\u{21a8}' => 0x17, // UP DOWN ARROW WITH BASE + '\u{2191}' => 0x18, // UPWARDS ARROW + '\u{2193}' => 0x19, // DOWNWARDS ARROW + '\u{2192}' => 0x1a, // RIGHTWARDS ARROW + '\u{2190}' => 0x1b, // LEFTWARDS ARROW + '\u{221f}' => 0x1c, // RIGHT ANGLE + '\u{2194}' => 0x1d, // LEFT RIGHT ARROW + '\u{25b2}' => 0x1e, // BLACK UP-POINTING TRIANGLE + '\u{25bc}' => 0x1f, // BLACK DOWN-POINTING TRIANGLE + '\u{0020}' => 0x20, // SPACE + '\u{0021}' => 0x21, // EXCLAMATION MARK + '\u{0022}' => 0x22, // QUOTATION MARK + '\u{0023}' => 0x23, // NUMBER SIGN + '\u{0024}' => 0x24, // DOLLAR SIGN + '\u{0025}' => 0x25, // PERCENT SIGN + '\u{0026}' => 0x26, // AMPERSAND + '\u{0027}' => 0x27, // APOSTROPHE + '\u{0028}' => 0x28, // LEFT PARENTHESIS + '\u{0029}' => 0x29, // RIGHT PARENTHESIS + '\u{002a}' => 0x2a, // ASTERISK + '\u{002b}' => 0x2b, // PLUS SIGN + '\u{002c}' => 0x2c, // COMMA + '\u{002d}' => 0x2d, // HYPHEN-MINUS + '\u{002e}' => 0x2e, // FULL STOP + '\u{002f}' => 0x2f, // SOLIDUS + '\u{0030}' => 0x30, // DIGIT ZERO + '\u{0031}' => 0x31, // DIGIT ONE + '\u{0032}' => 0x32, // DIGIT TWO + '\u{0033}' => 0x33, // DIGIT THREE + '\u{0034}' => 0x34, // DIGIT FOUR + '\u{0035}' => 0x35, // DIGIT FIVE + '\u{0036}' => 0x36, // DIGIT SIX + '\u{0037}' => 0x37, // DIGIT SEVEN + '\u{0038}' => 0x38, // DIGIT EIGHT + '\u{0039}' => 0x39, // DIGIT NINE + '\u{003a}' => 0x3a, // COLON + '\u{003b}' => 0x3b, // SEMICOLON + '\u{003c}' => 0x3c, // LESS-THAN SIGN + '\u{003d}' => 0x3d, // EQUALS SIGN + '\u{003e}' => 0x3e, // GREATER-THAN SIGN + '\u{003f}' => 0x3f, // QUESTION MARK + '\u{0040}' => 0x40, // COMMERCIAL AT + '\u{0041}' => 0x41, // LATIN CAPITAL LETTER A + '\u{0042}' => 0x42, // LATIN CAPITAL LETTER B + '\u{0043}' => 0x43, // LATIN CAPITAL LETTER C + '\u{0044}' => 0x44, // LATIN CAPITAL LETTER D + '\u{0045}' => 0x45, // LATIN CAPITAL LETTER E + '\u{0046}' => 0x46, // LATIN CAPITAL LETTER F + '\u{0047}' => 0x47, // LATIN CAPITAL LETTER G + '\u{0048}' => 0x48, // LATIN CAPITAL LETTER H + '\u{0049}' => 0x49, // LATIN CAPITAL LETTER I + '\u{004a}' => 0x4a, // LATIN CAPITAL LETTER J + '\u{004b}' => 0x4b, // LATIN CAPITAL LETTER K + '\u{004c}' => 0x4c, // LATIN CAPITAL LETTER L + '\u{004d}' => 0x4d, // LATIN CAPITAL LETTER M + '\u{004e}' => 0x4e, // LATIN CAPITAL LETTER N + '\u{004f}' => 0x4f, // LATIN CAPITAL LETTER O + '\u{0050}' => 0x50, // LATIN CAPITAL LETTER P + '\u{0051}' => 0x51, // LATIN CAPITAL LETTER Q + '\u{0052}' => 0x52, // LATIN CAPITAL LETTER R + '\u{0053}' => 0x53, // LATIN CAPITAL LETTER S + '\u{0054}' => 0x54, // LATIN CAPITAL LETTER T + '\u{0055}' => 0x55, // LATIN CAPITAL LETTER U + '\u{0056}' => 0x56, // LATIN CAPITAL LETTER V + '\u{0057}' => 0x57, // LATIN CAPITAL LETTER W + '\u{0058}' => 0x58, // LATIN CAPITAL LETTER X + '\u{0059}' => 0x59, // LATIN CAPITAL LETTER Y + '\u{005a}' => 0x5a, // LATIN CAPITAL LETTER Z + '\u{005b}' => 0x5b, // LEFT SQUARE BRACKET + '\u{005c}' => 0x5c, // REVERSE SOLIDUS + '\u{005d}' => 0x5d, // RIGHT SQUARE BRACKET + '\u{005e}' => 0x5e, // CIRCUMFLEX ACCENT + '\u{005f}' => 0x5f, // LOW LINE + '\u{0060}' => 0x60, // GRAVE ACCENT + '\u{0061}' => 0x61, // LATIN SMALL LETTER A + '\u{0062}' => 0x62, // LATIN SMALL LETTER B + '\u{0063}' => 0x63, // LATIN SMALL LETTER C + '\u{0064}' => 0x64, // LATIN SMALL LETTER D + '\u{0065}' => 0x65, // LATIN SMALL LETTER E + '\u{0066}' => 0x66, // LATIN SMALL LETTER F + '\u{0067}' => 0x67, // LATIN SMALL LETTER G + '\u{0068}' => 0x68, // LATIN SMALL LETTER H + '\u{0069}' => 0x69, // LATIN SMALL LETTER I + '\u{006a}' => 0x6a, // LATIN SMALL LETTER J + '\u{006b}' => 0x6b, // LATIN SMALL LETTER K + '\u{006c}' => 0x6c, // LATIN SMALL LETTER L + '\u{006d}' => 0x6d, // LATIN SMALL LETTER M + '\u{006e}' => 0x6e, // LATIN SMALL LETTER N + '\u{006f}' => 0x6f, // LATIN SMALL LETTER O + '\u{0070}' => 0x70, // LATIN SMALL LETTER P + '\u{0071}' => 0x71, // LATIN SMALL LETTER Q + '\u{0072}' => 0x72, // LATIN SMALL LETTER R + '\u{0073}' => 0x73, // LATIN SMALL LETTER S + '\u{0074}' => 0x74, // LATIN SMALL LETTER T + '\u{0075}' => 0x75, // LATIN SMALL LETTER U + '\u{0076}' => 0x76, // LATIN SMALL LETTER V + '\u{0077}' => 0x77, // LATIN SMALL LETTER W + '\u{0078}' => 0x78, // LATIN SMALL LETTER X + '\u{0079}' => 0x79, // LATIN SMALL LETTER Y + '\u{007a}' => 0x7a, // LATIN SMALL LETTER Z + '\u{007b}' => 0x7b, // LEFT CURLY BRACKET + '\u{007c}' => 0x7c, // VERTICAL LINE + '\u{007d}' => 0x7d, // RIGHT CURLY BRACKET + '\u{007e}' => 0x7e, // TILDE + '\u{2302}' => 0x7f, // HOUSE + '\u{00c7}' => 0x80, // LATIN CAPITAL LETTER C WITH CEDILLA + '\u{00fc}' => 0x81, // LATIN SMALL LETTER U WITH DIAERESIS + '\u{00e9}' => 0x82, // LATIN SMALL LETTER E WITH ACUTE + '\u{00e2}' => 0x83, // LATIN SMALL LETTER A WITH CIRCUMFLEX + '\u{00e4}' => 0x84, // LATIN SMALL LETTER A WITH DIAERESIS + '\u{00e0}' => 0x85, // LATIN SMALL LETTER A WITH GRAVE + '\u{00e5}' => 0x86, // LATIN SMALL LETTER A WITH RING ABOVE + '\u{00e7}' => 0x87, // LATIN SMALL LETTER C WITH CEDILLA + '\u{00ea}' => 0x88, // LATIN SMALL LETTER E WITH CIRCUMFLEX + '\u{00eb}' => 0x89, // LATIN SMALL LETTER E WITH DIAERESIS + '\u{00e8}' => 0x8a, // LATIN SMALL LETTER E WITH GRAVE + '\u{00ef}' => 0x8b, // LATIN SMALL LETTER I WITH DIAERESIS + '\u{00ee}' => 0x8c, // LATIN SMALL LETTER I WITH CIRCUMFLEX + '\u{00ec}' => 0x8d, // LATIN SMALL LETTER I WITH GRAVE + '\u{00c4}' => 0x8e, // LATIN CAPITAL LETTER A WITH DIAERESIS + '\u{00c5}' => 0x8f, // LATIN CAPITAL LETTER A WITH RING ABOVE + '\u{00c9}' => 0x90, // LATIN CAPITAL LETTER E WITH ACUTE + '\u{00e6}' => 0x91, // LATIN SMALL LETTER AE + '\u{00c6}' => 0x92, // LATIN CAPITAL LETTER AE + '\u{00f4}' => 0x93, // LATIN SMALL LETTER O WITH CIRCUMFLEX + '\u{00f6}' => 0x94, // LATIN SMALL LETTER O WITH DIAERESIS + '\u{00f2}' => 0x95, // LATIN SMALL LETTER O WITH GRAVE + '\u{00fb}' => 0x96, // LATIN SMALL LETTER U WITH CIRCUMFLEX + '\u{00f9}' => 0x97, // LATIN SMALL LETTER U WITH GRAVE + '\u{00ff}' => 0x98, // LATIN SMALL LETTER Y WITH DIAERESIS + '\u{00d6}' => 0x99, // LATIN CAPITAL LETTER O WITH DIAERESIS + '\u{00dc}' => 0x9a, // LATIN CAPITAL LETTER U WITH DIAERESIS + '\u{00a2}' => 0x9b, // CENT SIGN + '\u{00a3}' => 0x9c, // POUND SIGN + '\u{00a5}' => 0x9d, // YEN SIGN + '\u{20a7}' => 0x9e, // PESETA SIGN + '\u{0192}' => 0x9f, // LATIN SMALL LETTER F WITH HOOK + '\u{00e1}' => 0xa0, // LATIN SMALL LETTER A WITH ACUTE + '\u{00ed}' => 0xa1, // LATIN SMALL LETTER I WITH ACUTE + '\u{00f3}' => 0xa2, // LATIN SMALL LETTER O WITH ACUTE + '\u{00fa}' => 0xa3, // LATIN SMALL LETTER U WITH ACUTE + '\u{00f1}' => 0xa4, // LATIN SMALL LETTER N WITH TILDE + '\u{00d1}' => 0xa5, // LATIN CAPITAL LETTER N WITH TILDE + '\u{00aa}' => 0xa6, // FEMININE ORDINAL INDICATOR + '\u{00ba}' => 0xa7, // MASCULINE ORDINAL INDICATOR + '\u{00bf}' => 0xa8, // INVERTED QUESTION MARK + '\u{2310}' => 0xa9, // REVERSED NOT SIGN + '\u{00ac}' => 0xaa, // NOT SIGN + '\u{00bd}' => 0xab, // VULGAR FRACTION ONE HALF + '\u{00bc}' => 0xac, // VULGAR FRACTION ONE QUARTER + '\u{00a1}' => 0xad, // INVERTED EXCLAMATION MARK + '\u{00ab}' => 0xae, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + '\u{00bb}' => 0xaf, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + '\u{2591}' => 0xb0, // LIGHT SHADE + '\u{2592}' => 0xb1, // MEDIUM SHADE + '\u{2593}' => 0xb2, // DARK SHADE + '\u{2502}' => 0xb3, // BOX DRAWINGS LIGHT VERTICAL + '\u{2524}' => 0xb4, // BOX DRAWINGS LIGHT VERTICAL AND LEFT + '\u{2561}' => 0xb5, // BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE + '\u{2562}' => 0xb6, // BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE + '\u{2556}' => 0xb7, // BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE + '\u{2555}' => 0xb8, // BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE + '\u{2563}' => 0xb9, // BOX DRAWINGS DOUBLE VERTICAL AND LEFT + '\u{2551}' => 0xba, // BOX DRAWINGS DOUBLE VERTICAL + '\u{2557}' => 0xbb, // BOX DRAWINGS DOUBLE DOWN AND LEFT + '\u{255d}' => 0xbc, // BOX DRAWINGS DOUBLE UP AND LEFT + '\u{255c}' => 0xbd, // BOX DRAWINGS UP DOUBLE AND LEFT SINGLE + '\u{255b}' => 0xbe, // BOX DRAWINGS UP SINGLE AND LEFT DOUBLE + '\u{2510}' => 0xbf, // BOX DRAWINGS LIGHT DOWN AND LEFT + '\u{2514}' => 0xc0, // BOX DRAWINGS LIGHT UP AND RIGHT + '\u{2534}' => 0xc1, // BOX DRAWINGS LIGHT UP AND HORIZONTAL + '\u{252c}' => 0xc2, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + '\u{251c}' => 0xc3, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT + '\u{2500}' => 0xc4, // BOX DRAWINGS LIGHT HORIZONTAL + '\u{253c}' => 0xc5, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + '\u{255e}' => 0xc6, // BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE + '\u{255f}' => 0xc7, // BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE + '\u{255a}' => 0xc8, // BOX DRAWINGS DOUBLE UP AND RIGHT + '\u{2554}' => 0xc9, // BOX DRAWINGS DOUBLE DOWN AND RIGHT + '\u{2569}' => 0xca, // BOX DRAWINGS DOUBLE UP AND HORIZONTAL + '\u{2566}' => 0xcb, // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + '\u{2560}' => 0xcc, // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT + '\u{2550}' => 0xcd, // BOX DRAWINGS DOUBLE HORIZONTAL + '\u{256c}' => 0xce, // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + '\u{2567}' => 0xcf, // BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE + '\u{2568}' => 0xd0, // BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE + '\u{2564}' => 0xd1, // BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE + '\u{2565}' => 0xd2, // BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE + '\u{2559}' => 0xd3, // BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE + '\u{2558}' => 0xd4, // BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE + '\u{2552}' => 0xd5, // BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE + '\u{2553}' => 0xd6, // BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE + '\u{256b}' => 0xd7, // BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE + '\u{256a}' => 0xd8, // BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE + '\u{2518}' => 0xd9, // BOX DRAWINGS LIGHT UP AND LEFT + '\u{250c}' => 0xda, // BOX DRAWINGS LIGHT DOWN AND RIGHT + '\u{2588}' => 0xdb, // FULL BLOCK + '\u{2584}' => 0xdc, // LOWER HALF BLOCK + '\u{258c}' => 0xdd, // LEFT HALF BLOCK + '\u{2590}' => 0xde, // RIGHT HALF BLOCK + '\u{2580}' => 0xdf, // UPPER HALF BLOCK + '\u{03b1}' => 0xe0, // GREEK SMALL LETTER ALPHA + '\u{00df}' => 0xe1, // LATIN SMALL LETTER SHARP S + '\u{0393}' => 0xe2, // GREEK CAPITAL LETTER GAMMA + '\u{03c0}' => 0xe3, // GREEK SMALL LETTER PI + '\u{03a3}' => 0xe4, // GREEK CAPITAL LETTER SIGMA + '\u{03c3}' => 0xe5, // GREEK SMALL LETTER SIGMA + '\u{00b5}' => 0xe6, // MICRO SIGN + '\u{03c4}' => 0xe7, // GREEK SMALL LETTER TAU + '\u{03a6}' => 0xe8, // GREEK CAPITAL LETTER PHI + '\u{0398}' => 0xe9, // GREEK CAPITAL LETTER THETA + '\u{03a9}' => 0xea, // GREEK CAPITAL LETTER OMEGA + '\u{03b4}' => 0xeb, // GREEK SMALL LETTER DELTA + '\u{221e}' => 0xec, // INFINITY + '\u{03c6}' => 0xed, // GREEK SMALL LETTER PHI + '\u{03b5}' => 0xee, // GREEK SMALL LETTER EPSILON + '\u{2229}' => 0xef, // INTERSECTION + '\u{2261}' => 0xf0, // IDENTICAL TO + '\u{00b1}' => 0xf1, // PLUS-MINUS SIGN + '\u{2265}' => 0xf2, // GREATER-THAN OR EQUAL TO + '\u{2264}' => 0xf3, // LESS-THAN OR EQUAL TO + '\u{2320}' => 0xf4, // TOP HALF INTEGRAL + '\u{2321}' => 0xf5, // BOTTOM HALF INTEGRAL + '\u{00f7}' => 0xf6, // DIVISION SIGN + '\u{2248}' => 0xf7, // ALMOST EQUAL TO + '\u{00b0}' => 0xf8, // DEGREE SIGN + '\u{2219}' => 0xf9, // BULLET OPERATOR + '\u{00b7}' => 0xfa, // MIDDLE DOT + '\u{221a}' => 0xfb, // SQUARE ROOT + '\u{207f}' => 0xfc, // SUPERSCRIPT LATIN SMALL LETTER N + '\u{00b2}' => 0xfd, // SUPERSCRIPT TWO + '\u{25a0}' => 0xfe, // BLACK SQUARE + '\u{25a1}' => 0xff, // WHITE SQUARE + _ => 254, // Unknown + } } diff --git a/rust/k210-console/src/lib.rs b/rust/k210-console/src/lib.rs new file mode 100644 index 0000000..c99faf0 --- /dev/null +++ b/rust/k210-console/src/lib.rs @@ -0,0 +1,6 @@ +#![no_std] + +pub mod console; +pub mod cp437; +pub mod cp437_8x8; +pub mod palette_xterm256; diff --git a/rust/k210-console/src/main.rs b/rust/k210-console/src/main.rs index 0a1a8e1..3b2908b 100644 --- a/rust/k210-console/src/main.rs +++ b/rust/k210-console/src/main.rs @@ -4,11 +4,7 @@ #![no_std] #![no_main] -mod console; -mod cp437; -mod cp437_8x8; mod lfsr; -mod palette_xterm256; use k210_hal::pac; use k210_hal::prelude::*; @@ -22,8 +18,9 @@ use k210_shared::soc::spi::SPIExt; use k210_shared::soc::sysctl; use riscv_rt::entry; -use crate::console::{Color, Console, ScreenImage, DISP_HEIGHT, DISP_WIDTH}; -use crate::palette_xterm256::PALETTE; +use k210_console::console::{Color, Console, ScreenImage, DISP_HEIGHT, DISP_WIDTH}; +use k210_console::cp437; +use k210_console::palette_xterm256::PALETTE; /** Connect pins to internal functions */ fn io_mux_init() { @@ -120,7 +117,7 @@ fn main() -> ! { let spi = p.SPI0.constrain(); let mut lcd = LCD::new(spi); lcd.init(); - lcd.set_direction(lcd::direction::YX_RLDU); + lcd.set_direction(lcd::direction::YX_LRUD); lcd.clear(lcd_colors::PURPLE); let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2]; diff --git a/rust/weather/.gitignore b/rust/weather/.gitignore new file mode 100644 index 0000000..aa682e9 --- /dev/null +++ b/rust/weather/.gitignore @@ -0,0 +1,4 @@ +/target +**/*.rs.bk +# Do not check in secrets file +config.rs diff --git a/rust/weather/Cargo.toml b/rust/weather/Cargo.toml new file mode 100644 index 0000000..5745c16 --- /dev/null +++ b/rust/weather/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "weather" +version = "0.1.0" +authors = ["W.J. van der Laan "] +edition = "2018" + +[dependencies] +embedded-hal = { version = "0.2.1", features = ["unproven"] } +nb = "0.1.1" +riscv-rt = "0.5.0" +k210-hal = "0.1.0" +riscv = "0.5" +k210-shared = { path = "../k210-shared" } +k210-console = { path = "../k210-console" } +esp8266at = { path = "../esp8266at", default-features = false } +nom = { version = "4", default-features = false } diff --git a/rust/weather/README.md b/rust/weather/README.md new file mode 100644 index 0000000..41a9e56 --- /dev/null +++ b/rust/weather/README.md @@ -0,0 +1,15 @@ +# `weather` + +Uses the ESP8285 WiFi chip of the Maix Go to fetch weather data from +[wttr.in](https://wttr.in) and print it to the display using `k210-console`. + +As it needs to connect to an access point first, this needs configuration of one +to connect to in `src/config.rs`: + +```bash +cp src/config.rs.example src/config.rs +vim src/config.rs # ... +``` + +Set `` and `` accordingly. Do not check in `src/config.rs` ! +(gitignore settings should prevent this) diff --git a/rust/weather/src/config.rs.example b/rust/weather/src/config.rs.example new file mode 100644 index 0000000..66208fe --- /dev/null +++ b/rust/weather/src/config.rs.example @@ -0,0 +1,8 @@ +/** Secrets */ + +/** Access point name */ +pub const APNAME: &str = ""; + +/** Access point password */ +pub const APPASS: &str = ""; + diff --git a/rust/weather/src/main.rs b/rust/weather/src/main.rs new file mode 100644 index 0000000..0fe1645 --- /dev/null +++ b/rust/weather/src/main.rs @@ -0,0 +1,236 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![no_std] +#![no_main] + +use core::str; +use embedded_hal::serial; +use esp8266at::handler::{NetworkEvent, SerialNetworkHandler}; +use esp8266at::response::{parse_response, ConnectionType}; +use esp8266at::traits::{self, Write}; +use k210_hal::pac; +use k210_hal::prelude::*; +use k210_hal::stdout::Stdout; +use k210_shared::board::def::io; +use k210_shared::board::lcd::{self, LCD, LCDHL}; +use k210_shared::soc::fpioa; +use k210_shared::soc::gpio; +use k210_shared::soc::gpiohs; +use k210_shared::soc::sleep::usleep; +use k210_shared::soc::spi::SPIExt; +use k210_shared::soc::sysctl; +use nb::block; +use nom::Offset; +use riscv::register::mcycle; +use riscv_rt::entry; +use k210_console::console::{Console, ScreenImage, DISP_HEIGHT, DISP_WIDTH}; + +mod config; + +const DEFAULT_BAUD: u32 = 115_200; +const TIMEOUT: usize = 390_000_000 * 40 / 115200; + +struct WriteAdapter<'a, TX> +where + TX: serial::Write, +{ + tx: &'a mut TX, +} +impl<'a, TX> WriteAdapter<'a, TX> +where + TX: serial::Write, + TX::Error: core::fmt::Debug, +{ + fn new(tx: &'a mut TX) -> Self { + Self { tx } + } +} +impl<'a, TX> traits::Write for WriteAdapter<'a, TX> +where + TX: serial::Write, + TX::Error: core::fmt::Debug, +{ + type Error = TX::Error; + + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + for ch in buf { + block!(self.tx.write(*ch))?; + } + Ok(()) + } +} + +/** Connect pins to internal functions */ +fn io_init() { + /* Init SPI IO map and function settings */ + fpioa::set_function( + io::LCD_RST.into(), + fpioa::function::gpiohs(lcd::RST_GPIONUM), + ); + fpioa::set_io_pull(io::LCD_RST.into(), fpioa::pull::DOWN); // outputs must be pull-down + fpioa::set_function(io::LCD_DC.into(), fpioa::function::gpiohs(lcd::DCX_GPIONUM)); + fpioa::set_io_pull(io::LCD_DC.into(), fpioa::pull::DOWN); + fpioa::set_function(io::LCD_CS.into(), fpioa::function::SPI0_SS3); + fpioa::set_function(io::LCD_WR.into(), fpioa::function::SPI0_SCLK); + + sysctl::set_spi0_dvp_data(true); + + /* Set dvp and spi pin to 1.8V */ + sysctl::set_power_mode(sysctl::power_bank::BANK6, sysctl::io_power_mode::V18); + sysctl::set_power_mode(sysctl::power_bank::BANK7, sysctl::io_power_mode::V18); +} + +#[entry] +fn main() -> ! { + let p = pac::Peripherals::take().unwrap(); + let clocks = k210_hal::clock::Clocks::new(); + + usleep(200000); + io_init(); + + // Configure UARTHS (→host) + let mut serial = p.UARTHS.constrain(DEFAULT_BAUD.bps(), &clocks); + let (mut tx, mut rx) = serial.split(); + let mut debug = Stdout(&mut tx); + + // Configure UART1 (→WIFI) + sysctl::clock_enable(sysctl::clock::UART1); + sysctl::reset(sysctl::reset::UART1); + fpioa::set_function(io::WIFI_RX as u8, fpioa::function::UART1_TX); + fpioa::set_function(io::WIFI_TX as u8, fpioa::function::UART1_RX); + fpioa::set_function(io::WIFI_EN as u8, fpioa::function::GPIOHS8); + fpioa::set_io_pull(io::WIFI_EN as u8, fpioa::pull::DOWN); + gpiohs::set_pin(8, true); + gpiohs::set_direction(8, gpio::direction::OUTPUT); + let mut wifi_serial = p.UART1.constrain(DEFAULT_BAUD.bps(), &clocks); + let (mut wtx, mut wrx) = wifi_serial.split(); + + let mut wa = WriteAdapter::new(&mut wtx); + let mut sh = SerialNetworkHandler::new(&mut wa, config::APNAME.as_bytes(), config::APPASS.as_bytes()); + + // LCD ini + let spi = p.SPI0.constrain(); + let mut lcd = LCD::new(spi); + lcd.init(); + lcd.set_direction(lcd::direction::YX_LRUD); + let mut console: Console = Console::new(); + + let mut i:u16 = 0; + writeln!(console, "\x1b[48;2;128;192;255;38;5;0m WEATHER \x1b[0m \x1b[38;2;128;128;128mfetching...\x1b[0m").unwrap(); + + // Start off connection process state machine + sh.start(false); + writeln!(console, "∙ Connecting to AP").unwrap(); + + let mut serial_buf = [0u8; 2560]; // 2048 + some + let mut ofs: usize = 0; + + let mut cur_link = 0; + loop { + if console.dirty { + let mut image: ScreenImage = [0; DISP_WIDTH * DISP_HEIGHT / 2]; + console.render(&mut image); + lcd.draw_picture(0, 0, DISP_WIDTH as u16, DISP_HEIGHT as u16, &image); + console.dirty = false; + } + + // Receive byte into buffer + if let Ok(ch) = wrx.read() { + let ofs0 = ofs; + serial_buf[ofs] = ch; + ofs += 1; + let mut lastrecv = mcycle::read(); + loop { + // Read until we stop receiving for a certain duration + // This is a hack around the fact that in the time that the parser runs, + // more than one FIFO full of characters can be received so characters could be + // lost. The right way would be to receive in an interrupt handler, but, + // we don't have that yet. + if let Ok(ch) = wrx.read() { + serial_buf[ofs] = ch; + ofs += 1; + lastrecv = mcycle::read(); + } else if (mcycle::read().wrapping_sub(lastrecv)) >= TIMEOUT { + break; + } + } + //writeln!(debug, "ofs: {} received {} chars {:?}", ofs0, ofs - ofs0, + // &serial_buf[ofs0..ofs]).unwrap(); + + // Loop as long as there's something in the buffer to parse, starting at the + // beginning + let mut start = 0; + while start < ofs { + // try parsing + let tail = &serial_buf[start..ofs]; + let erase = match parse_response(tail) { + Ok((residue, resp)) => { + sh.message(&resp, &mut |port, ev, debug| { + match ev { + NetworkEvent::Ready => { + writeln!(console, "∙ Connected to AP").unwrap(); + cur_link = port.connect(ConnectionType::TCP, b"wttr.in", 80).unwrap(); + writeln!(console, "∙ \x1b[38;5;141m[{}]\x1b[0m Opening TCP conn", cur_link).unwrap(); + } + NetworkEvent::Error => { + writeln!(console, "∙ Could not connect to AP").unwrap(); + } + NetworkEvent::ConnectionEstablished(link) => { + if link == cur_link { + writeln!(console, "∙ \x1b[38;5;141m[{}]\x1b[0m Sending HTTP request", link).unwrap(); + port.write_all(b"GET /?0qA HTTP/1.1\r\nHost: wttr.in\r\nConnection: close\r\nUser-Agent: Weather-Spy\r\n\r\n").unwrap(); + port.send(link).unwrap(); + } + } + NetworkEvent::Data(link, data) => { + // write!(debug, "{}", str::from_utf8(data).unwrap()); + if link == cur_link { + console.puts(str::from_utf8(data).unwrap_or("???")); + } + } + NetworkEvent::ConnectionClosed(link) => { + writeln!(console, "∙ \x1b[38;5;141m[{}]\x1b[0m \x1b[38;2;100;100;100m[closed]\x1b[0m", link).unwrap(); + } + _ => { } + } + }, &mut debug).unwrap(); + + tail.offset(residue) + } + Err(nom::Err::Incomplete(_)) => { + // Incomplete, ignored, just retry after a new receive + 0 + } + Err(err) => { + writeln!(debug, "err: {:?}", err).unwrap(); + // Erase unparseable data to next line, if line is complete + if let Some(ofs) = tail.iter().position(|&x| x == b'\n') { + ofs + 1 + } else { + // If not, retry next time + 0 + } + } + }; + + if erase == 0 { + // End of input or remainder unparseable + break; + } + start += erase; + } + // Erase everything before new starting offset + for i in start..ofs { + serial_buf[i - start] = serial_buf[i]; + } + ofs -= start; + } + + /* + if let Ok(ch) = rx.read() { + let _res = block!(wtx.write(ch)); + } + */ + } +}