diff --git a/Minitel-RAM.js b/Minitel-RAM.js
new file mode 100644
index 0000000..8173505
--- /dev/null
+++ b/Minitel-RAM.js
@@ -0,0 +1,417 @@
+var Minitel=require("Minitel.js");
+
+//
+function DinOS(){
+ var app,appl,api,ui;
+ var apps={};
+ var m=global.m=new Minitel();
+ // === STARTUP ===
+ setWatch(()=>{
+ m.println("Reiniciando...");
+ setTimeout(load,200);
+ },BTN1,{
+ repeat:true,
+ edge:'falling',
+ debounce:10
+ });
+ m.onInput=(ch,code)=>{
+ if (code==m.keyCode.GUIDE){
+ selectApp("selector");
+ return;
+ }
+ if (code==m.keyCode.SOMMAIRE){
+ selectApp("conf");
+ return;
+ }
+ if (code==m.keyCode.FIN){
+ rst();
+ return;
+ }
+ if (appl && appl.onInput)
+ appl.onInput(ch,code);
+ else if (ui)
+ if (ui.onInput)
+ ui.onInput(ch,code);
+ };
+ // === PRIVATE API ===
+ function draw(){
+ if (!ui)
+ return;
+ ui.draw();
+ }
+ function rst(){
+ m.defaults().showCursor(false);
+ if (!app){
+ header(DinOS.name());
+ //TODO: Mostrar el menu de aplicaciones
+ }else{
+ appl=new app(api,m);
+ header(appl.name());
+ ui=appl.go();
+ draw();
+ }
+ }
+ function header(name){
+ //m.locate(1,0).write(95).repeat(40);
+ m.locate(1,0).graphics().write(127).repeat(40).text();
+ m.locate(2,0).print(name);
+ m.locate(1,1);
+ }
+ // === PUBLIC API ===
+ function registerApp(app){
+ apps[app.prototype.id()]=app;
+ }
+ function selectApp(id){
+ appl=null;
+ app=apps[id];
+ rst();
+ }
+ // === INIT ===
+ header(DinOS.name());
+ m.println("Loading...");
+
+ // === PUBLIC API ===
+ api={
+ getApps:()=>apps,
+ selectApp:selectApp,
+ show:(w)=>{
+ ui=w;
+ draw();
+ },
+ draw:draw
+ };
+ // === PUBLISHING ===
+ this.registerApp=registerApp;
+ this.selectApp=selectApp;
+ this.reset=rst;
+}
+DinOS.name=()=>`dinOS v0.3`;
+
+function Window(api,m,data){
+ if (!data)
+ throw new Error("Missing controller");
+ if (!data.controls || data.controls.length==0)
+ throw new Error("Empty window");
+ var focs,foci=-1,foca=[];
+ function draw(){
+ m.frame(data.x,data.y,data.w,data.h,{
+ title:data.title,
+ fill:data.fill
+ });
+ data.controls.forEach(ctrl=>ctrl.draw(data.x,data.y));
+ }
+ function onInput(ch,code){
+ switch (code){
+ case m.keyCode.ANNULATION:
+ if (data.onClose)
+ data.onClose(this);
+ break;
+ case m.keyCode.RETOUR:
+ if (focs && focs.prev)
+ focs.prev();
+ else
+ prev();
+ break;
+ case m.keyCode.SUITE:
+ if (focs && focs.next)
+ focs.next();
+ else
+ next();
+ break;
+ case m.keyCode.REPETITION:
+ next();
+ break;
+ default:
+ if (focs && focs.onInput)
+ focs.onInput(ch,code);
+ }
+ }
+ function next(){//next control (tab)
+ if (foca.length==0)
+ return;
+ foci++;
+ if (foci>=foca.length)
+ foci=0;
+ focs=foca[foci];
+ draw();
+ }
+ function prev(){//previous control (shift tab)
+ if (foca.length==0)
+ return;
+ foci--;
+ if (foci<0)
+ foci=foca.length-1;
+ focs=foca[foci];
+ draw();
+ }
+ for( var i in data.controls){
+ data.controls[i].parent=this;
+ if (data.controls[i].tabstop)
+ foca.push(data.controls[i]);
+ }
+
+ next();
+ this.draw=draw;
+ this.onInput=onInput;
+ this.next=next;
+ this.prev=prev;
+}
+
+function List(api,m,data){
+ if (!data)
+ throw new Error("Missing controller");
+ if (!data.items || data.items.length==0)
+ throw new Error("No items");
+
+ function draw(ox,oy){
+ for(var i=0;i=data.items.length)
+ data.sel=0;
+ draw();
+ }
+ function prev(){
+ data.sel--;
+ if (data.sel<0)
+ data.sel=data.items.length-1;
+ draw();
+ }
+ function onInput(ch,code){
+ switch(code){
+ case m.keyCode.RETOUR:
+ prev();
+ break;
+ case m.keyCode.SUITE:
+ next();
+ break;
+ case m.keyCode.ENVOI:
+ if (data.onSelect)
+ data.onSelect(this,data.items[data.sel].id);
+ break;
+ }
+ }
+
+ if (!data.sel)
+ data.sel=0;
+ this.draw=draw;
+ this.next=next;
+ this.prev=prev;
+ this.onInput=onInput;
+}
+List.prototype.tabstop=true;
+
+function Text(api,m,data){
+ function draw(ox,oy){
+ }
+
+ this.draw=draw;
+}
+//
+
+function APP_Selector(api,m){
+ this.go=()=>{
+ var applst=[],len=0,apps=api.getApps();
+ for(var k in apps){
+ if (apps[k].prototype.hide)
+ continue;
+ var itm={id:k,name:apps[k].prototype.name()};
+ applst.push(itm);
+ if (itm.name.length > len)
+ len=itm.name.length;
+ }
+ return new Window(api,m,{x:2,y:2,w:len+1,h:applst.length+1,controls:[
+ new List(api,m,{x:1,y:1,w:len,h:applst.length,items:applst,
+ onSelect:(sl,id)=>{
+ api.selectApp(id);
+ }})
+ ]});
+ };
+}
+APP_Selector.prototype.id=()=>"selector";
+APP_Selector.prototype.name=()=>DinOS.name();
+APP_Selector.prototype.hide=true; //Hide from selector
+
+function APP_DinOSCon(api,m){
+ var cmd="";
+ function motd(){
+ var motds=["NT significa No Testado",
+//"Los ordenadores son como los dioses del Antiguo Testamento, repletos de reglas y faltos de piedad",
+"Un idiota con un ordenador es\n\rmás rápido y mejor idiota.",
+//"UNIX es muy simple, lo que sucede es que solo un genio puede entender su\r\nsimplicidad.",
+"Creo que tal vez haya mercado para cinco ordenadores.\r\n-Thomas Watson, presidente de IBM, 1943-",
+//"¿Windows95 poco util? ¿Y con que te crees que juega ahora el perro?",
+"Y cuando todo lo demas falle, lea las instrucciones.",
+"Yo se lo que es trabajar duro...\r\nlo he visto!",
+"Soy shareware. A mi que me registren!!.",
+"Supongamos que no hay situaciones hipoteticas...",
+//"Quien es el General Failure y que hace\r\nleyendo mi disco duro!",
+"¡¡ QQuuiittaa eell LLooccaall EEcchhoo,, MMaannoolloo !!",
+"Se dan clases de ensamblador por\r\nprofesor nativo!",
+"Mis programas NO tienen errores, tienen caracteristicas involuntarias.",
+ ];
+ m.println(motds[Math.floor(Math.random()*motds.length)]);
+ }
+ function prompt(){
+ m.write(">");
+ m.showCursor(true);
+ }
+ this.go=()=>{
+ m.clear();
+ motd();
+ prompt();
+ };
+ this.onInput=(data,code)=>{
+ if (!data){
+ print("MiniCON: "+code);
+ if (code==m.keyCode.CORRECTION && cmd.length>0){
+ m.write([8,32,8]);
+ cmd=cmd.substr(0,cmd.length-1);
+ return;
+ }
+ if (code==m.keyCode.ENVOI)
+ data="\n";
+ }
+ cmd+=data;
+ var idx = cmd.indexOf("\n");
+ while (idx>=0) {
+ m.showCursor(false).write("\r\n");
+ var line = cmd.substr(0,idx);
+ print(" "+line);
+ cmd = cmd.substr(idx+1);
+ if (line.length>0)
+ try{
+ var out = eval(line);
+ if (out!==undefined){
+ print(out);
+ Serial1.println("="+out);
+ }
+ }catch(e){
+ print(e);
+ Serial1.println(e);
+ }
+ prompt();
+ idx = cmd.indexOf("\r");
+ }
+ };
+}
+APP_DinOSCon.prototype.id=()=>"js";
+APP_DinOSCon.prototype.name=()=>"Javascript console";
+
+function APP_CONF(api,m){
+ function showWifi(){
+ api.show(
+ new Window(api,m,{
+ x:13,y:2,w:26,h:20,title:"Wifi",
+ controls:[new Text(api,m,{
+ x:1,y:1,w:10,h:1,
+ text:"Hola",
+ })
+ ]
+ })
+ );
+ }
+
+ this.go=()=>{
+ return new Window(api,m,{
+ x:2,y:2,w:8,h:15,
+ controls:[new List(api,m,{
+ x:1,y:1,w:7,h:14,
+ items:[{id:"wifi",name:"Wifi"}],
+ onSelect:(sl,id)=>{
+ switch (id){
+ case "wifi":
+ showWifi();
+ break;
+ }
+ }
+ })]
+ });
+ };
+}
+APP_CONF.prototype.id=()=>"conf";
+APP_CONF.prototype.name=()=>"Configuracion";
+
+function NETCON(){
+ require("net").createServer(function (connection) {
+ connection.pipe(LoopbackA);
+ LoopbackA.pipe(connection);
+ LoopbackB.setConsole();
+ }).listen(23);
+}
+
+function WIFI(ssid,password){
+ if (!ssid){
+ m.println("Usar: WIFI(ssid,password)");
+ return;
+ }
+ var WIFI_OPTIONS = { password : password };
+ var wifi = require("Wifi");
+ m.println("Connecting to: "+ssid);
+ wifi.connect(ssid, WIFI_OPTIONS, function(err) {
+ if (err) {
+ console.log("Connection error: "+err);
+ m.println("Connection error: "+err);
+ return;
+ }
+ console.log("Connected!");
+ m.println("Connected!");
+ wifi.getIP((e,i)=>{
+ print("ip: "+i.ip+" mac:"+i.mac);
+ m.println("ip: "+i.ip+" mac:"+i.mac);
+ });
+ //getPage();
+ });
+}
+
+function WIFIHABA(){
+ WIFI("PajueloGodoy","WifiCasa55");
+}
+
+function SCAN(){
+ var wifi = require("Wifi");
+ m.println("Escaneando...");
+ wifi.scan((err,list)=>{
+ if (err){
+ m.println(err);
+ print(err);
+ return;
+ }
+ print(list);
+ for(var i in list){
+ var net=list[i];
+ m.println(" "+net.ssid+" ("+net.authMode+") ");
+ }
+ });
+}
+
+var dinOS;
+
+function bootDinOS(){
+ dinOS=new DinOS();
+ dinOS.registerApp(APP_Selector);
+ dinOS.registerApp(APP_DinOSCon);
+ dinOS.registerApp(APP_CONF);
+ dinOS.selectApp(APP_DinOSCon.prototype.id());
+ print(process.memory());
+}
+
+E.on('init', function() {
+ USB.setConsole();
+ setTimeout(bootDinOS,700);
+});
+
+//bootDinOS();
+
+//var m=new Minitel();
+print(process.memory());
\ No newline at end of file
diff --git a/Minitel.js b/Minitel.js
new file mode 100644
index 0000000..3bc2f78
--- /dev/null
+++ b/Minitel.js
@@ -0,0 +1,313 @@
+function Minitel(speed, tx, rx) {
+ const DEBUG_INPUT = true;
+ var term = {};
+ var s = Serial1;
+ var input = "";
+ const keyCode = {
+ ENVOI: 65,
+ GUIDE: 68,
+ CORRECTION: 71,
+ RETOUR: 66,
+ SUITE: 72,
+ ANNULATION: 69,
+ REPETITION: 67,
+ SOMMAIRE: 70,
+ FIN: 89,
+ };
+
+ /**
+ * INPUT HANDLING
+ */
+ var callInput = ((ch, code) => {
+ if (this.onInput)
+ this.onInput(ch, code);
+ else
+ print("INPUT DISCONNECTED!!");
+ }).bind(this);
+
+ function inputHandler(data) {
+ input += data;
+ if (input.length == 0)
+ return;
+ if (input.charCodeAt(0) == 19) {
+ if (input.length < 2)
+ return;
+ switch (input.charCodeAt(1)) {
+ case keyCode.ENVOI:
+ case keyCode.GUIDE:
+ case keyCode.CORRECTION:
+ case keyCode.RETOUR:
+ case keyCode.SUITE:
+ case keyCode.ANNULATION:
+ case keyCode.REPETITION:
+ case keyCode.SOMMAIRE:
+ callInput("", input.charCodeAt(1));
+ input = input.substr(2);
+ break;
+ case keyCode.FIN:
+ if (input.length < 4)
+ return;
+ if (input.charCodeAt(2) == 19 && input.charCodeAt(3) == keyCode.FIN) {
+ callInput("", input.charCodeAt(1));
+ input = input.substr(4);
+ return;
+ } else
+ input = input.substr(2);
+ }
+ } else {
+ callInput(input[0], 0);
+ input = input.substr(1);
+ }
+ }
+
+ /**
+ * PROTOCOL
+ */
+ function defaults() {
+ text();
+ clear();
+ bgColor(0);
+ fgColor(7);
+ return this;
+ }
+ function clear() {
+ s.write(12);
+ return this;
+ }
+ function graphics() {
+ s.write(14);
+ term.gm = true;
+ return this;
+ }
+ function text() {
+ s.write(15);
+ term.gm = false;
+ return this;
+ }
+ function home() { //CR / Line home
+ s.write(13);
+ return this;
+ }
+ function end() { //Line end
+ s.write(24);
+ return this;
+ }
+
+ //COLORS
+ function bgColor(col) {
+ if (col < 0 || col > 7)
+ return;
+ s.write([27, 80 + col]);
+ term.bg = col;
+ return this;
+ }
+ function fgColor(col) {
+ if (col < 0 || col > 7)
+ return;
+ s.write([27, 64 + col]);
+ term.fg = col;
+ return this;
+ }
+ function blink(blink) {
+ s.write([27, blink ? 72 : 73]);
+ return this;
+ }
+
+ //CURSOR
+ function locate(x, y) {
+ s.write([31, 64 + y, 64 + x]);
+ return this;
+ }
+ function showCursor(show) {
+ term.cursor = show;
+ s.write(show ? 17 : 20);
+ return this;
+ }
+
+ //TEXT
+ function mprint(str) {
+ s.print(str);
+ return this;
+ }
+ function println(str) {
+ s.println(str === undefined ? "" : str);
+ return this;
+ }
+ function write(data) {
+ s.write(data);
+ return this;
+ }
+ function font(mode) {
+ s.write([27, mode]);
+ return this;
+ }
+ function repeat(times) {
+ s.write([18, times + 64]);
+ return this;
+ }
+
+ /**
+ * GRAPHICS
+ */
+ var start = ((x, y, opts) => {
+ this.locate(x, y).graphics();
+ if (opts && opts.fg)
+ this.fgColor(opts.fg);
+ if (opts && opts.bg)
+ this.bgColor(opts.bg);
+ return this;
+ }).bind(this);
+
+ function gCurStart() {
+ if (term.cursor)
+ this.write(20);
+ }
+
+ function gCurEnd() {
+ if (term.cursor)
+ this.write(17);
+ }
+
+ function graph(x, y, val, opts) {
+ start(x, y, opts).write(val).text();
+ return this;
+ }
+
+ function frame(x, y, w, h, opts) {
+ gCurStart();
+ var i;
+ start(x, y, opts).write(118);
+ if (opts && opts.title) {
+ var s = String(opts.title);
+ this.write(53).text();
+ if (opts && opts.titleFg);
+ this.fgColor(opts.titleFg);
+ if (opts && opts.titleBg);
+ this.fgColor(opts.titleBg);
+ this.write(s);
+ this.graphics().write(106);
+ this.write(115).repeat(w - (s.length + 4));
+ } else
+ this.write(115).repeat(w - 2);
+ this.write(121);
+ if (opts && opts.fill) {
+ for (i = 1; i < h; i++)
+ start(x, y + i, opts).write([53, 32]).repeat(w - 2).write(106);
+ } else
+ for (i = 1; i < h; i++) {
+ start(x, y + i, opts).write(53);
+ start(x + w, y + i, opts).write(106);
+ }
+ start(x, y + h, opts).write([103, 115]).repeat(w - 2).write(59).text();
+ gCurEnd();
+ return this;
+ }
+
+ function fill(x, y, w, h, ch, opts) {
+ if (w <= 0 || h <= 0)
+ return;
+ gCurStart();
+ if (ch === undefined)
+ ch = 32;
+ for (var yy = y; yy < y + h; yy++)
+ start(x, yy, opts).write(ch).repeat(w - 1);
+ gCurEnd();
+ }
+
+ // *** PUBLISHING ***
+
+
+ /**
+ * CONSTRUCTOR
+ */
+ (() => {
+ if (!speed)
+ speed = 1200;
+ if (!tx)
+ tx = B6;
+ if (!rx)
+ rx = B7;
+ print(`Configuring ESPruino in tx: ${tx}, rx: ${rx}, speed: ${speed}`);
+ s.setup(speed, {
+ tx: tx,
+ rx: rx,
+ bytesize: 7,
+ parity: "e",
+ errors: true
+ });
+ s.on('parity', function () {
+ console.log("[!!] Parity error");
+ });
+ s.on('framing', function () {
+ console.log("[!!] Framing error");
+ });
+ s.on('data', data => {
+ if (DEBUG_INPUT) {
+ var s = " (" + data.length + ") [";
+ for (var i in data) {
+ if (i > 0)
+ s += ", ";
+ s += data.charCodeAt(i);
+ }
+ print(s + "]");
+ }
+ //***************
+ inputHandler(data);
+ });
+ })();
+
+ // *** PUBLSHING ***
+ this.defaults = defaults;
+ this.reset = reset;
+ this.clear = clear;
+ this.graphics = graphics;
+ this.text = text;
+
+ //COLORS
+ this.bgColor = bgColor;
+ this.fgColor = fgColor;
+ this.blink = blink;
+ this.color = {
+ black: 0,
+ red: 1,
+ green: 2,
+ yellow: 3,
+ magenta: 4,
+ blue: 5,
+ cyan: 6,
+ white: 7
+ };
+
+ //CURSOR
+ this.locate = locate;
+ this.showCursor = showCursor;
+
+ //TEXT
+ this.print = mprint;
+ this.println = println;
+ this.write = write;
+ this.font = font;
+ this.font.regular = 76;
+ this.font.doubleHeight = 77;
+ this.font.doubleWidth = 78;
+ this.font.double = 79;
+ this.font.double = 79;
+ this.font.normal = 92;
+ this.font.inverted = 93;
+ this.font.transparent = 94;
+ this.font.mask = 88;
+ this.font.demask = 95;
+ this.repeat = repeat;
+
+ // INPUT
+ this.keyCode = keyCode;
+
+ // GRAPHICS
+ this.graph = graph;
+ this.frame = frame;
+ this.fill = fill;
+ //EVENTS
+ this.onInput = undefined;
+}
+
+module.exports = Minitel;
\ No newline at end of file