├── M5Stack_BLE_client_Owon_B35T.ino ├── README.md ├── docs ├── icons.xlsx ├── m5stack.jpg └── m5stack_meter.jpg └── meter_graphics.h /M5Stack_BLE_client_Owon_B35T.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------ 3 | * "THE BEERWARE LICENSE" (Revision 42): 4 | * wrote this code. As long as you retain this 5 | * notice, you can do whatever you want with this stuff. If we 6 | * meet someday, and you think this stuff is worth it, you can 7 | * buy me a beer in return. 8 | * ------------------------------------------------------------ 9 | */ 10 | 11 | // M5Stack_BLE_client_Owon_B35T.ino 12 | // 2023 Reaper7 (tested on M5Stack) 13 | // part for B35TPlus based on https://github.com/DeanCording/owonb35 by DeanCording 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include // M5Stack lib >= 0.1.9 26 | #include "M5StackUpdater.h" // https://github.com/tobozo/M5Stack-SD-Updater 27 | 28 | #include "meter_graphics.h" 29 | 30 | //#define MYDEBUG 31 | #ifdef MYDEBUG 32 | #define DEBUG_MSG(...) Serial.printf( __VA_ARGS__ ) 33 | #else 34 | #define DEBUG_MSG(...) 35 | #endif 36 | 37 | //#define BATMETERI2C 38 | 39 | //------------------------------------------------------------------------------ 40 | //------------------------------------------------------------------------------ 41 | const char* OWONNAME = "BDM"; // OWON device name 42 | static BLEUUID serviceUUID("0000fff0-0000-1000-8000-00805f9b34fb"); // OWON service UUID 43 | static BLEUUID charnotificationUUID("0000fff4-0000-1000-8000-00805f9b34fb"); // OWON notification characteristic UUID 44 | static BLEUUID charwriteUUID("0000fff3-0000-1000-8000-00805f9b34fb"); // OWON write characteristic UUID 45 | 46 | static BLEAddress *pServerAddress; 47 | static BLERemoteCharacteristic* pRemoteCharacteristicNotify; 48 | static BLERemoteCharacteristic* pRemoteCharacteristicWrite; 49 | 50 | volatile boolean deviceBleConnected = false; // flag BLE conneted 51 | volatile boolean newBleData = false; // flag "we get new data from meter" 52 | static unsigned long lastBleNotify = 0; // timestamp "last received data from meter" in ms 53 | static unsigned long startBleScanning = 0; // timestamp when ble scan is beginning in ms 54 | 55 | const uint16_t maxWaitForNotify = 10000; // max wait time for next notify from meter in ms 56 | const uint16_t scanTime = 10; // BLE scan time in s 57 | 58 | const uint8_t meterReplySize = 14; // number of bytes in meter reply 59 | char valuechar[meterReplySize]; // meter reply buffer 60 | 61 | const uint8_t rawBufferSize = 14; // number of bytes for reading meter 62 | char rawvalbuf[rawBufferSize]; // meter reply raw buffer 63 | 64 | const uint8_t overLoadFrame[] = { 0x2b, 0x3f, 0x30, 0x3a, 0x3f }; // first 5 bytes of frame if overload 65 | 66 | const uint8_t replySizeNorm = 14; // notify size for b35t 67 | const uint8_t replySizePlus = 6; // notify size for b35tPLUS 68 | 69 | static boolean meterIsPlus = false; // flag for meter b35tPLUS 70 | static boolean meterPlusLowBat = false; // meter b35tPLUS Low Bat 71 | static boolean meterPlusLowBatLast = !meterPlusLowBat; 72 | 73 | static boolean overLoad = false; // overload flag 74 | 75 | static boolean buzzOn = false; // buzzer on 76 | 77 | static boolean firstNotify = true; // flag first notify after start or reconnect 78 | 79 | //write to OWON 80 | volatile boolean deviceBleWriteAvailable = false; // write to meter available 81 | const uint8_t buttonsMax = 6; // max buttons available on meter 82 | const char *btnName[] = { "SELECT", "RANGE", "HLD/LIG", "REL/BT", "HZ/DUTY", "MAX/MIN" }; // buttons name 83 | uint8_t btnNumber = 0x01; // initial/current button number(code) 84 | uint8_t btnShortPress = 0x01; // code for short press (one short press) 85 | uint8_t btnLongPress = 0x00; // code for long press 86 | const uint16_t btnLongPressTime = 1800; // long button press time in ms 87 | const uint8_t owonBtnArraySize = 0x02; // buffer size for write to meter 88 | uint8_t owonBtnArray[owonBtnArraySize] = {btnNumber, btnShortPress}; // output buffer for write to meter 89 | 90 | #ifdef BATMETERI2C 91 | // bat meter MAX17043 1-Cell Fuel Gauge 92 | static uint16_t batReadEvery = 10000; // read MAX17043 every 10000 sec 93 | static unsigned long batNextReadTime = 0; // time for next read MAX17043 in ms 94 | const uint8_t MAX17043ADDR=0x36; // MAX17043 i2c addr 95 | const uint8_t MAX17043CMDADDR=0xFE; // MAX17043 command register addr 96 | const uint8_t MAX17043SOCADDR=0x04; // MAX17043 soc data addr 97 | const uint8_t MAX17043VCELLADDR=0x02; // MAX17043 voltage data addr 98 | #endif 99 | //------------------------------------------------------------------------------ 100 | //------------------------------------------------------------------------------ 101 | /* 102 | 14 bytes reply 103 | ----------------------------------------------------------------------- 104 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 105 | ----------------------------------------------------------------------- 106 | |0x2b|0x33 0x36 0x32 0x33|0x20|0x34|0x31|0x00|0x40 0x80|0x24|0x0d 0x0a| 107 | ----------------------------------------------------------------------- 108 | | 0 | 1 2 3 4 | 5 | 6 | 7 | 8 | 9 10 | 11 | 12 13 | 109 | ----------------------------------------------------------------------- 110 | */ 111 | 112 | /* 113 | 1: + or - (dec 43 or 45) 114 | BYTE 0 115 | */ 116 | #define REGPLUSMINUS 0x00 117 | #define FLAGPLUS B00101011 118 | #define FLAGMINUS B00101101 119 | 120 | /* 121 | 2: Value 0000-9999 in dec 122 | BYTE 1-4 123 | */ 124 | #define REGDIG1 0x01 125 | #define REGDIG2 0x02 126 | #define REGDIG3 0x03 127 | #define REGDIG4 0x04 128 | 129 | /* 130 | 3: Just space (dec 32) 131 | BYTE 5 132 | */ 133 | 134 | /* 135 | 4: Decimal point position 136 | - dec 48 no point 137 | - dec 49 after the first number 138 | - dec 50 after the second number 139 | - dec 52 after the third number 140 | BYTE 6 141 | */ 142 | #define REGPOINT 0x06 143 | #define FLAGPOINT0 B00110000 144 | #define FLAGPOINT1 B00110001 145 | #define FLAGPOINT2 B00110010 146 | #define FLAGPOINT3 B00110100 147 | 148 | /* 149 | 5: AC or DC and Auto mode 150 | - dec 49 DC Auto mode / 51 HOLD 151 | - dec 41 AC Auto mode / 43 HOLD 152 | - dec 17 DC Manual mode / 19 HOLD 153 | - dec 09 AC Manual mode / 11 HOLD 154 | BYTE 7 155 | */ 156 | #define REGMODE 0x07 157 | #define FLAGMODENONE B00000000 //none 158 | #define FLAGMODEMAN B00000001 //manual 159 | #define FLAGMODEHOLD B00000010 //hold 160 | #define FLAGMODEREL B00000100 //relative 161 | #define FLAGMODEAC B00001000 //ac 162 | #define FLAGMODEDC B00010000 //dc 163 | #define FLAGMODEAUTO B00100000 //auto 164 | #define FLAGMODECHECK B11111111 //mask for check others 165 | 166 | /* 167 | 6: MIN MAX 168 | - dec 0 MIN MAX off 169 | - dec 16 MIN 170 | - dec 32 MAX 171 | BYTE 8 172 | */ 173 | #define REGMINMAX 0x08 174 | #define FLAGMINMAXNONE B00000000 175 | #define FLAGMIN B00010000 176 | #define FLAGMAX B00100000 177 | 178 | /* 179 | 7: Units 180 | - dec 2 0 % Duty 181 | - dec 0 1 Fahrenheit 182 | - dec 0 2 Grad 183 | - dec 0 4 nF 184 | - dec 0 8 Hz 185 | - dec 0 16 hFE 186 | - dec 0 32 Ohm 187 | - dec 32 32 kOhm 188 | - dec 16 32 MOhm 189 | - dec 128 64 uA 190 | - dec 64 64 mA 191 | - dec 0 64 A 192 | - dec 64 128 mV 193 | - dec 0 128 V 194 | BYTES 9 & 10 195 | */ 196 | #define REGSCALE 0x09 197 | #define FLAGSCALEDUTY B00000010 198 | #define FLAGSCALEDIODE B00000100 199 | #define FLAGSCALEBUZZ B00001000 200 | #define FLAGSCALEMEGA B00010000 201 | #define FLAGSCALEKILO B00100000 202 | #define FLAGSCALEMILI B01000000 203 | #define FLAGSCALEMICRO B10000000 204 | 205 | #define REGUNIT 0x0a 206 | #define FLAGUNITNONE B00000000 207 | #define FLAGUNITFAHR B00000001 208 | #define FLAGUNITGRAD B00000010 209 | #define FLAGUNITNF B00000100 210 | #define FLAGUNITHZ B00001000 211 | #define FLAGUNITHFE B00010000 212 | #define FLAGUNITOHM B00100000 213 | #define FLAGUNITAMP B01000000 214 | #define FLAGUNITVOLT B10000000 215 | 216 | /* 217 | 8: ??? 218 | */ 219 | 220 | /* 221 | 9: CR + LF 222 | */ 223 | //------------------------------------------------------------------------------ 224 | //------------------------------------------------------------------------------ 225 | uint16_t digitsFromBuff() { 226 | uint16_t outval = 0; 227 | 228 | if (valuechar[REGDIG1] >= 0x30 && valuechar[REGDIG1] <= 0x39) 229 | outval += (valuechar[REGDIG1] - 0x30) * 1000; 230 | if (valuechar[REGDIG2] >= 0x30 && valuechar[REGDIG2] <= 0x39) 231 | outval += (valuechar[REGDIG2] - 0x30) * 100; 232 | if (valuechar[REGDIG3] >= 0x30 && valuechar[REGDIG3] <= 0x39) 233 | outval += (valuechar[REGDIG3] - 0x30) * 10; 234 | if (valuechar[REGDIG4] >= 0x30 && valuechar[REGDIG4] <= 0x39) 235 | outval += (valuechar[REGDIG4] - 0x30) * 1; 236 | 237 | return (outval); 238 | } 239 | //------------------------------------------------------------------------------ 240 | float valFromDigits() { 241 | float outval = 0.0; 242 | 243 | if (overLoad == true) // if overloaded 244 | return (NAN); 245 | else { 246 | 247 | uint8_t decpoint = valuechar[REGPOINT] & 0x07; 248 | uint8_t decimal = 0; 249 | 250 | switch (decpoint) { 251 | case B001: 252 | decimal = 1; 253 | break; 254 | case B010: 255 | decimal = 2; 256 | break; 257 | case B100: 258 | decimal = 3; 259 | break; 260 | default: 261 | break; 262 | } 263 | 264 | outval = (float)digitsFromBuff() / pow(10.0, decimal); 265 | 266 | } 267 | 268 | //DEBUG_MSG("D: VALUE : %06.4f\n", outval); 269 | 270 | return (outval); 271 | } 272 | //------------------------------------------------------------------------------ 273 | void buzzCheck() { 274 | float val = valFromDigits(); 275 | 276 | if (buzzOn == true) { 277 | if ((deviceBleConnected == false) || (valuechar[REGSCALE] != FLAGSCALEBUZZ) || ((valuechar[REGSCALE] == FLAGSCALEBUZZ) && (val >= 30.0)) || (overLoad == true)) { 278 | buzzOn = false; 279 | M5.Speaker.end(); 280 | drawIcon(WBUZZPOSX, TOPROWPOSY, ICONW, ICONH, BUZZ_BMP, (valuechar[REGSCALE] & FLAGSCALEBUZZ) == FLAGSCALEBUZZ?TFT_LIGHTGREY:COLORNOTACTIVE); 281 | DEBUG_MSG("D: BUZZ OFF\n"); 282 | } 283 | } else { 284 | if ((deviceBleConnected == true) && (valuechar[REGSCALE] == FLAGSCALEBUZZ)) { 285 | if (val < 30.0) { 286 | buzzOn = true; 287 | M5.Speaker.tone(990); 288 | drawIcon(WBUZZPOSX, TOPROWPOSY, ICONW, ICONH, BUZZ_BMP, COLORICONBUZZ); 289 | DEBUG_MSG("D: BUZZ ON\n"); 290 | } 291 | } 292 | } 293 | } 294 | //------------------------------------------------------------------------------ 295 | //------------------------------------------------------------------------------ 296 | void drawIcon(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, const uint8_t* data, uint16_t color) { 297 | M5.Lcd.setBitmapColor(color, BACKGROUND); 298 | M5.Lcd.pushImage((int32_t)x0, (int32_t)y0, (uint32_t)w, (uint32_t)h, const_cast(data), 0, false); 299 | } 300 | //------------------------------------------------------------------------------ 301 | void batCheckDraw() { 302 | //based on soc 303 | double soc = 0; 304 | #ifdef BATMETERI2C 305 | DEBUG_MSG("I: BAT check..."); 306 | Wire.beginTransmission(MAX17043ADDR); // get SoC 307 | Wire.write(MAX17043SOCADDR); 308 | Wire.endTransmission(false); 309 | Wire.requestFrom(MAX17043ADDR, (uint8_t)2); 310 | soc = Wire.read() + (double) Wire.read() / 256; 311 | DEBUG_MSG(" SoC: %6.2f%%\n", soc); 312 | M5.Lcd.fillRect(WACCUPOSX + 2, TOPROWPOSY + 5, ACCUSCALW, ACCUSCALH, soc>75?COLORICONACCU:COLORNOTACTIVE); 313 | M5.Lcd.fillRect(WACCUPOSX + 5, TOPROWPOSY + 5, ACCUSCALW, ACCUSCALH, soc>50?COLORICONACCU:COLORNOTACTIVE); 314 | M5.Lcd.fillRect(WACCUPOSX + 8, TOPROWPOSY + 5, ACCUSCALW, ACCUSCALH, soc>25?COLORICONACCU:COLORNOTACTIVE); 315 | M5.Lcd.fillRect(WACCUPOSX + 11, TOPROWPOSY + 5, ACCUSCALW, ACCUSCALH, soc>5?COLORICONACCU:COLORNOTACTIVE); 316 | drawIcon(WACCUPOSX, TOPROWPOSY, ICONW, ICONH, ACCU_BMP, soc>0?COLORICONACCU:COLORNOTACTIVE); 317 | #else 318 | if (meterIsPlus) { // if meter is b35tPLUS 319 | if (meterPlusLowBat) // if BAT LOW 320 | soc = 1; 321 | drawIcon(WACCUPOSX, TOPROWPOSY, ICONW, ICONH, ACCU_BMP, soc>0?COLORICONBAT:COLORICONACCU); 322 | } else { 323 | drawIcon(WACCUPOSX, TOPROWPOSY, ICONW, ICONH, ACCU_BMP, soc>0?COLORICONACCU:COLORNOTACTIVE); 324 | } 325 | #endif 326 | /* 327 | //based on voltage 328 | double volt = 0; 329 | DEBUG_MSG("I: BAT check..."); 330 | Wire.beginTransmission(MAX17043ADDR); // get voltage 331 | Wire.write(MAX17043VCELLADDR); 332 | Wire.endTransmission(false); 333 | Wire.requestFrom(MAX17043ADDR, (uint8_t)2); 334 | volt = ( (Wire.read() << 4) + (Wire.read() >> 4) ) * 0.00125; 335 | DEBUG_MSG(" Volt: %4.2fV\n", volt); 336 | // >3.3 = 1 337 | // >3.5 = 2 338 | // >3.7 = 3 339 | // >3.9 = 4 340 | M5.Lcd.fillRect(WACCUPOSX + 2, TOPROWPOSY + 5, ACCUSCALW, ACCUSCALH, volt>3.9?TFT_GREEN:COLORNOTACTIVE); 341 | M5.Lcd.fillRect(WACCUPOSX + 5, TOPROWPOSY + 5, ACCUSCALW, ACCUSCALH, volt>3.7?TFT_GREEN:COLORNOTACTIVE); 342 | M5.Lcd.fillRect(WACCUPOSX + 8, TOPROWPOSY + 5, ACCUSCALW, ACCUSCALH, volt>3.5?TFT_GREEN:COLORNOTACTIVE); 343 | M5.Lcd.fillRect(WACCUPOSX + 11, TOPROWPOSY + 5, ACCUSCALW, ACCUSCALH, volt>3.3?TFT_GREEN:COLORNOTACTIVE); 344 | drawIcon(WACCUPOSX, TOPROWPOSY, ICONW, ICONH, ACCU_BMP, volt>0?COLORICONACCU:COLORNOTACTIVE); 345 | */ 346 | } 347 | //------------------------------------------------------------------------------ 348 | void drawButtons() { 349 | M5.Lcd.setTextSize(2); 350 | M5.Lcd.setTextColor(BACKGROUND); 351 | 352 | M5.Lcd.fillRoundRect(BTNAXPOS, M5.Lcd.height()-BTNYPOSFROMBOTTOM, BTNAW, BTNH, 3, deviceBleWriteAvailable == true?FONTCOLORVALUE:COLORNOTACTIVE); 353 | M5.Lcd.fillRoundRect(BTNBXPOS, M5.Lcd.height()-BTNYPOSFROMBOTTOM, BTNBW, BTNH, 3, deviceBleWriteAvailable == true?FONTCOLORVALUE:COLORNOTACTIVE); 354 | M5.Lcd.fillRoundRect(BTNCXPOS, M5.Lcd.height()-BTNYPOSFROMBOTTOM, BTNCW, BTNH, 3, deviceBleWriteAvailable == true?FONTCOLORVALUE:COLORNOTACTIVE); 355 | 356 | M5.Lcd.drawCentreString("<", BTNAXPOS + (BTNAW/2), M5.Lcd.height() - BTNYTEXTPOSFROMBOTTOM, 1); 357 | M5.Lcd.drawCentreString(btnName[btnNumber-1], BTNBXPOS + (BTNBW/2), M5.Lcd.height() - BTNYTEXTPOSFROMBOTTOM, 1); 358 | M5.Lcd.drawCentreString(">", BTNCXPOS + (BTNCW/2), M5.Lcd.height() - BTNYTEXTPOSFROMBOTTOM, 1); 359 | } 360 | //------------------------------------------------------------------------------ 361 | void drawBarGraph(bool active = true) { 362 | uint16_t digall=0; 363 | 364 | if (active == false) { 365 | M5.Lcd.fillRect(BARGRAPHPOSX, BARGRAPHPOSY, TFT_HEIGHT - 70, 2, COLORNOTACTIVE); // bargraph bottom init scale 366 | } else { 367 | digall = digitsFromBuff(); 368 | } 369 | 370 | uint16_t mapval = map(digall,0,6000,0,240); 371 | uint16_t CURCOL; 372 | 373 | for(uint16_t i = 0; i <= 240; i++) { 374 | if (i <= mapval && active == true) 375 | CURCOL = TFT_WHITE; 376 | else 377 | CURCOL = COLORNOTACTIVE; 378 | 379 | if (i%4==0) { 380 | if (i%5==0) { 381 | if (i%40==0) 382 | M5.Lcd.drawFastVLine(BARGRAPHADDX+i, BARGRAPHYA, 20, CURCOL); 383 | else 384 | M5.Lcd.drawFastVLine(BARGRAPHADDX+i, BARGRAPHYB, 15, CURCOL); 385 | } else { 386 | M5.Lcd.drawFastVLine(BARGRAPHADDX+i, BARGRAPHYC, 10, CURCOL); 387 | } 388 | } 389 | } 390 | } 391 | //------------------------------------------------------------------------------ 392 | void displayShow(bool active = false) { 393 | drawIcon(WBLEPOSX, TOPROWPOSY, ICONW, ICONH, BLE_BMP, deviceBleConnected == true?COLORICONBLE:COLORNOTACTIVE); 394 | drawIcon(WAUTOPOSX, TOPROWPOSY, ICONW*2, ICONH, AUTO_BMP, active == true?COLORICONAUTO:COLORNOTACTIVE); 395 | drawIcon(WMAXPOSX, TOPROWPOSY, ICONW*2, ICONH, MAX_BMP, active == true?COLORICONMAX:COLORNOTACTIVE); 396 | drawIcon(WMINPOSX, TOPROWPOSY, ICONW*2, ICONH, MIN_BMP, active == true?COLORICONMIN:COLORNOTACTIVE); 397 | drawIcon(WHOLDPOSX, TOPROWPOSY, ICONW, ICONH, HOLD_BMP, active == true?COLORICONHOLD:COLORNOTACTIVE); 398 | drawIcon(WRELPOSX, TOPROWPOSY, ICONW, ICONH, REL_BMP, active == true?COLORICONREL:COLORNOTACTIVE); 399 | drawIcon(WDIODEPOSX, TOPROWPOSY, ICONW, ICONH, DIODE_BMP, active == true?COLORICONDIODE:COLORNOTACTIVE); 400 | drawIcon(WBUZZPOSX, TOPROWPOSY, ICONW, ICONH, BUZZ_BMP, active == true?COLORICONBUZZ:COLORNOTACTIVE); 401 | drawIcon(WHVPOSX, TOPROWPOSY, ICONW, ICONH, HV_BMP, active == true?COLORICONHV:COLORNOTACTIVE); 402 | 403 | drawIcon(DCPOSX, DCPOSY, ICONW*2, ICONH, DC_BMP, active == true?COLORICONDC:COLORNOTACTIVE); 404 | drawIcon(ACPOSX, ACPOSY, ICONW*2, ICONH, AC_BMP, active == true?COLORICONAC:COLORNOTACTIVE); 405 | 406 | M5.Lcd.fillRect(SIGNPOSX, SIGNPOSY, SIGNW, SIGNH, active == true?FONTCOLORVALUE:COLORNOTACTIVE); 407 | 408 | M5.Lcd.fillRect(POINTSPOSX + (0 * DIGITSDISTANCE), POINTSPOSY, POINTSW, POINTSH, active == true?FONTCOLORVALUE:COLORNOTACTIVE); 409 | M5.Lcd.fillRect(POINTSPOSX + (1 * DIGITSDISTANCE), POINTSPOSY, POINTSW, POINTSH, active == true?FONTCOLORVALUE:COLORNOTACTIVE); 410 | M5.Lcd.fillRect(POINTSPOSX + (2 * DIGITSDISTANCE), POINTSPOSY, POINTSW, POINTSH, active == true?FONTCOLORVALUE:COLORNOTACTIVE); 411 | 412 | M5.Lcd.setTextDatum(TL_DATUM); 413 | M5.Lcd.setTextSize(2); 414 | M5.Lcd.setTextColor(active == true?FONTCOLORVALUE:COLORNOTACTIVE, BACKGROUND); 415 | M5.Lcd.drawNumber(8, DIGITSPOSX + (0 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 416 | M5.Lcd.drawNumber(8, DIGITSPOSX + (1 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 417 | M5.Lcd.drawNumber(8, DIGITSPOSX + (2 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 418 | M5.Lcd.drawNumber(8, DIGITSPOSX + (3 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 419 | 420 | M5.Lcd.setTextSize(1); 421 | M5.Lcd.fillRect(SCALEPOSX, SCALEUNITPOSY, SCALEW, SCALEUNITH, BACKGROUND); 422 | M5.Lcd.fillRect(UNITPOSX, SCALEUNITPOSY, UNITW, SCALEUNITH, BACKGROUND); 423 | M5.Lcd.drawString("M", SCALEPOSX, SCALEUNITPOSY, SCALEUNITFONT); 424 | M5.Lcd.drawString("Ohm", UNITPOSX, SCALEUNITPOSY, SCALEUNITFONT); 425 | 426 | drawBarGraph(active); 427 | 428 | drawButtons(); 429 | 430 | if (active == false) 431 | firstNotify = true; 432 | } 433 | //------------------------------------------------------------------------------ 434 | void parseMeterPlus() { 435 | //based on: https://github.com/DeanCording/owonb35 by DeanCording 436 | //protocol: https://github.com/DeanCording/owonb35#protocol 437 | //code: https://github.com/DeanCording/owonb35/blob/master/owonb35.c#L277 438 | // 439 | //parse meterPlus from rawvalbuf to b35t format and fill valuechar buffer 440 | //rawvalbuf e.x. 19 F0 04 00 49 04 441 | 442 | uint16_t temp16 = 0; 443 | uint8_t function, scale, decimal; 444 | float measurement; 445 | 446 | //prepare valuechar buff 447 | memset(valuechar, 0, meterReplySize); // clean valuechar buffer 448 | valuechar[5]=0x20; 449 | valuechar[12]=0x0d; 450 | valuechar[13]=0x0a; 451 | 452 | //---------------------------------------------------------------------------- 453 | //---------------------------------------------------------------------------- 454 | 455 | // 0 and 1 byte from rawvalbuf 456 | temp16 = *((uint16_t *)(rawvalbuf)); 457 | 458 | DEBUG_MSG("D: B35tPlus PAIR1: %04X\n", temp16); 459 | 460 | // parse 461 | function = (temp16 >> 6) & 0x0f; 462 | scale = (temp16 >> 3) & 0x07; 463 | decimal = temp16 & 0x07; 464 | 465 | // decode and set appropriate bits in valuechar 466 | DEBUG_MSG(" - FUNCT: %02d\n", function); 467 | DEBUG_MSG(" - SCALE: %02d\n", scale); 468 | DEBUG_MSG(" - DECIM: %02d\n", decimal); 469 | 470 | //decinal point 471 | switch (decimal) { 472 | case B000: 473 | valuechar[REGPOINT] = FLAGPOINT0; 474 | break; 475 | case B001: 476 | valuechar[REGPOINT] = FLAGPOINT3; 477 | break; 478 | case B010: 479 | valuechar[REGPOINT] = FLAGPOINT2; 480 | break; 481 | case B011: 482 | valuechar[REGPOINT] = FLAGPOINT1; 483 | break; 484 | default: 485 | break; 486 | } 487 | 488 | //function 489 | switch (function) { 490 | case B0000: //DCV 491 | bitSet(valuechar[REGUNIT], 7); //V 492 | bitSet(valuechar[REGMODE], 4); //DC 493 | break; 494 | case B0001: //ACV 495 | bitSet(valuechar[REGUNIT], 7); //V 496 | bitSet(valuechar[REGMODE], 3); //AC 497 | break; 498 | case B0010: //DCA 499 | bitSet(valuechar[REGUNIT], 6); //A 500 | bitSet(valuechar[REGMODE], 4); //DC 501 | break; 502 | case B0011: //ACA 503 | bitSet(valuechar[REGUNIT], 6); //A 504 | bitSet(valuechar[REGMODE], 3); //AC 505 | break; 506 | case B0100: //Ohm 507 | bitSet(valuechar[REGUNIT], 5); //Ohm 508 | break; 509 | case B0101: //Cap 510 | bitSet(valuechar[REGUNIT], 2); //Cap 511 | break; 512 | case B0110: //Hz 513 | bitSet(valuechar[REGUNIT], 3); //Hz 514 | break; 515 | case B0111: //Duty 516 | bitSet(valuechar[REGSCALE], 1); //Duty 517 | break; 518 | case B1000: //TempC 519 | bitSet(valuechar[REGUNIT], 1); //TempC 520 | break; 521 | case B1001: //TempF 522 | bitSet(valuechar[REGUNIT], 0); //TempF 523 | break; 524 | case B1010: //Diode 525 | bitSet(valuechar[REGSCALE], 2); //Diode 526 | break; 527 | case B1011: //Continuity 528 | bitSet(valuechar[REGSCALE], 3); //Continuity 529 | break; 530 | case B1100: //hFE 531 | bitSet(valuechar[REGUNIT], 4); //hFE 532 | break; 533 | default: 534 | break; 535 | } 536 | 537 | //scale 538 | switch (scale) { 539 | case B010: //micro 540 | bitSet(valuechar[REGSCALE], 7); //micro 541 | break; 542 | case B011: //milli 543 | bitSet(valuechar[REGSCALE], 6); //milli 544 | break; 545 | case B101: //kilo 546 | bitSet(valuechar[REGSCALE], 5); //kilo 547 | break; 548 | case B110: //mega 549 | bitSet(valuechar[REGSCALE], 4); //mega 550 | break; 551 | default: 552 | break; 553 | } 554 | 555 | //------------------------------------------------------------------ 556 | //------------------------------------------------------------------ 557 | 558 | // 2 and 3 byte from rawvalbuf 559 | temp16 = *((uint16_t *)(rawvalbuf+2)); 560 | 561 | DEBUG_MSG("D: B35tPlus PAIR2: %04X\n", temp16); 562 | 563 | // decode and set appropriate bits in valuechar 564 | bitWrite(valuechar[REGMODE], 1, bitRead(temp16, 0)); // HOLD 565 | bitWrite(valuechar[REGMODE], 2, bitRead(temp16, 1)); // DELTA 566 | bitWrite(valuechar[REGMODE], 5, bitRead(temp16, 2)); // AUTORANGE 567 | bitWrite(valuechar[REGMINMAX], 4, bitRead(temp16, 4)); // MIN 568 | bitWrite(valuechar[REGMINMAX], 5, bitRead(temp16, 5)); // MAX 569 | if (bitRead(temp16, 3) == 1) // LOW BAT 570 | meterPlusLowBat = true; 571 | else 572 | meterPlusLowBat = false; 573 | 574 | if (meterPlusLowBat != meterPlusLowBatLast) { 575 | meterPlusLowBatLast = meterPlusLowBat; 576 | batCheckDraw(); 577 | } 578 | 579 | //------------------------------------------------------------------ 580 | //------------------------------------------------------------------ 581 | 582 | // 4 and 5 byte from rawvalbuf 583 | temp16 = *((uint16_t *)(rawvalbuf+4)); 584 | 585 | DEBUG_MSG("D: B35tPlus PAIR3: %04X\n", temp16); 586 | 587 | // parse 588 | if (decimal < 0x07) { // if not overloaded 589 | if (temp16 < 0x7fff) { 590 | measurement = (float)temp16 / pow(10.0, decimal); 591 | valuechar[REGPLUSMINUS]=FLAGPLUS; // set flag plus 592 | } else { 593 | measurement = -1 * (float)(temp16 & 0x7fff) / pow(10.0, decimal); 594 | valuechar[REGPLUSMINUS]=FLAGMINUS; // set flag minus 595 | temp16 = temp16 & 0x7fff; 596 | } 597 | 598 | DEBUG_MSG(" - VALUE : %06.4f\n", measurement); 599 | 600 | valuechar[REGDIG1] = 0x30 + (int(temp16 / 1000) % 10); 601 | valuechar[REGDIG2] = 0x30 + (int(temp16 / 100) % 10); 602 | valuechar[REGDIG3] = 0x30 + (int(temp16 / 10) % 10); 603 | valuechar[REGDIG4] = 0x30 + (int(temp16 / 1) % 10); 604 | } else { 605 | memcpy(valuechar, overLoadFrame, 5); 606 | } 607 | 608 | } 609 | //------------------------------------------------------------------------------ 610 | void displayValues() { 611 | 612 | static char valuetmp[meterReplySize-3]; // local copy of data 613 | 614 | if (meterIsPlus) { // if meter is b35tPLUS 615 | parseMeterPlus(); 616 | } else { // if meter is b35t 617 | memcpy(valuechar, rawvalbuf, meterReplySize); // simply copy rawvalbuf to valuechar 618 | } 619 | 620 | #ifdef MYDEBUG 621 | DEBUG_MSG(" - OUT BUFF: [ "); 622 | for (uint8_t i = 0; i < meterReplySize; i++) { 623 | DEBUG_MSG("%02X ", valuechar[i]); 624 | } 625 | DEBUG_MSG("]\n"); 626 | #endif 627 | 628 | if (memcmp(valuechar, overLoadFrame, 5) == 0) { // if overloaded 629 | if (overLoad == false) { 630 | overLoad = true; 631 | DEBUG_MSG(" - OVERLOAD: YES\n"); 632 | } 633 | } else { 634 | if (overLoad == true) { 635 | overLoad = false; 636 | DEBUG_MSG(" - OVERLOAD: NO\n"); 637 | } 638 | } 639 | 640 | if (((valuechar[REGSCALE] & FLAGSCALEBUZZ) == FLAGSCALEBUZZ) || (buzzOn == true)) { 641 | buzzCheck(); 642 | } 643 | 644 | if (firstNotify == true) { 645 | displayShow(); 646 | M5.Lcd.setTextColor(FONTCOLORVALUE, BACKGROUND); 647 | M5.Lcd.setTextDatum(TL_DATUM); 648 | M5.Lcd.fillRect(BARGRAPHPOSX, BARGRAPHPOSY, TFT_HEIGHT - 70, 2, TFT_WHITE); // bargraph bottom init scale 649 | memset(valuetmp, 0, meterReplySize-3); 650 | //drawIcon(WBLEPOSX, TOPROWPOSY, ICONW, ICONH, BLE_BMP, deviceBleConnected == true?COLORICONBLE:COLORNOTACTIVE); 651 | firstNotify = false; 652 | } else { 653 | drawBarGraph(!overLoad); 654 | } 655 | 656 | if (valuetmp[REGUNIT] != valuechar[REGUNIT]) { // unit 657 | String unit = ""; 658 | valuetmp[REGUNIT] = valuechar[REGUNIT]; 659 | switch (valuetmp[REGUNIT]) { 660 | case FLAGUNITFAHR: 661 | unit = "*F"; 662 | break; 663 | case FLAGUNITGRAD: 664 | unit = "*C"; 665 | break; 666 | case FLAGUNITNF: 667 | unit = "nF"; 668 | break; 669 | case FLAGUNITHZ: 670 | unit = "Hz"; 671 | break; 672 | case FLAGUNITHFE: 673 | unit = "hFE"; 674 | break; 675 | case FLAGUNITOHM: 676 | unit = "Ohm"; 677 | break; 678 | case FLAGUNITAMP: 679 | unit = "A"; 680 | break; 681 | case FLAGUNITVOLT: 682 | unit = "V"; 683 | break; 684 | break; 685 | } 686 | M5.Lcd.fillRect(UNITPOSX, SCALEUNITPOSY, UNITW, SCALEUNITH, BACKGROUND); 687 | M5.Lcd.setTextSize(1); 688 | M5.Lcd.setTextColor(TFT_YELLOW, BACKGROUND); 689 | M5.Lcd.drawString(unit, UNITPOSX, SCALEUNITPOSY, SCALEUNITFONT); 690 | } 691 | 692 | if (valuetmp[REGSCALE] != valuechar[REGSCALE]) { //scale 693 | String scale = ""; 694 | if ((valuechar[REGSCALE] & FLAGSCALEDUTY) == FLAGSCALEDUTY) { 695 | valuetmp[REGSCALE] |= FLAGSCALEDUTY; 696 | scale = "%"; 697 | } else { 698 | valuetmp[REGSCALE] &= ~(FLAGSCALEDUTY); 699 | } 700 | 701 | if ((valuechar[REGSCALE] & FLAGSCALEDIODE) == FLAGSCALEDIODE) 702 | valuetmp[REGSCALE] |= FLAGSCALEDIODE; 703 | else 704 | valuetmp[REGSCALE] &= ~(FLAGSCALEDIODE); 705 | 706 | drawIcon(WDIODEPOSX, TOPROWPOSY, ICONW, ICONH, DIODE_BMP, (valuetmp[REGSCALE] & FLAGSCALEDIODE) == FLAGSCALEDIODE?COLORICONDIODE:COLORNOTACTIVE); 707 | 708 | if ((valuechar[REGSCALE] & FLAGSCALEBUZZ) == FLAGSCALEBUZZ) { 709 | valuetmp[REGSCALE] |= FLAGSCALEBUZZ; 710 | } else { 711 | valuetmp[REGSCALE] &= ~(FLAGSCALEBUZZ); 712 | } 713 | 714 | //drawIcon(WBUZZPOSX, TOPROWPOSY, ICONW, ICONH, BUZZ_BMP, (valuetmp[REGSCALE] & FLAGSCALEBUZZ) == FLAGSCALEBUZZ?COLORICONBUZZ:COLORNOTACTIVE); 715 | drawIcon(WBUZZPOSX, TOPROWPOSY, ICONW, ICONH, BUZZ_BMP, (valuetmp[REGSCALE] & FLAGSCALEBUZZ) == FLAGSCALEBUZZ?(buzzOn == true?COLORICONBUZZ:TFT_LIGHTGREY):COLORNOTACTIVE); 716 | 717 | if ((valuechar[REGSCALE] & FLAGSCALEMEGA) == FLAGSCALEMEGA) { 718 | valuetmp[REGSCALE] |= FLAGSCALEMEGA; 719 | scale = "M"; 720 | } else { 721 | valuetmp[REGSCALE] &= ~(FLAGSCALEMEGA); 722 | } 723 | 724 | if ((valuechar[REGSCALE] & FLAGSCALEKILO) == FLAGSCALEKILO) { 725 | valuetmp[REGSCALE] |= FLAGSCALEKILO; 726 | scale = "k"; 727 | } else { 728 | valuetmp[REGSCALE] &= ~(FLAGSCALEKILO); 729 | } 730 | 731 | if ((valuechar[REGSCALE] & FLAGSCALEMILI) == FLAGSCALEMILI) { 732 | valuetmp[REGSCALE] |= FLAGSCALEMILI; 733 | scale = "m"; 734 | } else { 735 | valuetmp[REGSCALE] &= ~(FLAGSCALEMILI); 736 | } 737 | 738 | if ((valuechar[REGSCALE] & FLAGSCALEMICRO) == FLAGSCALEMICRO) { 739 | valuetmp[REGSCALE] |= FLAGSCALEMICRO; 740 | scale = "u"; 741 | } else { 742 | valuetmp[REGSCALE] &= ~(FLAGSCALEMICRO); 743 | } 744 | 745 | M5.Lcd.fillRect(SCALEPOSX, SCALEUNITPOSY, SCALEW, SCALEUNITH, BACKGROUND); 746 | if (scale != "") { 747 | M5.Lcd.setTextSize(1); 748 | M5.Lcd.setTextColor(TFT_YELLOW, BACKGROUND); 749 | M5.Lcd.drawString(scale, SCALEPOSX, SCALEUNITPOSY, SCALEUNITFONT); 750 | } 751 | } 752 | 753 | if ((valuetmp[REGMODE] & FLAGMODEAUTO) != (valuechar[REGMODE] & FLAGMODEAUTO)) { // auto 754 | if ((valuechar[REGMODE] & FLAGMODEAUTO) == FLAGMODEAUTO) 755 | valuetmp[REGMODE] |= FLAGMODEAUTO; 756 | else 757 | valuetmp[REGMODE] &= ~(FLAGMODEAUTO); 758 | 759 | drawIcon(WAUTOPOSX, TOPROWPOSY, ICONW*2, ICONH, AUTO_BMP, (valuetmp[REGMODE] & FLAGMODEAUTO) == FLAGMODEAUTO?COLORICONAUTO:COLORNOTACTIVE); 760 | 761 | } 762 | 763 | if ((valuetmp[REGMINMAX] & FLAGMAX) != (valuechar[REGMINMAX] & FLAGMAX)) { // max 764 | if ((valuechar[REGMINMAX] & FLAGMAX) == FLAGMAX) 765 | valuetmp[REGMINMAX] |= FLAGMAX; 766 | else 767 | valuetmp[REGMINMAX] &= ~(FLAGMAX); 768 | 769 | drawIcon(WMAXPOSX, TOPROWPOSY, ICONW*2, ICONH, MAX_BMP, (valuetmp[REGMINMAX] & FLAGMAX) == FLAGMAX?COLORICONMAX:COLORNOTACTIVE); 770 | 771 | } 772 | 773 | if ((valuetmp[REGMINMAX] & FLAGMIN) != (valuechar[REGMINMAX] & FLAGMIN)) { // min 774 | if ((valuechar[REGMINMAX] & FLAGMIN) == FLAGMIN) 775 | valuetmp[REGMINMAX] |= FLAGMIN; 776 | else 777 | valuetmp[REGMINMAX] &= ~(FLAGMIN); 778 | 779 | drawIcon(WMINPOSX, TOPROWPOSY, ICONW*2, ICONH, MIN_BMP, (valuetmp[REGMINMAX] & FLAGMIN) == FLAGMIN?COLORICONMIN:COLORNOTACTIVE); 780 | 781 | } 782 | 783 | if ((valuetmp[REGMODE] & FLAGMODEHOLD) != (valuechar[REGMODE] & FLAGMODEHOLD)) { // hold 784 | if ((valuechar[REGMODE] & FLAGMODEHOLD) == FLAGMODEHOLD) 785 | valuetmp[REGMODE] |= FLAGMODEHOLD; 786 | else 787 | valuetmp[REGMODE] &= ~(FLAGMODEHOLD); 788 | 789 | drawIcon(WHOLDPOSX, TOPROWPOSY, ICONW, ICONH, HOLD_BMP, (valuetmp[REGMODE] & FLAGMODEHOLD) == FLAGMODEHOLD?COLORICONHOLD:COLORNOTACTIVE); 790 | 791 | } 792 | 793 | if ((valuetmp[REGMODE] & FLAGMODEREL) != (valuechar[REGMODE] & FLAGMODEREL)) { // relative 794 | if ((valuechar[REGMODE] & FLAGMODEREL) == FLAGMODEREL) 795 | valuetmp[REGMODE] |= FLAGMODEREL; 796 | else 797 | valuetmp[REGMODE] &= ~(FLAGMODEREL); 798 | 799 | drawIcon(WRELPOSX, TOPROWPOSY, ICONW, ICONH, REL_BMP, (valuetmp[REGMODE] & FLAGMODEREL) == FLAGMODEREL?COLORICONREL:COLORNOTACTIVE); 800 | 801 | } 802 | 803 | if ((valuetmp[REGMODE] & FLAGMODEDC) != (valuechar[REGMODE] & FLAGMODEDC)) { // dc 804 | if ((valuechar[REGMODE] & FLAGMODEDC) == FLAGMODEDC) 805 | valuetmp[REGMODE] |= FLAGMODEDC; 806 | else 807 | valuetmp[REGMODE] &= ~(FLAGMODEDC); 808 | 809 | drawIcon(DCPOSX, DCPOSY, ICONW*2, ICONH, DC_BMP, (valuetmp[REGMODE] & FLAGMODEDC) == FLAGMODEDC?COLORICONDC:COLORNOTACTIVE); 810 | 811 | } 812 | 813 | if ((valuetmp[REGMODE] & FLAGMODEAC) != (valuechar[REGMODE] & FLAGMODEAC)) { // ac 814 | if ((valuechar[REGMODE] & FLAGMODEAC) == FLAGMODEAC) 815 | valuetmp[REGMODE] |= FLAGMODEAC; 816 | else 817 | valuetmp[REGMODE] &= ~(FLAGMODEAC); 818 | 819 | drawIcon(ACPOSX, ACPOSY, ICONW*2, ICONH, AC_BMP, (valuetmp[REGMODE] & FLAGMODEAC) == FLAGMODEAC?COLORICONAC:COLORNOTACTIVE); 820 | 821 | } 822 | 823 | M5.Lcd.setTextSize(2); 824 | M5.Lcd.setTextColor(FONTCOLORVALUE, BACKGROUND); 825 | 826 | if (valuetmp[REGPLUSMINUS] != valuechar[REGPLUSMINUS]) { // sign "-" 827 | valuetmp[REGPLUSMINUS] = valuechar[REGPLUSMINUS]; 828 | M5.Lcd.fillRect(SIGNPOSX, SIGNPOSY, SIGNW, SIGNH, (valuetmp[REGPLUSMINUS] & FLAGMINUS) == FLAGMINUS?FONTCOLORVALUE:BACKGROUND); 829 | } 830 | 831 | if (valuetmp[REGDIG1] != valuechar[REGDIG1]) { // digits 832 | valuetmp[REGDIG1] = valuechar[REGDIG1]; 833 | if (valuetmp[REGDIG1] >= 0x30 && valuetmp[REGDIG1] <= 0x39) 834 | M5.Lcd.drawChar(valuetmp[REGDIG1], DIGITSPOSX + (0 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 835 | else 836 | M5.Lcd.fillRect(DIGITSPOSX + (0 * DIGITSDISTANCE), DIGITSPOSY, 63, 94, BACKGROUND); 837 | } 838 | 839 | if (valuetmp[REGDIG2] != valuechar[REGDIG2]) { 840 | valuetmp[REGDIG2] = valuechar[REGDIG2]; 841 | if (valuetmp[REGDIG2] >= 0x30 && valuetmp[REGDIG2] <= 0x39) 842 | M5.Lcd.drawChar(valuetmp[REGDIG2], DIGITSPOSX + (1 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 843 | else 844 | M5.Lcd.fillRect(DIGITSPOSX + (1 * DIGITSDISTANCE), DIGITSPOSY, 63, 94, BACKGROUND); 845 | } 846 | 847 | if (valuetmp[REGDIG3] != valuechar[REGDIG3]) { 848 | valuetmp[REGDIG3] = valuechar[REGDIG3]; 849 | if (valuetmp[REGDIG3] >= 0x30 && valuetmp[REGDIG3] <= 0x39) 850 | M5.Lcd.drawChar(valuetmp[REGDIG3], DIGITSPOSX + (2 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 851 | else if (valuetmp[REGDIG3] == 0x3a) 852 | M5.Lcd.drawNumber(1, DIGITSPOSX + (2 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 853 | else 854 | M5.Lcd.fillRect(DIGITSPOSX + (2 * DIGITSDISTANCE), DIGITSPOSY, 63, 94, BACKGROUND); 855 | } 856 | 857 | if (valuetmp[REGDIG4] != valuechar[REGDIG4]) { 858 | valuetmp[REGDIG4] = valuechar[REGDIG4]; 859 | if (valuetmp[REGDIG4] >= 0x30 && valuetmp[REGDIG4] <= 0x39) 860 | M5.Lcd.drawChar(valuetmp[REGDIG4], DIGITSPOSX + (3 * DIGITSDISTANCE), DIGITSPOSY, DIGITSFONT); 861 | else 862 | M5.Lcd.fillRect(DIGITSPOSX + (3 * DIGITSDISTANCE), DIGITSPOSY, 63, 94, BACKGROUND); 863 | } 864 | 865 | DEBUG_MSG(" - DIGITS: %c %c %c %c\n", valuetmp[REGDIG1], valuetmp[REGDIG2], valuetmp[REGDIG3], valuetmp[REGDIG4]); 866 | 867 | if (valuetmp[REGPOINT] != valuechar[REGPOINT]) { // decimal point 868 | valuetmp[REGPOINT] = valuechar[REGPOINT]; 869 | M5.Lcd.fillRect(POINTSPOSX + (0 * DIGITSDISTANCE), POINTSPOSY, POINTSW, POINTSH, (valuetmp[REGPOINT] & FLAGPOINT1) == FLAGPOINT1?FONTCOLORVALUE:BACKGROUND); 870 | M5.Lcd.fillRect(POINTSPOSX + (1 * DIGITSDISTANCE), POINTSPOSY, POINTSW, POINTSH, (valuetmp[REGPOINT] & FLAGPOINT2) == FLAGPOINT2?FONTCOLORVALUE:BACKGROUND); 871 | M5.Lcd.fillRect(POINTSPOSX + (2 * DIGITSDISTANCE), POINTSPOSY, POINTSW, POINTSH, (valuetmp[REGPOINT] & FLAGPOINT3) == FLAGPOINT3?FONTCOLORVALUE:BACKGROUND); 872 | } 873 | 874 | newBleData = false; 875 | 876 | } 877 | 878 | static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) { 879 | if (isNotify == true && length <= rawBufferSize && pBLERemoteCharacteristic->getUUID().equals(charnotificationUUID)) { 880 | 881 | if (memcmp(rawvalbuf, pData, length) != 0) { // if new data <> old data 882 | DEBUG_MSG("D: Notify callback len=%d (UUID OK), new frame\n", length); 883 | if (newBleData == false) { // and if old data are displayed then copy new data 884 | memcpy(rawvalbuf, pData, length); 885 | 886 | #ifdef MYDEBUG 887 | DEBUG_MSG(" - RAW BUFF: [ "); 888 | for (uint8_t i = 0; i < length; i++) { 889 | DEBUG_MSG("%02X ", rawvalbuf[i]); 890 | } 891 | DEBUG_MSG("]\n"); 892 | #endif 893 | 894 | if ((length == replySizePlus) && (rawvalbuf[1] >= 0xf0)) { // if meter PLUS detected 895 | if (meterIsPlus == false) { 896 | meterIsPlus = true; 897 | DEBUG_MSG(" - type B35TPlus\n"); 898 | } 899 | newBleData = true; 900 | } else if ((length == replySizeNorm) && (rawvalbuf[12] == 0x0d) && (rawvalbuf[13] == 0x0a)) { 901 | if (meterIsPlus == true) { 902 | meterIsPlus = false; 903 | DEBUG_MSG(" - type B35T\n"); 904 | } 905 | newBleData = true; 906 | } else { 907 | DEBUG_MSG(" - type UNKNOWN\n"); 908 | } 909 | } else { 910 | DEBUG_MSG(" - skipped, previous frame still processed\n"); 911 | } 912 | } else { 913 | DEBUG_MSG("D: Notify callback len=%d (UUID OK), frame same as previous one\n", length); 914 | } 915 | lastBleNotify = millis(); 916 | } 917 | } 918 | 919 | class MyClientCallbacks: public BLEClientCallbacks { 920 | void onConnect(BLEClient *pClient) { 921 | deviceBleConnected = true; // set ble connected flag 922 | DEBUG_MSG("I: %s connected\n", OWONNAME); 923 | }; 924 | 925 | void onDisconnect(BLEClient *pClient) { 926 | pClient->disconnect(); 927 | deviceBleConnected = false; // clear ble connected flag 928 | deviceBleWriteAvailable = false; // clear ble available for write flag 929 | DEBUG_MSG("I: %s disconnected\n", OWONNAME); 930 | } 931 | }; 932 | 933 | bool connectToServer(BLEAddress pAddress) { 934 | 935 | DEBUG_MSG("I: Create a connection to addr: %s\n", pAddress.toString().c_str()); 936 | 937 | BLEClient* pClient = BLEDevice::createClient(); 938 | 939 | DEBUG_MSG(" - Client created\n"); 940 | 941 | pClient->setClientCallbacks(new MyClientCallbacks()); 942 | 943 | DEBUG_MSG(" - Connecting to server...\n"); 944 | 945 | pClient->connect(pAddress); // connect to the remove BLE Server. 946 | 947 | BLERemoteService* pRemoteService = pClient->getService(serviceUUID); // check if remote BLE service exists 948 | if (pRemoteService == nullptr) { 949 | DEBUG_MSG(" - Service not found (UUID: %s)\n", serviceUUID.toString().c_str()); 950 | return false; 951 | } else { 952 | DEBUG_MSG(" - Service found (UUID: %s)\n", serviceUUID.toString().c_str()); 953 | } 954 | 955 | // notify characteristic 956 | pRemoteCharacteristicNotify = pRemoteService->getCharacteristic(charnotificationUUID); 957 | if (pRemoteCharacteristicNotify == nullptr) { 958 | DEBUG_MSG(" - Notify characteristic not found (UUID: %s)\n", charnotificationUUID.toString().c_str()); 959 | return false; 960 | } else { 961 | DEBUG_MSG(" - Notify characteristic found (UUID: %s)\n", charnotificationUUID.toString().c_str()); 962 | } 963 | pRemoteCharacteristicNotify->registerForNotify(notifyCallback); //register notify callback 964 | 965 | // write characteristic 966 | pRemoteCharacteristicWrite = pRemoteService->getCharacteristic(charwriteUUID); 967 | if (pRemoteCharacteristicWrite == nullptr) { 968 | DEBUG_MSG(" - Write characteristic not found (UUID: %s)\n", charwriteUUID.toString().c_str()); 969 | deviceBleWriteAvailable = false; // clear ble available for write flag 970 | } else { 971 | DEBUG_MSG(" - Write characteristic found (UUID: %s)\n", charwriteUUID.toString().c_str()); 972 | deviceBleWriteAvailable = true; // set ble available for write flag 973 | } 974 | 975 | return true; 976 | 977 | } 978 | 979 | class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { 980 | 981 | void onResult(BLEAdvertisedDevice advertisedDevice) { 982 | DEBUG_MSG("I: BLE scan stop\n"); 983 | if (advertisedDevice.haveName() && strcmp(advertisedDevice.getName().c_str(), OWONNAME) == 0) { 984 | advertisedDevice.getScan()->stop(); 985 | //DEBUG_MSG("I: Stop BLE scan\n"); 986 | pServerAddress = new BLEAddress(advertisedDevice.getAddress()); 987 | DEBUG_MSG("I: BLE device found (%s at addr: %s)\n", OWONNAME, pServerAddress->toString().c_str()); 988 | } else { 989 | DEBUG_MSG("I: BLE device not found\n"); 990 | } 991 | drawIcon(WBLEPOSX, TOPROWPOSY, ICONW, ICONH, BLE_BMP, deviceBleConnected == true?COLORICONBLE:COLORNOTACTIVE); 992 | } 993 | 994 | }; 995 | 996 | void doScan() { 997 | drawIcon(WBLEPOSX, TOPROWPOSY, ICONW, ICONH, BLE_BMP, COLORICONBLESEARCH); 998 | DEBUG_MSG("I: BLE scan start\n"); 999 | startBleScanning = millis(); 1000 | BLEScan* pBLEScan = BLEDevice::getScan(); 1001 | pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); 1002 | pBLEScan->setActiveScan(true); 1003 | pBLEScan->start(scanTime); 1004 | } 1005 | 1006 | void setup() { 1007 | Serial.begin(115200); 1008 | DEBUG_MSG("I: Start OWON B35T Client\n"); 1009 | 1010 | #ifdef BATMETERI2C 1011 | Wire.begin(); 1012 | #endif 1013 | 1014 | WiFi.persistent(false); 1015 | WiFi.enableSTA(false); 1016 | WiFi.enableAP(false); 1017 | WiFi.mode(WIFI_OFF); 1018 | 1019 | M5.begin(); 1020 | M5.Power.begin(); 1021 | if(digitalRead(BUTTON_A_PIN) == 0) { 1022 | Serial.println("Will Load menu binary"); 1023 | updateFromFS(SD); 1024 | ESP.restart(); 1025 | } 1026 | M5.Lcd.fillScreen(BACKGROUND); 1027 | 1028 | memset(rawvalbuf, 0, rawBufferSize); // clean buffer 1029 | memset(valuechar, 0, meterReplySize); // clean buffer 1030 | 1031 | displayShow(); 1032 | 1033 | batCheckDraw(); 1034 | 1035 | BLEDevice::init(""); 1036 | } 1037 | 1038 | void loop() { 1039 | 1040 | M5.update(); 1041 | 1042 | if (deviceBleConnected == false) { 1043 | 1044 | delay(250); 1045 | 1046 | if (startBleScanning != 0 && millis() > (startBleScanning + (scanTime*1000))) { 1047 | startBleScanning = 0; 1048 | return; 1049 | } 1050 | 1051 | if (startBleScanning == 0) { 1052 | if (firstNotify == false) { 1053 | displayShow(); 1054 | if (buzzOn == true) { 1055 | buzzCheck(); 1056 | } 1057 | if (meterIsPlus) { 1058 | drawIcon(WACCUPOSX, TOPROWPOSY, ICONW, ICONH, ACCU_BMP, COLORNOTACTIVE); 1059 | meterPlusLowBatLast = !meterPlusLowBat; 1060 | } 1061 | } else 1062 | drawIcon(WBLEPOSX, TOPROWPOSY, ICONW, ICONH, BLE_BMP, deviceBleConnected == true?COLORICONBLE:COLORNOTACTIVE); 1063 | doScan(); 1064 | return; 1065 | } 1066 | 1067 | startBleScanning = 0; 1068 | if (connectToServer(*pServerAddress)) { 1069 | lastBleNotify = millis(); 1070 | } 1071 | 1072 | } else { 1073 | 1074 | if (millis() > (lastBleNotify + maxWaitForNotify) && firstNotify == false) { 1075 | DEBUG_MSG("I: No notify from %s (>%d)\n", OWONNAME, maxWaitForNotify); 1076 | deviceBleWriteAvailable = false; // clear ble available for write flag 1077 | displayShow(); 1078 | return; 1079 | } 1080 | 1081 | if (newBleData == true) { 1082 | if (deviceBleWriteAvailable == false) { 1083 | deviceBleWriteAvailable = true; // set ble available for write flag 1084 | } 1085 | displayValues(); 1086 | } else 1087 | 1088 | drawIcon(WBLEPOSX, TOPROWPOSY, ICONW, ICONH, BLE_BMP, deviceBleConnected == true?COLORICONBLE:COLORNOTACTIVE); 1089 | 1090 | if (deviceBleWriteAvailable == true) { 1091 | 1092 | if (M5.BtnA.wasReleased()) { 1093 | if (btnNumber > 1) { 1094 | btnNumber--; 1095 | owonBtnArray[0] = btnNumber; 1096 | drawButtons(); 1097 | DEBUG_MSG("I: BTN set [%s]\n", btnName[btnNumber-1]); 1098 | } 1099 | } 1100 | 1101 | if (M5.BtnB.wasReleased()) { 1102 | DEBUG_MSG("I: BTN short press [%s]\n", btnName[owonBtnArray[0]-1]); 1103 | owonBtnArray[1] = btnShortPress; 1104 | pRemoteCharacteristicWrite->writeValue(owonBtnArray, owonBtnArraySize); 1105 | } else if (M5.BtnB.pressedFor(btnLongPressTime)) { 1106 | DEBUG_MSG("I: BTN long press [%s]\n", btnName[owonBtnArray[0]-1]); 1107 | if (owonBtnArray[0] == 1 || owonBtnArray[0] == 5) // select and hz/duty accept only short press 1108 | owonBtnArray[1] = btnShortPress; 1109 | else 1110 | owonBtnArray[1] = btnLongPress; 1111 | pRemoteCharacteristicWrite->writeValue(owonBtnArray, owonBtnArraySize); 1112 | while(M5.BtnB.isPressed()){ 1113 | M5.update(); 1114 | } 1115 | } 1116 | 1117 | if (M5.BtnC.wasReleased()) { 1118 | if (btnNumber < buttonsMax) { 1119 | btnNumber++; 1120 | owonBtnArray[0] = btnNumber; 1121 | drawButtons(); 1122 | DEBUG_MSG("I: BTN set [%s]\n", btnName[btnNumber-1]); 1123 | } 1124 | } 1125 | 1126 | } 1127 | 1128 | } 1129 | 1130 | #ifdef BATMETERI2C 1131 | if (millis() > batNextReadTime) { 1132 | batCheckDraw(); 1133 | batNextReadTime = millis() + batReadEvery; 1134 | } 1135 | #endif 1136 | 1137 | } 1138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32 M5Stack BLE client for OWON B35T & B35T PLUS meters 2 | 3 | Uses Bluetooth Low Energy (BLE) client to allow the M5Stack to remotely display the screen output and to control the functions of an Owon B35T multi-meter. 4 | 5 | **Please note:** 6 | 7 | Current arduino-esp32 git (14.05.2018) allows you to choose partiton scheme from Arduino IDE->Tools->Partition Scheme,
8 | it is important to change this from default to minimal_spiffs or no_ota,
9 | because the size of the binary file exceeds the allowable value in the default partition.
10 | 11 | ![remote meter 1](https://github.com/reaper7/M5Stack_BLE_client_Owon_B35T/blob/master/docs/m5stack.jpg) 12 | 13 | ![remote meter 2](https://github.com/reaper7/M5Stack_BLE_client_Owon_B35T/blob/master/docs/m5stack_meter.jpg) 14 | 15 | **2023 Reaper7** 16 | 17 | I publish this sketch as it is. I do not have free time to clean it up and polish. 18 | 19 | If you like it, buy me a beer: (https://www.paypal.me/reaper7md) 20 | -------------------------------------------------------------------------------- /docs/icons.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reaper7/M5Stack_BLE_client_Owon_B35T/540e576cda4257ad3f241897fda3d23f2838bf00/docs/icons.xlsx -------------------------------------------------------------------------------- /docs/m5stack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reaper7/M5Stack_BLE_client_Owon_B35T/540e576cda4257ad3f241897fda3d23f2838bf00/docs/m5stack.jpg -------------------------------------------------------------------------------- /docs/m5stack_meter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reaper7/M5Stack_BLE_client_Owon_B35T/540e576cda4257ad3f241897fda3d23f2838bf00/docs/m5stack_meter.jpg -------------------------------------------------------------------------------- /meter_graphics.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ------------------------------------------------------------ 3 | * "THE BEERWARE LICENSE" (Revision 42): 4 | * wrote this code. As long as you retain this 5 | * notice, you can do whatever you want with this stuff. If we 6 | * meet someday, and you think this stuff is worth it, you can 7 | * buy me a beer in return. 8 | * ------------------------------------------------------------ 9 | */ 10 | 11 | // this is a part of 12 | // M5Stack_BLE_client_Owon_B35T.ino 13 | // 2023 Reaper7 (tested on M5Stack) 14 | 15 | #ifndef METER_GRAPHICS_H 16 | #define METER_GRAPHICS_H 17 | 18 | //global color settings 19 | const uint16_t BACKGROUND = TFT_BLACK; 20 | const uint16_t FONTCOLORVALUE = TFT_LIGHTGREY; 21 | //elements color 22 | const uint16_t COLORNOTACTIVE = 0x2965; // r,g,b = 45,45,45 23 | const uint16_t COLORICONACCU = TFT_GREEN; 24 | const uint16_t COLORICONBLE = TFT_BLUE; 25 | const uint16_t COLORICONBLESEARCH = TFT_YELLOW; 26 | const uint16_t COLORICONAUTO = TFT_LIGHTGREY; 27 | const uint16_t COLORICONMAX = TFT_RED; 28 | const uint16_t COLORICONMIN = TFT_GREEN; 29 | const uint16_t COLORICONREL = TFT_OLIVE; 30 | const uint16_t COLORICONDIODE = TFT_MAGENTA; 31 | const uint16_t COLORICONBUZZ = TFT_ORANGE; 32 | const uint16_t COLORICONHV = TFT_GREENYELLOW; 33 | const uint16_t COLORICONHOLD = TFT_BLUE; 34 | const uint16_t COLORICONBAT = TFT_RED; 35 | const uint16_t COLORICONDC = TFT_CYAN; 36 | const uint16_t COLORICONAC = TFT_MAGENTA; 37 | //elements position & size 38 | //digits 39 | const uint16_t DIGITSFONT = 7; 40 | const uint16_t DIGITSPOSY = 35; 41 | const uint16_t DIGITSPOSX = 40; 42 | const uint16_t DIGITSDISTANCE = 73; 43 | //decimal points 44 | const uint16_t POINTSW = 8; 45 | const uint16_t POINTSH = 10; 46 | const uint16_t POINTSPOSY = DIGITSPOSY + 84; 47 | const uint16_t POINTSPOSX = DIGITSPOSX + 64; 48 | //sign "-" 49 | const uint16_t SIGNW = 30; 50 | const uint16_t SIGNH = 10; 51 | const uint16_t SIGNPOSY = DIGITSPOSY + 42; 52 | const uint16_t SIGNPOSX = DIGITSPOSX - (SIGNW + 3); 53 | //ac/dc 54 | const uint16_t DCPOSY = SIGNPOSY + SIGNH + 5; 55 | const uint16_t ACPOSY = DCPOSY + 21; 56 | const uint16_t DCPOSX = DIGITSPOSX - 34; 57 | const uint16_t ACPOSX = DIGITSPOSX - 34; 58 | //scale 59 | const uint16_t SCALEUNITFONT = 4; 60 | const uint16_t SCALEUNITPOSY = DIGITSPOSY + 105; 61 | const uint16_t SCALEPOSX = 235; 62 | const uint16_t UNITPOSX = SCALEPOSX + 23; 63 | const uint16_t SCALEW = 23; 64 | const uint16_t UNITW = 60; 65 | const uint16_t SCALEUNITH = 20; 66 | //bargraph 67 | const uint16_t BARGRAPHPOSX = 35; 68 | const uint16_t BARGRAPHPOSY = 185; 69 | 70 | const uint16_t BARGRAPHADDX = BARGRAPHPOSX + 5; 71 | const uint16_t BARGRAPHYA = BARGRAPHPOSY - 20; 72 | const uint16_t BARGRAPHYB = BARGRAPHPOSY - 15; 73 | const uint16_t BARGRAPHYC = BARGRAPHPOSY - 10; 74 | //icons 75 | const uint16_t ICONW = 16; 76 | const uint16_t ICONH = 16; 77 | const uint16_t ICONSMARGIN = 10; 78 | 79 | const uint16_t TOPROWPOSX = 12; 80 | const uint16_t TOPROWPOSY = 8; 81 | 82 | const uint16_t WACCUPOSX = TOPROWPOSX; 83 | const uint16_t WBLEPOSX = WACCUPOSX + ICONW + ICONSMARGIN; 84 | const uint16_t WAUTOPOSX = WBLEPOSX + ICONW + ICONSMARGIN; 85 | const uint16_t WMAXPOSX = WAUTOPOSX + (ICONW * 2) + ICONSMARGIN; 86 | const uint16_t WMINPOSX = WMAXPOSX + (ICONW * 2) + ICONSMARGIN; 87 | const uint16_t WHOLDPOSX = WMINPOSX + (ICONW * 2) + ICONSMARGIN; 88 | const uint16_t WRELPOSX = WHOLDPOSX + ICONW + ICONSMARGIN; 89 | const uint16_t WDIODEPOSX = WRELPOSX + ICONW + ICONSMARGIN; 90 | const uint16_t WBUZZPOSX = WDIODEPOSX + ICONW + ICONSMARGIN; 91 | const uint16_t WHVPOSX = WBUZZPOSX + ICONW + ICONSMARGIN; 92 | 93 | const uint16_t ACCUSCALW = 2; 94 | const uint16_t ACCUSCALH = 6; 95 | 96 | //buttons 97 | const uint16_t BTNAXPOS = 34; 98 | const uint16_t BTNBXPOS = 108; 99 | const uint16_t BTNCXPOS = 242; 100 | 101 | const uint16_t BTNYPOSFROMBOTTOM = 28; 102 | 103 | const uint16_t BTNAW = 40; 104 | const uint16_t BTNBW = 100; 105 | const uint16_t BTNCW = 40; 106 | 107 | const uint16_t BTNH = 28; 108 | 109 | const uint16_t BTNYTEXTPOSFROMBOTTOM = 21; 110 | 111 | const uint8_t PROGMEM ACCU_BMP[32] = { 112 | B00000000, B00000000, 113 | B00000000, B00000000, 114 | B00000000, B00000000, 115 | B11111111, B11111110, 116 | B10000000, B00000010, 117 | B10000000, B00000011, 118 | B10000000, B00000011, 119 | B10000000, B00000011, 120 | B10000000, B00000011, 121 | B10000000, B00000011, 122 | B10000000, B00000011, 123 | B10000000, B00000010, 124 | B11111111, B11111110, 125 | B00000000, B00000000, 126 | B00000000, B00000000, 127 | B00000000, B00000000 128 | }; 129 | 130 | const uint8_t PROGMEM BLE_BMP[32] = { 131 | B00000001, B10000000, 132 | B00000001, B11000000, 133 | B00010001, B01100000, 134 | B00011001, B00110000, 135 | B00001101, B00011000, 136 | B00000111, B00110000, 137 | B00000011, B01100000, 138 | B00000001, B11000000, 139 | B00000001, B11000000, 140 | B00000011, B01100000, 141 | B00000111, B00110000, 142 | B00001101, B00011000, 143 | B00011001, B00110000, 144 | B00010001, B01100000, 145 | B00000001, B11000000, 146 | B00000001, B10000000 147 | }; 148 | 149 | const uint8_t PROGMEM AUTO_BMP[64] = { 150 | B01111100, B01100011, B01111111, B00111110, 151 | B11111110, B01100011, B01111111, B01111111, 152 | B11101110, B01100011, B00011100, B01110111, 153 | B11000110, B01100011, B00011100, B01100011, 154 | B11000110, B01100011, B00011100, B01100011, 155 | B11000110, B01100011, B00011100, B01100011, 156 | B11000110, B01100011, B00011100, B01100011, 157 | B11111110, B01100011, B00011100, B01100011, 158 | B11111110, B01100011, B00011100, B01100011, 159 | B11000110, B01100011, B00011100, B01100011, 160 | B11000110, B01100011, B00011100, B01100011, 161 | B11000110, B01100011, B00011100, B01100011, 162 | B11000110, B01100011, B00011100, B01100011, 163 | B11000110, B01110111, B00011100, B01110111, 164 | B11000110, B00111110, B00011100, B01111111, 165 | B11000110, B00111110, B00011100, B00111110 166 | }; 167 | 168 | const uint8_t PROGMEM MAX_BMP[64] = { 169 | B11000000, B01100011, B11100011, B00000011, 170 | B11100000, B11100111, B11110011, B00000011, 171 | B11110001, B11100111, B01110011, B10000111, 172 | B11111011, B11100110, B00110001, B10000110, 173 | B11011111, B01100110, B00110001, B11001110, 174 | B11001110, B01100110, B00110000, B11001100, 175 | B11000100, B01100110, B00110000, B11001100, 176 | B11000000, B01100111, B11110000, B01111000, 177 | B11000000, B01100111, B11110000, B01111000, 178 | B11000000, B01100110, B00110000, B11001100, 179 | B11000000, B01100110, B00110000, B11001100, 180 | B11000000, B01100110, B00110001, B11001110, 181 | B11000000, B01100110, B00110001, B10000110, 182 | B11000000, B01100110, B00110011, B10000111, 183 | B11000000, B01100110, B00110011, B00000011, 184 | B11000000, B01100110, B00110011, B00000011 185 | }; 186 | 187 | const uint8_t PROGMEM MIN_BMP[64] = { 188 | B11000000, B01100001, B10000110, B00000011, 189 | B11100000, B11100001, B10000111, B00000011, 190 | B11110001, B11100001, B10000111, B10000011, 191 | B11111011, B11100001, B10000111, B11000011, 192 | B11011111, B01100001, B10000110, B11100011, 193 | B11001110, B01100001, B10000110, B01110011, 194 | B11000100, B01100001, B10000110, B00111011, 195 | B11000000, B01100001, B10000110, B00011111, 196 | B11000000, B01100001, B10000110, B00001111, 197 | B11000000, B01100001, B10000110, B00000111, 198 | B11000000, B01100001, B10000110, B00000011, 199 | B11000000, B01100001, B10000110, B00000011, 200 | B11000000, B01100001, B10000110, B00000011, 201 | B11000000, B01100001, B10000110, B00000011, 202 | B11000000, B01100001, B10000110, B00000011, 203 | B11000000, B01100001, B10000110, B00000011 204 | }; 205 | 206 | const uint8_t PROGMEM HOLD_BMP[32] = { 207 | B01111111, B11111110, 208 | B11111111, B11111111, 209 | B11100011, B11000111, 210 | B11100011, B11000111, 211 | B11100011, B11000111, 212 | B11100011, B11000111, 213 | B11100011, B11000111, 214 | B11100000, B00000111, 215 | B11100000, B00000111, 216 | B11100011, B11000111, 217 | B11100011, B11000111, 218 | B11100011, B11000111, 219 | B11100011, B11000111, 220 | B11100011, B11000111, 221 | B11111111, B11111111, 222 | B01111111, B11111110 223 | }; 224 | 225 | const uint8_t PROGMEM REL_BMP[32] = { 226 | B00000001, B00000000, 227 | B00000001, B00000000, 228 | B00000011, B10000000, 229 | B00000011, B10000000, 230 | B00000110, B11000000, 231 | B00000110, B11000000, 232 | B00001100, B01100000, 233 | B00001100, B01100000, 234 | B00011000, B00110000, 235 | B00011000, B00110000, 236 | B00110000, B00011000, 237 | B00110000, B00011000, 238 | B01100000, B00001100, 239 | B01100000, B00001100, 240 | B11111111, B11111110, 241 | B11111111, B11111110 242 | }; 243 | 244 | const uint8_t PROGMEM DIODE_BMP[32] = { 245 | B00001000, B00011000, 246 | B00001100, B00011000, 247 | B00001110, B00011000, 248 | B00001111, B00011000, 249 | B00001111, B10011000, 250 | B00001111, B11011000, 251 | B00001111, B11111000, 252 | B11111111, B11111111, 253 | B11111111, B11111111, 254 | B00001111, B11111000, 255 | B00001111, B11011000, 256 | B00001111, B10011000, 257 | B00001111, B00011000, 258 | B00001110, B00011000, 259 | B00001100, B00011000, 260 | B00001000, B00011000 261 | }; 262 | 263 | const uint8_t PROGMEM BUZZ_BMP[32] = { 264 | B00000000, B11000000, 265 | B00000001, B11000000, 266 | B00000011, B11000001, 267 | B00000111, B11000001, 268 | B00001111, B11000101, 269 | B11111111, B11000101, 270 | B11111111, B11010101, 271 | B11111111, B11010101, 272 | B11111111, B11010101, 273 | B11111111, B11010101, 274 | B11111111, B11000101, 275 | B00001111, B11000101, 276 | B00000111, B11000001, 277 | B00000011, B11000001, 278 | B00000001, B11000000, 279 | B00000000, B11000000 280 | }; 281 | 282 | const uint8_t PROGMEM HV_BMP[32] = { 283 | B00000000, B10000000, 284 | B00000000, B10000000, 285 | B00000001, B10000000, 286 | B00000011, B00000000, 287 | B00000111, B00000000, 288 | B00000110, B00010000, 289 | B00001100, B01110000, 290 | B00001111, B11100000, 291 | B00001111, B11100000, 292 | B00011100, B01100000, 293 | B00010000, B11000000, 294 | B00000001, B11000000, 295 | B00000001, B10000000, 296 | B00000011, B00000000, 297 | B00000010, B00000000, 298 | B00000010, B00000000 299 | }; 300 | 301 | const uint8_t PROGMEM BAT_BMP[32] = { 302 | B00000000, B00000000, 303 | B00000000, B00000000, 304 | B00111000, B00011100, 305 | B00111000, B00011100, 306 | B11111111, B11111111, 307 | B10000000, B00000001, 308 | B10010000, B00000001, 309 | B10111000, B00011101, 310 | B10010000, B00000001, 311 | B10000000, B00000001, 312 | B10000000, B00000001, 313 | B10000000, B00000001, 314 | B10000000, B00000001, 315 | B11111111, B11111111 316 | }; 317 | 318 | const uint8_t PROGMEM DC_BMP[64] = { 319 | B00111111, B11000000, B00000011, B11000000, 320 | B00111111, B11110000, B00001111, B11110000, 321 | B00111111, B11111000, B00011111, B11111000, 322 | B00111000, B00111000, B00011100, B00111100, 323 | B00111000, B00011100, B00111000, B00011100, 324 | B00111000, B00011100, B00111000, B00000000, 325 | B00111000, B00011100, B00111000, B00000000, 326 | B00111000, B00011100, B00111000, B00000000, 327 | B00111000, B00011100, B00111000, B00000000, 328 | B00111000, B00011100, B00111000, B00000000, 329 | B00111000, B00011100, B00111000, B00000000, 330 | B00111000, B00011100, B00111000, B00011100, 331 | B00111000, B00111000, B00011100, B00111000, 332 | B00111111, B11111000, B00011111, B11110000, 333 | B00111111, B11110000, B00001111, B11110000, 334 | B00111111, B11000000, B00000011, B11000000 335 | }; 336 | 337 | const uint8_t PROGMEM AC_BMP[64] = { 338 | B00000011, B11000000, B00000011, B11000000, 339 | B00001111, B11110000, B00001111, B11110000, 340 | B00011111, B11111000, B00011111, B11111000, 341 | B00011100, B00111000, B00011100, B00111100, 342 | B00111000, B00011100, B00111000, B00011100, 343 | B00111000, B00011100, B00111000, B00000000, 344 | B00111000, B00011100, B00111000, B00000000, 345 | B00111000, B00011100, B00111000, B00000000, 346 | B00111111, B11111100, B00111000, B00000000, 347 | B00111111, B11111100, B00111000, B00000000, 348 | B00111111, B11111100, B00111000, B00000000, 349 | B00111000, B00011100, B00111000, B00011100, 350 | B00111000, B00011100, B00011100, B00111000, 351 | B00111000, B00011100, B00011111, B11111000, 352 | B00111000, B00011100, B00001111, B11110000, 353 | B00111000, B00011100, B00000011, B11000000 354 | }; 355 | 356 | #endif //METER_GRAPHICS_H 357 | --------------------------------------------------------------------------------