-
-
Save nomissbowling/d63c40b27655d8e1c5bf5546458d82a4 to your computer and use it in GitHub Desktop.
M5StickCPlus_QMP6988_SHT30.ino
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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