├── LICENSE ├── README.md ├── fs └── init.js ├── mos.yml └── src └── main.c /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Cesanta Software Limited 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basic demo for M5stack 2 | -------------------------------------------------------------------------------- /fs/init.js: -------------------------------------------------------------------------------- 1 | load('api_azure.js'); 2 | load('api_config.js'); 3 | load('api_events.js'); 4 | load('api_gpio.js'); 5 | load('api_ili9341_spi.js'); 6 | load('api_mqtt.js'); 7 | load('api_net.js'); 8 | load('api_rpc.js'); 9 | load('api_shadow.js'); 10 | load('api_sys.js'); 11 | load('api_timer.js'); 12 | load('api_watson.js'); 13 | 14 | let BTN1 = 39, BTN2 = 38, BTN3 = 37; 15 | let LCD_BACKLIGHT = 32; 16 | 17 | let devID = Cfg.get('device.id'); 18 | let greeting = ''; 19 | let btnc = [-1, 0, 0, 0]; 20 | let netStatus = null; 21 | let cloudName = null; 22 | let cloudConnected = false; 23 | 24 | if (Cfg.get('azure.enable')) { 25 | cloudName = 'Azure'; 26 | Event.addGroupHandler(Azure.EVENT_GRP, function(ev, evdata, arg) { 27 | if (ev === Azure.EV_CONNECT) { 28 | cloudConnected = true; 29 | } else if (ev === Azure.EV_C2D) { 30 | let c2d = Azure.getC2DArg(evdata); 31 | print('C2D message:', c2d.props, c2d.body); 32 | greeting = ''; 33 | printGreeting(); 34 | greeting = c2d.body; 35 | printGreeting(); 36 | } else if (ev === Azure.EV_CLOSE) { 37 | cloudConnected = false; 38 | } 39 | }, null); 40 | } else if (Cfg.get('gcp.enable')) { 41 | cloudName = 'GCP'; 42 | } else if (Cfg.get('watson.enable')) { 43 | cloudName = 'Watson'; 44 | Event.addGroupHandler(Watson.EVENT_GRP, function(ev, evdata, arg) { 45 | if (ev === Watson.EV_CONNECT) { 46 | cloudConnected = true; 47 | watsonReportBtnStatus(); 48 | } else if (ev === Watson.EV_CLOSE) { 49 | cloudConnected = false; 50 | } 51 | }, null); 52 | } else if (Cfg.get('dash.enable')) { 53 | cloudName = 'Mongoose'; 54 | } else if (Cfg.get('mqtt.enable')) { 55 | if (Cfg.get('mqtt.server').indexOf('amazonaws') > 0) { 56 | cloudName = 'Amazon'; 57 | } else { 58 | cloudName = 'MQTT'; 59 | } 60 | } 61 | 62 | MQTT.setEventHandler(function(conn, ev, edata) { 63 | if (cloudName && cloudName !== 'Azure' && cloudName !== 'Watson') { 64 | if (ev === MQTT.EV_CONNACK) { 65 | cloudConnected = true; 66 | } else if (ev === MQTT.EV_CLOSE) { 67 | cloudConnected = false; 68 | } 69 | } 70 | }, null); 71 | 72 | let getFont = ffi('void* get_font(int)'); 73 | let fonts = [getFont(0), getFont(1), getFont(2), getFont(3)]; 74 | function clearLine(n) { 75 | ILI9341.setFgColor565(ILI9341.BLACK); 76 | ILI9341.fillRect(0, ILI9341.line(n), ILI9341.getScreenWidth(), ILI9341.getMaxFontHeight()); 77 | ILI9341.setFgColor565(ILI9341.WHITE); 78 | } 79 | 80 | function printCentered(xc, y, text) { 81 | ILI9341.print(xc - ILI9341.getStringWidth(text) / 2, y, text); 82 | } 83 | 84 | // Display orientation settings. 85 | // See https://github.com/mongoose-os-libs/ili9341-spi#orientations for details. 86 | let M5STACK_LANDSCAPE = 0x0; // Buttons at the bottom, 320x240 87 | let M5STACK_PORTRAIT = 0xa0; // Buttons on the left, 240x320 88 | let M5STACK_LANDSCAPE_FLIP = 0xd0; // Buttons at the top, 320x240 89 | let M5STACK_PORTRAIT_FLIP = 0x60; // Buttons on the right, 240x320 90 | 91 | GPIO.set_mode(LCD_BACKLIGHT, GPIO.MODE_OUTPUT); 92 | GPIO.write(LCD_BACKLIGHT, 1); 93 | ILI9341.setOrientation(M5STACK_LANDSCAPE, 320, 240); 94 | ILI9341.setBgColor(0, 0, 0); 95 | ILI9341.fillScreen(); 96 | ILI9341.setFont(fonts[1]); 97 | ILI9341.setFgColor565(ILI9341.WHITE); 98 | printCentered(ILI9341.getScreenWidth() / 2, ILI9341.line(0), devID); 99 | 100 | let formatTime = ffi('char *format_time(char *)'); 101 | 102 | function printNetStatus() { 103 | if (!netStatus) netStatus = 'not configured'; 104 | ILI9341.setFont(fonts[1]); 105 | ILI9341.setFgColor565(ILI9341.WHITE); 106 | ILI9341.print(5, ILI9341.line(1), 'WiFi: ' + netStatus + ' '); 107 | } 108 | 109 | function printCloudStatus() { 110 | ILI9341.setFont(fonts[1]); 111 | ILI9341.setFgColor565(ILI9341.WHITE); 112 | let cs; 113 | if (cloudName) { 114 | cs = cloudName + ', ' + (cloudConnected ? 'connected' : 'not connected'); 115 | } else { 116 | cs = 'not configured'; 117 | } 118 | ILI9341.print(5, ILI9341.line(2), 'Cloud: ' + cs + ' '); 119 | } 120 | 121 | function printTime() { 122 | ILI9341.setFont(fonts[1]); 123 | ILI9341.setFgColor565(ILI9341.WHITE); 124 | let ts = formatTime('%H:%M:%S'); 125 | ILI9341.print(5, ILI9341.line(3), 'Time: ' + (ts ? ts : 'not set') + ' '); 126 | } 127 | 128 | function printGreeting() { 129 | ILI9341.setFont(fonts[1]); 130 | ILI9341.setFgColor565(ILI9341.WHITE); 131 | if (greeting) { 132 | printCentered(160, ILI9341.line(5), greeting); 133 | } else { 134 | clearLine(5); 135 | } 136 | } 137 | 138 | function printBtnStatus() { 139 | ILI9341.setFont(fonts[2]); 140 | ILI9341.setFgColor565(ILI9341.WHITE); 141 | let y = ILI9341.line(-1); 142 | printCentered(65, y, JSON.stringify(btnc[1])) 143 | printCentered(160, y, JSON.stringify(btnc[2])) 144 | printCentered(255, y, JSON.stringify(btnc[3])) 145 | } 146 | 147 | function printStatus() { 148 | printNetStatus(); 149 | printCloudStatus(); 150 | printTime() 151 | printGreeting() 152 | printBtnStatus(); 153 | } 154 | 155 | // Monitor network connectivity. 156 | Event.addGroupHandler(Net.EVENT_GRP, function(ev, evdata, arg) { 157 | if (ev === Net.STATUS_DISCONNECTED) { 158 | netStatus = 'not connected'; 159 | } else if (ev === Net.STATUS_CONNECTING) { 160 | netStatus = 'connecting'; 161 | } else if (ev === Net.STATUS_GOT_IP) { 162 | netStatus = 'connected'; 163 | } 164 | printNetStatus(); 165 | }, null); 166 | 167 | function watsonReportBtnStatus() { 168 | // Make sure BTN1 is always reported first, to make the QuickStart graph deterministic. 169 | Watson.sendEventJSON('btnStatus', {d:{btn1: btnc[1]}}); 170 | Watson.sendEventJSON('btnStatus', {d:{btn2: btnc[2], btn3: btnc[3]}}); 171 | } 172 | 173 | function reportBtnPress(n) { 174 | btnc[n] = btnc[n] + 1; 175 | 176 | let btns = JSON.stringify(n); 177 | let msg = JSON.stringify({btn: n, cnt: btnc[n]}); 178 | if (cloudName === 'Azure') { 179 | Azure.sendD2CMsg('btn=' + btns, msg); 180 | } else if (cloudName === 'Watson') { 181 | watsonReportBtnStatus(); 182 | } else { 183 | MQTT.pub(devID + '/messages', msg); 184 | } 185 | let upd = {}; 186 | upd["btn" + btns] = btnc[n]; 187 | Shadow.update(0, upd); 188 | printBtnStatus(); 189 | } 190 | 191 | GPIO.set_button_handler(BTN1, GPIO.PULL_UP, GPIO.INT_EDGE_NEG, 20, function() { reportBtnPress(1) }, null); 192 | GPIO.set_button_handler(BTN2, GPIO.PULL_UP, GPIO.INT_EDGE_NEG, 20, function() { reportBtnPress(2) }, null); 193 | GPIO.set_button_handler(BTN3, GPIO.PULL_UP, GPIO.INT_EDGE_NEG, 20, function() { reportBtnPress(3) }, null); 194 | RPC.addHandler('M5.SetGreeting', function(args) { 195 | if (args.greeting === undefined) { 196 | return {"error": 400, "message": "greeting not specified"}; 197 | } 198 | ILI9341.setFont(fonts[1]); 199 | greeting = ''; 200 | printGreeting(); 201 | greeting = args.greeting; 202 | printGreeting(); 203 | }); 204 | 205 | Shadow.addHandler(function(ev, obj) { 206 | print(ev, JSON.stringify(obj)); 207 | if (ev === 'CONNECTED') { 208 | // Nothing. A GET will be delivered shortly. 209 | } else if (ev === 'GET_ACCEPTED' && obj.reported !== undefined ) { 210 | btnc[1] = obj.reported.btn1 || 0; 211 | btnc[2] = obj.reported.btn2 || 0; 212 | btnc[3] = obj.reported.btn3 || 0; 213 | if (obj.desired !== undefined && obj.desired.greeting !== undefined) { 214 | greeting = obj.desired.greeting; 215 | } 216 | } else if (ev === 'UPDATE_DELTA') { 217 | if (obj.greeting !== undefined) greeting = obj.greeting; 218 | } 219 | }); 220 | 221 | printStatus(); 222 | Timer.set(1000 /* 1 sec */, Timer.REPEAT, printStatus, null); 223 | -------------------------------------------------------------------------------- /mos.yml: -------------------------------------------------------------------------------- 1 | author: mongoose-os 2 | description: A JS-enabled demo application for M5stack 3 | platform: esp32 4 | version: 1.0 5 | libs_version: ${mos.version} 6 | modules_version: ${mos.version} 7 | mongoose_os_version: ${mos.version} 8 | 9 | config_schema: 10 | - ["device.id", "M5stack-??????"] 11 | - ["i2c.enable", true] 12 | - ["i2c.sda_gpio", 21] 13 | - ["i2c.scl_gpio", 22] 14 | # M5-Core display 15 | - ["spi.enable", true] 16 | - ["spi.cs0_gpio", 14] 17 | - ["ili9341.spi_freq", 40000000] 18 | - ["ili9341.dc_pin", 27] 19 | - ["ili9341.rst_pin", 33] 20 | - ["ili9341.width", 240] 21 | - ["ili9341.height", 320] 22 | 23 | sources: 24 | - src 25 | 26 | filesystem: 27 | - fs 28 | 29 | libs: 30 | - origin: https://github.com/mongoose-os-libs/aws 31 | - origin: https://github.com/mongoose-os-libs/azure 32 | - origin: https://github.com/mongoose-os-libs/boards 33 | - origin: https://github.com/mongoose-os-libs/ca-bundle 34 | - origin: https://github.com/mongoose-os-libs/dash 35 | - origin: https://github.com/mongoose-os-libs/gcp 36 | - origin: https://github.com/mongoose-os-libs/http-server 37 | - origin: https://github.com/mongoose-os-libs/i2c 38 | - origin: https://github.com/mongoose-os-libs/ili9341-spi 39 | - origin: https://github.com/mongoose-os-libs/mjs 40 | - origin: https://github.com/mongoose-os-libs/mqtt 41 | - origin: https://github.com/mongoose-os-libs/ota-shadow 42 | - origin: https://github.com/mongoose-os-libs/ota-http-client 43 | - origin: https://github.com/mongoose-os-libs/ota-http-server 44 | - origin: https://github.com/mongoose-os-libs/rpc-azure 45 | - origin: https://github.com/mongoose-os-libs/rpc-mqtt 46 | - origin: https://github.com/mongoose-os-libs/rpc-service-config 47 | - origin: https://github.com/mongoose-os-libs/rpc-service-fs 48 | - origin: https://github.com/mongoose-os-libs/rpc-uart 49 | - origin: https://github.com/mongoose-os-libs/spi 50 | - origin: https://github.com/mongoose-os-libs/sntp 51 | - origin: https://github.com/mongoose-os-libs/watson 52 | - origin: https://github.com/mongoose-os-libs/wifi 53 | 54 | manifest_version: 2017-09-19 55 | platforms: [ esp32 ] 56 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2018 Cesanta Software Limited 3 | * All rights reserved 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the ""License""); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an ""AS IS"" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include 19 | 20 | #include "mgos_ili9341.h" 21 | #include "fonts/FreeSerif9pt7b.h" 22 | #include "fonts/FreeSerif12pt7b.h" 23 | #include "fonts/FreeSerif18pt7b.h" 24 | #include "fonts/FreeSerif24pt7b.h" 25 | 26 | GFXfont *get_font(int n) { 27 | switch (n) { 28 | case 0: 29 | return &FreeSerif9pt7b; 30 | case 1: 31 | return &FreeSerif12pt7b; 32 | case 2: 33 | return &FreeSerif18pt7b; 34 | case 3: 35 | return &FreeSerif24pt7b; 36 | } 37 | return NULL; 38 | } 39 | 40 | const char *format_time(const char *fmt) { 41 | static char buf[32]; 42 | time_t now = time(NULL); 43 | struct tm tm; 44 | if (now < 1500000000) return NULL; 45 | if (localtime_r(&now, &tm) == NULL) return NULL; 46 | strftime(buf, sizeof(buf), fmt, &tm); 47 | return buf; 48 | } 49 | --------------------------------------------------------------------------------