Skip to content

Instantly share code, notes, and snippets.

@nomissbowling
Created March 22, 2022 00:46
Show Gist options
  • Save nomissbowling/d63c40b27655d8e1c5bf5546458d82a4 to your computer and use it in GitHub Desktop.
Save nomissbowling/d63c40b27655d8e1c5bf5546458d82a4 to your computer and use it in GitHub Desktop.
M5StickCPlus_QMP6988_SHT30.ino
/*
Hello_M5StickC_Plus.ino
Circuit Schematics
StickC_Plus_20200616.pdf
How To Write FactoryTest (default config)
EasyLoader M5StickC https://docs.m5stack.com/en/core/m5stickc
EasyLoader_M5StickC_FactoryTest.exe
EasyLoader M5StickC Plus https://docs.m5stack.com/en/core/m5stickc_plus
EasyLoader_M5StickC_Plus_FactoryTest.exe
How To Recover
https://zenn.dev/mongonta/articles/dca118b555ef128c2c62
https://lang-ship.com/blog/work/m5stickc-trouble-shoot/
https://bey.jp/?p=89309
How To Reset (NVS pref, WiFi ssid and key, RTC, NTP, Axp, ...)
https://lang-ship.com/blog/work/m5stickc-resetter/
set WiFi ssid and key from http
https://lang-ship.com/reference/unofficial/M5StickC/UseCase/RTCSetWeb/
esptool.py --port COMn erase_flash
0: introduction
https://logikara.blog/arduino_ide/
https://logikara.blog/m5stickc_plus/
download driver https://shop.m5stack.com/pages/download
CP210x_VCP_Windows.zip
( FDTI VCP https://ftdichip.com/drivers/vcp-drivers/ )
1: install driver
CP210xVCPInstaller_x64_v6.7.0.0.exe
2: connect
[unknown M5Stack] <- M5StickC Plus (other devices)
select it and update driver [auto search from network]
disconnect and connect
[unknown M5Stack] -> [USB Serial Converter] (auto changed) (USB controller)
[unknown USB Serial Port] <- M5StickC Plus (other devices)
select it and update driver [auto search from network]
disconnect and connect
[unknown USB Serial Port] -> [USB Serial Port (COMx)] (auto changed) (Port)
3: arduino - file - environment
additional board manager URL: ...
arduino - tool - board - board manager
search esp32 - install - https://github.com/espressif/arduino-esp32
(search M5StickC - install - M5StickC Plus ...)
arduino - tool - board select - ESP32 Arduino - M5StickC for M5StickC Plus
M5StickC 80x160
M5StickC Plus 135x240
M5StickV 135x240 cam 656x488
4: arduino - sketch - include library
https://interface.cqpub.co.jp/202104support_4/
download zip https://github.com/m5stack/M5StickC-Plus
M5StickC-Plus-master_m5stack_f1e4b95_20220224.zip
Arduino IDE - tool bar - sketch - include library - zip
MQTT 2.4.8 https://www.arduinolibraries.info/libraries/mqtt
ArduinoJSON 6.17.2 https://www.arduinolibraries.info/libraries/arduino-json
Serial2: G32/G33 GROVE
Serial1: G0/G26
Serial: USB-C (Arduino IDE tool Serial Monitor)
in setup Serial.begin(9600); // 115200 ...
in loop if(Serial.available()){ String s = Serial.readString(); ... }
M5StickV - M5StickC - network
https://qiita.com/nnn112358/items/5efd926fea20cd6c2c43
https://iothonpo.com/m5stickv-connect-wifi/
*/
#define LOAD_FONT2
#define LOAD_FONT4
// #include <M5StickC.h>
#include <M5StickCPlus.h>
#include <Preferences.h>
#include <Wire.h>
#include <WiFi.h>
#include <WiFiUdp.h>
// #include <WiFiMulti.h>
#include <WiFiClient.h>
#include <HTTPClient.h>
// #include <FS.h>
// #include <SPIFFS.h>
#include <Adafruit_NeoPixel.h>
#include <time.h>
// #include <math.h>
#include <cmath>
#define _countof(X) (sizeof(X) / sizeof(X[0]))
typedef unsigned char uchar;
void out_msg(String msg, int x, int y, int f, int w, int p);
uint16_t tones[] = {2200, 2331, 2469, // A3 B3b B3
2616, 2772, 2937, 3111, 3296, 3492, // C4 D4b D4 E4b E4 F4
3700, 3920, 4153, 4400, 4662, 4939, // G4b G4 A4b A4 B4b B4
5233, 5544, 5873, 6223, 6593, 6985, // C5 D5b D5 E5b E5 F5
7400, 7840, 8306, 8800, 9323, 9878}; // G5b G5 A5b A5 B5b B5
/*
A3: 220.000 B3b: 233.082 B3: 246.941
C4: 261.625 D4b: 277.182 D4: 293.665 E4b: 311.127 E4: 329.627 F4: 349.228
G4b: 369.994 G4: 391.995 A4b: 415.304 A4: 440.000 B4b: 466.164 B4: 493.883
C5: 523.251 D5b: 554.365 D5: 587.330 E5b: 622.254 E5: 659.255 F5: 698.456
G5b: 739.989 G5: 783.991 A5b: 830.609 A5: 880.000 B5b: 932.328 B5: 987.766
*/
#define URL_TEST "https://gist.githubusercontent.com/nomissbowling/c6a34455c5556989d80669764f7db930/raw/21b866bec3ed46fbfb7ba6c6617fe4f36b948ee7/interface.md"
#if 0 // can not access to other clients (stand alone)
char *ssid = "********-w";
char *pwid = "********";
#else // can access to other clients
char *ssid = "********";
char *pwid = "********";
#endif
uint16_t port_lsn = 8890;
uint16_t port_res = 8899;
char *ntp_server = "ntp.jst.mfeed.ad.jp";
int tz_diff = 9 * 3600; // JST
int tz_daylight = 0; // JST
float acc_eps = 0.02; // acceleration epsilon
/*
QMP Pressure Temperature ENV_III/QMP6988_Datasheet.pdf
b00U bt1 bt2 bp1 b11 bp2 b12 b21 bp3 a0U a1 a2 b00L/a0L
b_70_coe 4B52 04EC FD01 FC3D FCD4 0FAA 0BFE 0904 FA37 F903 0643 F8C2 91
raw Coefficient (BE 2bytes signed) (b00 a0 BE 20bits signed)
b00 = 4B529(308521) a0 = F9031(1019953)(-28623)
bt1 = 1260 bt2 = 64769(-767) bp1 = 64573(-963) b11 = 64724(-812)
bp2 = 4010 b12 = 3070 b21 = 2308 bp3 = 64055(-1481)
a1 = 1603 a2 = -1854(63682)
real Coefficient
(n != 0, n != 9) kas[n].k = kas[n].a + kas[n].s * b_70_coe[n] / 32767.0
(n == 0, n == 9) kas[0=b00].k = b00 / 16.0, kas[9=a0].k = a0 / 16.0
b_70_temp 1st(6CFEAC) 2nd(6C5860) ave001(22bit+0+0) (6BEE74) (6BC28) (6BC6FC)
d = (b_70_temp(BE 3bytes) >> (24 - temp_bits)) - (1 << (temp_bits - 1)) ?
d = b_70_temp(BE 3bytes) - (1 << 23) // calc always as 24
temp = kas[9=a0].k + (kas[10=a1].k + kas[11=a2].k * d) * d
b_70_pres 1st(A58EA8) 2nd(A57A45) ave111(24bit)
p = (b_70_pres(BE 3bytes) >> (24 - pres_bits)) - (1 << (pres_bits - 1)) ?
p = b_70_pres(BE 3bytes) - (1 << 23) // calc always as 24
pres = kas[0=b00].k + (kas[1=bt1].k + kas[2=bt2].k * temp) * temp
+ (kas[3=bp1].k + (kas[5=bp2].k + kas[8=bp3].k * p) * p) * p
+ (kas[4=b11].k + kas[6=b12].k * temp + kas[7=b21].k * p) * p * temp
(difference pres_fact and pres_expand <= 1E-4 order)
T(Celsius) = temp / 256
P(hPa) = pres / 100
SHT Temperature Humidity ENV_III/SHT3x_Datasheet_digital.pdf
b_44 1st(639C BD - 7237 31) 2nd(6553 8E - 6685 01) CRC-8 (651E FF - 678E 1F)
T 25500 RH 29239 T 25939 RH 26245 T 25886 RH 26510
T 23.09 RH 44.62 T 24.27 RH 40.05 T 24.12 RH 40.45
T(Celsius) = -45 + 175 * S(T) / 65535.0
T(F) = -49 + 315 * S(T) / 65535.0
RH(Relative Humidity) = 100 * S(RH) / 65535.0
CRC-8-Dallas/Maxim 0x31=0x131 = x**8 + x**5 + x**4 + 1 (ini 0xff, xor 0x00)
(Input reflected OFF) (Result reflected OFF) 0xbeef=0x92
tested CRC_8_Dallas_Maxim.c
*/
typedef struct _QMP6988_KAS {
float k, a, s; // Compensation Coefficient of Pressure or Temperature
} QMP6988_KAS;
QMP6988_KAS kas[] = {
{0, 0, 0}, // b00
{0, 1.00E-01, 9.10E-02}, // bt1
{0, 1.20E-08, 1.20E-06}, // bt2
{0, 3.30E-02, 1.90E-02}, // bp1
{0, 2.10E-07, 1.40E-07}, // b11
{0, -6.30E-10, 3.50E-10}, // bp2
{0, 2.90E-13, 7.60E-13}, // b12
{0, 2.10E-15, 1.20E-14}, // b21
{0, 1.30E-16, 7.90E-17}, // bp3
{0, 0, 0}, // a0
{0, -6.30E-03, 4.30E-04}, // a1
{0, -1.90E-11, 1.20E-10}}; // a2
typedef struct _i2c_dev {
uint8_t stat, id;
} i2c_dev;
uint8_t ID_QMP6988 = 0x70; // Pressure Temp (SDO=L:70 (default), H:56)
uint8_t b_70_reset[] = {0, 2, 0xe0, 0xe6}; // reset write no resp and wait >2ms
uint8_t b_70_checkid[] = {1, 1, 0xd1, 0}; // check id read expect 5c ok
uint8_t b_70_iosetup[] = {1, 2, 0xf5, 0x00, 0}; // b4x1x1 stby spi3_sdim spi3w
uint8_t b_70_control[] = {1, 2, 0xf4, 0x3f, 0}; // ctrl ave b332 tmp1 prs7 pow3
uint8_t b_70_coe[] = {25, 1, 0xa0, // compensation coefficient
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0}; // coe_xx regs
uint8_t b_70_temp[] = {3, 1, 0xfa, 0, 0, 0}; // read from fa fb fc Temperature
uint8_t b_70_pres[] = {3, 1, 0xf7, 0, 0, 0}; // read from f7 f8 f9 Pressure
uint8_t ID_SHT30 = 0x44; // Humidity (ADDR=L:44 (default) A, H:45 B)
uint8_t b_44[] = {6, 2, 0x2c, 0x06, 0, 0, 0, 0, 0, 0}; // {reg, BE bytes}, res
i2c_dev i2c_inf[] = {{0, ID_QMP6988}, {0, ID_SHT30}};
float f_70_temp = 0, f_70_pres = 0;
float f_44_temp = 0, f_44_rhmd = 0;
String strfloat(float f, bool e)
{
char s[16];
sprintf(s, e ? "%e" : "%7.2f", f);
return String((const char *)s);
}
String strbytes(uint8_t *buf)
{
char s[buf[0] * 2 + 1] = {0};
char *p = s;
for(int i = 0; i < buf[0]; ++i) p += sprintf(p, "%02X", buf[2 + buf[1] + i]);
*p = '\0';
return String((const char *)s);
}
String strbit128(uint16_t *sbit)
{
char s[40] = {0}; // [32=8('0000')] + [7(':')] + ['\0'] 0001:::0010::::
char *p = s;
for(int i = 0; i < 8; ++i){
if(sbit[i]) p += sprintf(p, "%04X", sbit[i]);
if(i != 8 - 1) *p++ = ':';
}
*p = '\0';
return String((const char *)s);
}
uint32_t be3bytes(uint8_t *p)
{
uint8_t s[4] = {p[2], p[1], p[0], 0}; // L, M, H, 0
return *(uint32_t *)s;
}
uint16_t be(uint8_t *p)
{
uint8_t s[2] = {p[1], p[0]}; // L, H
return *(uint16_t *)s;
}
int32_t be32s(uint8_t *p)
{
uint8_t s[4] = {p[1], p[0], 0, 0}; // L, H
if(p[0] & 0x80) s[2] = s[3] = 0xff;
return *(int32_t *)s;
}
void coe_QMP6988(i2c_dev *dev, uint8_t *b_coe)
{
if(!dev || !dev->stat) return;
uint8_t *b = &b_coe[3];
int i; // through down
for(i = 0; i < _countof(kas); ++i)
kas[i].k = kas[i].a + kas[i].s * (int16_t)be(&b[i * 2]) / 32767.0;
kas[0].k = ((be32s(&b[0]) << 4) | ((b[i * 2] >> 4) & 0x0f)) / 16.0;
kas[9].k = ((be32s(&b[9 * 2]) << 4) | (b[i * 2] & 0x0f)) / 16.0;
}
void calc_QMP6988(i2c_dev *dev, uint8_t *b_temp, uint8_t *b_pres)
{
if(!dev || !dev->stat) return;
// out_msg(strbytes(b_temp), 8, 180, 1, 0, 1);
// out_msg(strbytes(b_pres), 8 + 6 * 6, 180, 1, 0, 0);
uint8_t *b_t = &b_temp[3], *b_p = &b_pres[3];
#if 0
int temp_bits = 22, pres_bits = 24;
int32_t d = (be3bytes(b_t) >> (24 - temp_bits)) - (1 << (temp_bits - 1));
int32_t p = (be3bytes(b_p) >> (24 - pres_bits)) - (1 << (pres_bits - 1));
#else
int32_t d = be3bytes(b_t) - (1 << 23);
int32_t p = be3bytes(b_p) - (1 << 23);
#endif
float temp = kas[9].k + (kas[10].k + kas[11].k * d) * d;
float pres = kas[0].k + (kas[1].k + kas[2].k * temp) * temp
+ (kas[3].k + (kas[5].k + kas[8].k * p) * p) * p
+ (kas[4].k + kas[6].k * temp + kas[7].k * p) * p * temp;
f_70_temp = temp / 256; // temp: 256 degree Celsius
f_70_pres = pres / 100; // pres: Pa to hPa
out_msg(strfloat(f_70_temp, false), 20, 180, 1, 0, 1);
out_msg(" hPa", 20 + 6 * 14 + 3, 180, 1, 0, 0);
out_msg(strfloat(f_70_pres, false), 20 + 6 * 8, 180, 1, 0, 0);
}
uint8_t crc8maxim(uint8_t *p)
{
uint16_t crc = 0xff;
for(int i = 0; i < 2; ++i){
crc ^= *p++;
for(uint8_t b = 8; b > 0; --b)
if((crc <<= 1) & 0x0100) crc ^= 0x31;
}
return crc;
}
void calc_SHT30(i2c_dev *dev, uint8_t *b_)
{
if(!dev || !dev->stat) return;
uint8_t *b = &b_[4];
if(crc8maxim(&b[0]) != b[2] || crc8maxim(&b[3]) != b[5]) return;
// out_msg(strbytes(b_), 8 + 6 * 6, 170, 1, 0, 0);
f_44_temp = -45 + 175 * be(&b[0]) / 65535.0;
f_44_rhmd = 100 * be(&b[3]) / 65535.0;
out_msg(strfloat(f_44_temp, false), 20, 170, 1, 0, 1);
out_msg(" %", 20 + 6 * 14 + 3, 170, 1, 0, 0);
out_msg(strfloat(f_44_rhmd, false), 20 + 6 * 8, 170, 1, 0, 0);
}
i2c_dev *get_i2c_dev(uint8_t id, int sz, i2c_dev *inf)
{
for(int i = 0; i < sz; ++i)
if(inf[i].id == id) return &inf[i];
return NULL;
}
void i2c_scan(int sz, i2c_dev *inf)
{
int cnt = 0;
uint16_t sbit[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // BE 128bit = 16bit*8
// Wire.begin(...); or Wire1.begin(...);
for(int i = 0; i < sz; ++i){
uint8_t a = inf ? inf[i].id : (uint8_t)(i + 1);
Wire.beginTransmission(a); // Wire or Wire1
uint8_t e = Wire.endTransmission();
if(!e){ // e == ESP_OK
++cnt;
if(inf) inf[i].stat |= 0x01;
else sbit[7 - a / 16] |= 1 << (a % 16);
}
}
if(!cnt) out_msg("no I2C devices", 20, 190, 1, 1, 2);
else if(!inf) out_msg(strbit128(sbit), 20, 190, 1, 1, 2);
}
void i2c_read(i2c_dev *dev, uint8_t *buf)
{
if(!dev || !dev->stat) return;
dev->stat &= 0xfd;
// https://github.com/m5stack/M5StickC-Plus/blob/master/examples/FactoryTest/FactoryTest.ino
// Wire.begin(...); or Wire1.begin(...);
Wire.beginTransmission(dev->id);
for(int i = 0; i < buf[1]; ++i) Wire.write(buf[2 + i]);
Wire.endTransmission();
uint8_t m = buf[0];
if(m){
Wire.requestFrom(dev->id, m); // request m bytes (ino<=32, pico<=1024)
while(Wire.available() < m){} // wait
for(int i = 0; i < m; ++i) buf[2 + buf[1] + i] = Wire.read();
}
dev->stat |= 0x02;
}
void nvs_pref_init()
{
static Preferences pref;
pref.clear();
#if 0 // obsoleted
// see also 'set WiFi ssid and key from http'
// esptool.py --port COMn erase_flash
pref.begin("Wi-Fi", true);
pref.getString("ssid", wifi_ssid, sizeof(wifi_ssid)); // char wifi_ssid[...]
pref.getString("key", wifi_key, sizeof(wifi_pwid)); // char wifi_key[...]
pref.end();
// WiFi.begin(wifi_ssid, wifi_key); // ssid and key are witten to nvs
WiFi.begin(); // auto load ssid and key
#endif
}
void rtc_init()
{
Wire1.beginTransmission(0x51); // reset RTC
Wire1.write(0x00);
Wire1.write(0x00);
Wire1.write(0x00);
Wire1.endTransmission();
}
void rtc_show(int x, int y, int f, int w, int p)
{
static char *wd[] = {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"};
RTC_DateTypeDef rtc_date;
M5.Rtc.GetData(&rtc_date);
RTC_TimeTypeDef rtc_time;
M5.Rtc.GetTime(&rtc_time);
char buf[32];
sprintf(buf, "%04d%02d%02d %02d:%02d:%02d %-3s",
rtc_date.Year, rtc_date.Month, rtc_date.Date,
rtc_time.Hours, rtc_time.Minutes, rtc_time.Seconds, wd[rtc_date.WeekDay]);
out_msg(buf, x, y, f, w, p);
}
void ntp_connected()
{
static bool ntp_flg = false;
configTime(tz_diff, tz_daylight, ntp_server);
struct tm time_inf;
if(!ntp_flg && getLocalTime(&time_inf)){
ntp_flg = true;
RTC_DateTypeDef rtc_date;
rtc_date.Year = time_inf.tm_year + 1900;
rtc_date.Month = time_inf.tm_mon + 1;
rtc_date.Date = time_inf.tm_mday;
rtc_date.WeekDay = time_inf.tm_wday;
M5.Rtc.SetData(&rtc_date);
RTC_TimeTypeDef rtc_time;
rtc_time.Hours = time_inf.tm_hour;
rtc_time.Minutes = time_inf.tm_min;
rtc_time.Seconds = time_inf.tm_sec;
M5.Rtc.SetTime(&rtc_time);
}
}
void wifi_connect()
{
WiFi.begin(ssid, pwid);
}
String wifi_macaddress()
{
char s[18];
uint8_t ma[6];
WiFi.macAddress(ma);
for(int i = 0; i < sizeof(ma); ++i){
sprintf(s + i * 3, "%02x", ma[i]);
if(i < sizeof(ma) - 1) s[i * 3 + 2] = ':';
}
return String((const char *)s);
}
void wifi_connected()
{
static WiFiUDP wifi_udp;
static bool wifi_flg = false;
if(WiFi.status() != WL_CONNECTED){
out_msg("connecting", 20, 200, 1, 1, 0);
out_msg(wifi_macaddress(), 20, 210, 1, 1, 0);
return;
}
if(!wifi_flg){
wifi_flg = true;
out_msg("connected", 20, 200, 1, 1, 0);
out_msg(WiFi.localIP().toString(), 20, 220, 1, 1, 0);
ntp_connected();
wifi_udp.begin(port_lsn);
HTTPClient http;
http.begin(URL_TEST);
// http.addHeader("", "");
int stat = http.GET();
if(stat > 0){ // == 200
String payload = http.getString();
char b[16] = {0};
int i;
for(i = 0; i < 8; ++i) b[i] = payload[i];
b[i] = '\0';
out_msg(b, 20, 230, 1, 0, 0);
}else{
out_msg("error", 20, 230, 1, 0, 0);
}
http.end();
}else{
int np = wifi_udp.parsePacket();
if(np){
char pbuf[32] = {0};
int nb = wifi_udp.read(pbuf, np);
if(nb > 0) pbuf[nb] = '\0';
out_msg((const char *)pbuf, 20, 190, 1, 1, 1);
IPAddress tgt = wifi_udp.remoteIP();
out_msg(tgt.toString(), 20, 200, 1, 1, 1);
wifi_udp.beginPacket(tgt, port_res);
#if 0
#if 0
wifi_udp.write('M');
wifi_udp.write('5');
wifi_udp.write('.');
#else
uint8_t bbuf[32] = {'t', 'e', 's', 't'};
wifi_udp.write((const uint8_t *)bbuf, 4);
wifi_udp.write((const uint8_t *)"test", 4);
#endif
#else
#if 0
uint8_t *blst[] = {b_70_checkid, b_70_control, b_70_coe,
b_70_temp, b_70_pres, b_44};
for(int i = 0; i < _countof(blst); ++i){
String bstr = strbytes(blst[i]);
uint8_t bbuf[bstr.length() + 1];
bstr.getBytes(bbuf, bstr.length() + 1); // bstr.toCharArray(cbuf, ...);
wifi_udp.write((const uint8_t *)bbuf, bstr.length());
wifi_udp.write('.');
}
for(int i = 0; i < _countof(kas); ++i){
for(int j = 0; j < 1; ++j){ // < 1: k only, < 3: kas
float f = 0;
switch(j){
case 0: f = kas[i].k; break;
case 1: f = kas[i].a; break;
case 2: f = kas[i].s; break;
}
String fstr = strfloat(f, true);
uint8_t fbuf[fstr.length() + 1];
fstr.getBytes(fbuf, fstr.length() + 1);
wifi_udp.write((const uint8_t *)fbuf, fstr.length());
wifi_udp.write(';');
}
}
#else
float *flst[] = {&f_70_temp, &f_70_pres, &f_44_temp, &f_44_rhmd};
for(int i = 0; i < _countof(flst); ++i){
String fstr = strfloat(*flst[i], false);
uint8_t fbuf[fstr.length() + 1];
fstr.getBytes(fbuf, fstr.length() + 1);
wifi_udp.write((const uint8_t *)fbuf, fstr.length());
wifi_udp.write(',');
}
#endif
#endif
wifi_udp.endPacket();
}
// WiFi.disconnect(true);
// WiFi.mode(WIFI_OFF);
}
}
int signColor(float a)
{
#if 0
int s = 1;
if(a > -acc_eps && a < acc_eps) s = 0;
else if(signbit(a)) s = -1;
#else
int s = 0;
if(a <= -acc_eps) s = -1;
else if(a >= acc_eps) s = 1;
#endif
return (s + 2) % 3;
}
void out_msg(String msg, int x, int y, int f, int w, int p)
{
uint16_t fgc[] = {TFT_YELLOW, TFT_NAVY, TFT_DARKGREEN, TFT_ORANGE};
uint16_t bgc[] = {TFT_NAVY, TFT_YELLOW, TFT_PINK, TFT_BLUE};
// M5.Lcd.setTextColor(TFT_GREEN); // skip bgc
M5.Lcd.setTextColor(fgc[p], bgc[p]);
// M5.Lcd.setTextDatum(org); // org 0: left top, 1-7: ..., 8: right bottom
// M5.Lcd.setCursor(x, y); // M5.Lcd.setCursor(x, y, font);
// M5.Lcd.printf("%-2.2f", 1.456f); // .print(); .println();
// M5.Lcd.drawRightString("", x, y, f);
// M5.Lcd.drawCentreString("", x, y, f);
if(w) M5.Lcd.drawString(" ", x, y, f);
M5.Lcd.drawString(msg, x, y, f);
}
void draw(int cnt)
{
/*
M5.Lcd.drawXBitmap(x, y, pix, w, h, color); // uchar pix[h * w / 8] slow
M5.Lcd.drawBitmap(x, y, w, h, data); // uint16_t data[h * w] slow
M5.Lcd.drawPixel(x, y, color);
M5.Lcd.drawLine(x0, y0, x1, y1, color);
M5.Lcd.fillRect(x, y, w, h, color);
M5.Lcd.drawRect(x, y, w, h, color);
M5.Lcd.drawRoundRect(x0, y0, w, h, radius, color);
M5.Lcd.fillRoundRect(x0, y0, w, h, radius, color);
M5.Lcd.drawCircle(x0, y0, r, color);
M5.Lcd.drawCircleHelper(x0, y0, r, cornername, color);
M5.Lcd.fillCircle(x0, y0, r, color);
M5.Lcd.drawEllipse(x0, y0, rx, ry, color);
M5.Lcd.fillEllipse(x0, y0, rx, ry, color);
M5.Lcd.drawTriangle(x0, y0, x1, y1, x2, y2, color);
M5.Lcd.fillTriangle(x0, y0, x1, y1, x2, y2, color);
*/
uint16_t c = M5.Lcd.color565(255, cnt * 8 + 0x80 + 7, 255); // c = PINK;
uchar pix[2] = {0x55, 0xAA};
for(int i = 0; i < 8; ++i)
for(int j = 0; j < 2; ++j)
M5.Lcd.drawXBitmap(4 + j * 8, 10 + i * 2, pix, 8, 2, c);
#if 0
uint16_t c0 = M5.Lcd.color565(0, cnt * 8 + 7, 0); // 0x03e0; // 0x780f;
uint16_t c1 = M5.Lcd.color565(0, cnt * 16 + 15, 0); // 0x07e0; // 0xf81f;
uint16_t dat[2 * 2] = {c0, c1, c1, c0};
for(int i = 0; i < 8; ++i)
for(int j = 0; j < 8; ++j)
M5.Lcd.drawBitmap(22 + j * 2, 10 + i * 2, 2, 2, dat);
#else
uint16_t dat[16][16];
for(int i = 0; i < 16; ++i){
for(int j = 0; j < 16; ++j){
uchar c[3] = {
j >= i ? 255 : (240 + (j - i) * 16),
(j + i) * 8 + 15, // 8 == 256 / 32
i >= j ? 255 : (240 + (i - j) * 16)};
int s = j, t = i, u = cnt / 5;
switch(cnt % 5){
case 1: s = 15 - i, t = j; break;
case 2: s = 15 - j, t = 15 - i; break;
case 3: s = i, t = 15 - j; break;
case 4: s = j, t = i; break;
}
dat[t][s] = M5.Lcd.color565(c[u % 3], c[(u + 1) % 3], c[(u + 2) % 3]);
}
}
M5.Lcd.drawBitmap(22, 10, 16, 16, (uint16_t *)dat);
#endif
}
void handleBtnA()
{
//
}
void handleBtnB()
{
//
}
void setup()
{
M5.begin();
M5.IMU.Init();
M5.Axp.ScreenBreath(10); // brightness 7-12
M5.Lcd.setRotation(0); // USB position 0: bottom 1: right 2: top 3: left
M5.Lcd.setTextSize(1); // 1-7: multiplex (not dots)
M5.Lcd.setTextFont(1); // 1: 8px 2: 16px 4: 26px 6: 36pxNum 7: 7seg 8: 75pxN
// JP fonts https://lang-ship.com/blog/work/arduino-m5stickc-efont-unicode/
// https://lang-ship.com/reference/unofficial/M5StickC/UseCase/UnicodeFont/
M5.Lcd.setCursor(0, 0);
/*
7seg etc https://karakuri-musha.com/inside-technology/arduino-m5stickc-04-time-display-for-m5stickc/
WiFi 1 https://karakuri-musha.com/inside-technology/arduino-m5stickc-01-connect-wifi-for-ssid/
WiFi 2 https://karakuri-musha.com/inside-technology/arduino-m5stickc-02-connect-wifi-for-nossid/
NTP https://karakuri-musha.com/inside-technology/arduino-m5stickc-03-time-synchronization-with-wifi/
M5 Docs https://docs.m5stack.com/en/platform
M5StickV https://docs.m5stack.com/en/core/m5stickv
MaixPy https://github.com/sipeed/MaixPy
6axis https://tech.144lab.com/entry/2019/07/24/M5StickV_IMU
M5StickC Plus https://github.com/m5stack/M5StickC-Plus
Rev. Pendulum Madgwick https://qiita.com/coppercele/items/e4d71537a386966338d0
NeoPixel https://github.com/adafruit/Adafruit_NeoPixel
https://101010.fun/iot/arduino-serial-led.html
https://zoe6120.com/2019/07/03/990/
Hex https://qiita.com/yskmjp/items/f78a13d034b1319b390f
https://www.webzoit.net/hp/it/internet/homepage/env/iot/arduino/neopixel_ws2812b_5050_led_strip/
http://tetsuakibaba.jp/ws/doku.php?id=arduino:m5stickc:06.neopixel
https://wak-tech.com/archives/1842
https://ajimitei.blogspot.com/2019/09/m5stackneopixelm5stickc.html
https://shikarunochi.matrix.jp/?p=3510
Fetch Colors https://www.remotte.jp/ja/appl/senoshibiem5stickc
https://yoichi-41.hatenablog.com/entry/2019/09/15/204835
Servo https://101010.fun/iot/arduino-servo.html
Servo5V PWM https://burariweb.info/electronic-work/m5stickc-servo-control.html
Servo4.5V PWM http://blog.robotakao.jp/blog-entry-379.html
I2C protocol https://www.youtube.com/watch?v=THBcBykksaQ
I2C M5StickC Plus GND-5V-SDA(G32)-SCL(G33)
I2C ENV III SHT30 QMP6988 https://docs.m5stack.com/en/unit/envIII
SDA SCL 4.7k pullup 5Vcc - HT7533 - 3.3Vdd
saved to ENV_III_m5-docs.html
https://shop.m5stack.com/products/env-iii-unit-with-temperature-humidity-air-pressure-sensor-sht30-qmp6988
SHT3X.h .cpp QMP6988.h .cpp UNIT_ENV.h https://github.com/m5stack/UNIT_ENV
UNIT_ENV_III.ino https://github.com/m5stack/UNIT_ENV/blob/master/examples/UNIT_ENV_III/UNIT_ENV_III.ino
required https://github.com/adafruit/Adafruit_BMP280_Library
QMP6988 QMP6988_Datasheet.pdf Pressure Temp (SDO=L:70 (default), H:56)
SHT30 SHT3x_Datasheet_digital.pdf Humidity (ADDR=L:44 (default) A, H:45 B)
https://www.hackster.io/vany5921/m5stickc-with-homekit-automation-63acb9
https://www.hackster.io/makerfactory_team/smart-plant-with-m5stack-89212e
https://www.hackster.io/alessandro-polselli/matrix-shelly-thermostat-a-serverless-approach-7ec5d2
I2C BME280 ? 0x76 0x77 RPi ? (micro:bit ?)
I2C AHT-20 ? 0x30 8 ? 3.3V-5V ? () Humidity (micro:bit)
GPIO GROVE sonic ? (micro:bit)
HeartRate MAX30102 ?
MAX30102_HEART_RATE_MAX30102.ino
MAX30102_Internet_Of_SpO2_M5StickC_Plus_SpO2.ino
I2C / Btn Interrupt https://101010.fun/iot/m5stickc-plus-firststep.html
I2C OLED https://101010.fun/iot/arduino-oled-display.html
I2C LCD https://101010.fun/iot/arduino-lcd-i2c.html
GPIO super sonic https://101010.fun/iot/arduino-hc-sr04.html
7seg NTP Beep https://qiita.com/tranquility/items/3d121edc188ae398e028
Beep https://lang-ship.com/blog/work/m5stickc-plus-beep/
tips https://lang-ship.com/reference/unofficial/M5StickC/Tips/M5Display/
class https://lang-ship.com/reference/unofficial/M5StickC/Class/M5Display/
https://lang-ship.com/blog/work/m5stickc-display/
M5StickC Plus Screen 135x240 (M5Stick C 80x160)
int h = M5.Lcd.height(), w = M5.Lcd.width(); // changed by setRotation()
uint16_t c = M5.Lcd.color565(red, green, blue); // bits 5, 6, 5
BLACK, NAVY, DARKGREEN, DARKCYAN, MAROON, PURPLE, OLIVE, LIGHTGREY, DARKGREY,
BLUE, GREEN, CYAN, RED, MAGENTA, YELLOW, WHITE, ORANGE, GREENYELLOW, PINK
*/
M5.Lcd.fillScreen(DARKGREEN);
M5.Lcd.fillRect(8, 4, 119, 232, RED);
nvs_pref_init();
Serial.begin(115200);
Serial.printf("init %e\n", 0.1);
rtc_init(); // must before use Wire1
rtc_show(4, 2, 1, 0, 2);
out_msg("M5StickC Plus!", 40, 10, 1, 0, 0);
/*
generic
G2 Output (Beep)
G9 Output (Low: IR etc)
G10 Output (Low: LED etc)
G37 Input (BtnA)
G39 Input (BtnB)
... Input (Btn power Axp)
HAT/GPIO (TOP 8pin)
5V in
3V3 out (when power on)
BAT
G0 pull up 1k to 3V3 (I/O and clock of microphone) (Analog without WiFi)
G36/G25 common (G36 Input Analog ok) (G25 I/O Analog without WiFi)
G26 I/O (Analog without WiFi)
5V out
GND
I2C/UART/GPIO (BOTTOM 4pin) (Wire.begin(32, 33); // SDA=32, SCL=33)
G33 I/O SCL GROVE (Analog ok)
G32 I/O SDA GROVE (Analog ok)
V *** caution *** 5V/3V3 (pull up on/off)
GND
*/
// must use Wire1 after rtc_init();
// Wire1.begin(); // Wire1 default connected to inner I2C (RTC etc)
#if 0
while(!setCpuFrequencyMhz(10)){} // 10MHz 20 40 80 160 240 for some devices
Wire1.begin(0, 26, 400000);
#else
// Wire1.begin(0, 26); // (without inner) for M5StickC HAT I2C SDA=0, SCL=26
Wire.begin(32, 33); // for M5StickC GROVE I2C SDA=32, SCL=33
#endif
// setting G36 and G25 at the same time
// pinMode(36, INPUT); // or INPUT_PULLUP or INPUT_PULLDOWN
// gpio_pullup_dis(GPIO_NUM_25); // disable(both up down) = H-Z on G36
// gpio_pulldown_dis(GPIO_NUM_25); // disable(both up down) = H-Z on G36
// pinMode(0, OUTPUT);
// digitalWrite(0, HIGH);
// pinMode(37, INPUT_PULLUP); // BtnA
// attachInterrupt(digitalPinToInterrupt(37), handleBtnA, FALLING);
// pinMode(39, INPUT_PULLUP); // BtnB
// attachInterrupt(digitalPinToInterrupt(39), handleBtnB, FALLING);
pinMode(10, OUTPUT); // LED
digitalWrite(10, HIGH); // off
// pinMode(2, OUTPUT); // Beep (needless to work)
// digitalWrite(2, HIGH); // off
M5.Beep.begin(); // (needless to work)
M5.Beep.setVolume(10); // default 1
wifi_connect(); // with init rtc ntp
// Serial2.begin(115200);
i2c_scan(126, NULL); // scan 0x01-0x7e
i2c_scan(_countof(i2c_inf), i2c_inf);
i2c_dev *dev = get_i2c_dev(ID_QMP6988, _countof(i2c_inf), i2c_inf);
i2c_read(dev, b_70_reset);
delay(4);
i2c_read(dev, b_70_checkid);
// if(b_70_checkid[3] != 0x5c) out_msg(...); // error
i2c_read(dev, b_70_iosetup);
// if(b_70_iosetup[4] != b_70_iosetup[3]) out_msg(...); // error
i2c_read(dev, b_70_control);
// if(b_70_control[4] != b_70_control[3]) out_msg(...); // error
i2c_read(dev, b_70_coe);
coe_QMP6988(dev, b_70_coe);
}
void loop()
{
char buf[16];
M5.update(); // check Btn
M5.Beep.update(); // auto tone off after ms
// M5.Beep.mute();
// M5.Beep.setBeep(4000, 100); // skip (set default) ok
// M5.Beep.beep(); // same as tone() default 400.0Hz 100ms
// M5.Beep.tone(tones[0], 300); // 220.0Hz 300ms
// M5.Beep.tone(tones[0]); // always
if(Serial.available()){
String s = Serial.readString();
out_msg(s, 60, 20, 1, 1, 1);
#if 1
char cbuf[s.length() + 1];
s.toCharArray(cbuf, s.length() + 1);
cbuf[s.length() - 1] = '\0'; // without LF
Serial.printf("[%s]\n", cbuf);
#else
Serial.printf("[%s]\n", s); // with LF
#endif
}
static int cnt = 0;
sprintf(buf, "x%02X", cnt);
out_msg(buf, 40, 20, 1, 0, 0);
draw(cnt);
cnt = (cnt + 1) % 0x10;
wifi_connected(); // with init rtc ntp
rtc_show(4, 2, 1, 0, 2);
RTC_TimeTypeDef rt;
M5.Rtc.GetTime(&rt);
// if(!(rt.Minutes % 10) && !rt.Seconds){ // every 10 minutes
if(!(rt.Seconds % 15)){ // every 15 seconds
// out_msg("reading I2C...", 20, 180, 1, 1, 2);
i2c_dev *dev = get_i2c_dev(ID_QMP6988, _countof(i2c_inf), i2c_inf);
i2c_read(dev, b_70_temp);
i2c_read(dev, b_70_pres);
calc_QMP6988(dev, b_70_temp, b_70_pres);
dev = get_i2c_dev(ID_SHT30, _countof(i2c_inf), i2c_inf);
i2c_read(dev, b_44);
calc_SHT30(dev, b_44);
}
float battery = (M5.Axp.GetBatVoltage() - 3.2) * 100 / 0.8; // or (v-3)*90 ?
if(battery < 0) battery = 0;
else if(battery > 100) battery = 100;
sprintf(buf, "battery:%6.2f", battery);
out_msg(buf, 20, 30, 1, 1, 0);
int pw = M5.Axp.GetBtnPress(); // 2: <1s 1-0: >1s wake up
sprintf(buf, "pow:%d", pw);
out_msg(buf, 20, 40, 1, 1, 0);
// float val_ADC = analogRead(36); // 0-4095
// float v_in = val_ADC * (3.3 / 4095);
// float val_DAC = 0.0f; // 0-255
// dacWrite(26, val_DAC);
// float v_out = val_DAC * (3.3 / 255);
float ax = 0.0f, ay = 0.0f, az = 0.0f;
M5.IMU.getAccelData(&ax, &ay, &az);
sprintf(buf, "accX:%6.2f", ax);
out_msg(buf, 20, 90, 1, 1, signColor(ax));
sprintf(buf, "accY:%6.2f", ay);
out_msg(buf, 20, 100, 1, 1, signColor(ay));
sprintf(buf, "accZ:%6.2f", az);
out_msg(buf, 20, 110, 1, 1, signColor(az));
if(M5.BtnB.wasPressed()){
out_msg("B on", 20, 80, 1, 1, 0);
M5.Beep.tone(tones[10], 1000);
digitalWrite(10, HIGH); // off
}
if(M5.BtnB.pressedFor(300)){
out_msg("B long", 20, 80, 1, 1, 0);
M5.Beep.tone(tones[12], 1000);
digitalWrite(10, HIGH); // off
}
if(M5.BtnA.wasPressed()){
out_msg("A on", 20, 70, 1, 1, 0);
M5.Beep.tone(tones[15], 1000);
digitalWrite(10, LOW); // on
}
if(M5.BtnA.wasReleased()){
out_msg("A off", 20, 70, 1, 1, 0);
}
delay(500);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment