Lichten regelen met een m5stack Core 2 en Unit 8encoder

Pagina: 1
Acties:

  • Knappa
  • Registratie: April 2009
  • Laatst online: 14:50
Dag Allen,

Lang geleden kocht ik een m5stack Core 2 samen met een m5stack Unit 8encoder. Met deze combinatie wil ik ervoor zorgen dat de lichten in de kamer van m'n zoon geregeld kunnen worden d.m.v. een soort afstandsbediening. Ik merk zelf immers dat de gsm vastpakken, inloggen en de intensiteit veranderen lastiger dan iets fysiek vastnemen en aan een knop draaien. Ik heb Home Assistant draaien met Node Red en MQTT en daar maak ik dan gebruik van om alles te laten samenwerken.

Omdat ik er toch veel langer mee bezig ben geweest dan ik had verwacht, bezorg ik jullie graag de code hieronder (eerst van de m5stack Core2 en later de nodes in Node Red). Hebben jullie er wat aan, zoveel te beter !!

Alle opmerkingen en vragen over de code zijn welkom. Wellicht kan het hier en daar wat compacter, maar ik heb leesbaarheid boven efficiëntie geplaatst.
C: Core2code.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include <PubSubClient.h>

#include <WiFi.h>
#include <WiFiAP.h>
#include <WiFiClient.h>
#include <WiFiGeneric.h>
#include <WiFiMulti.h>
#include <WiFiSTA.h>
#include <WiFiScan.h>
#include <WiFiServer.h>
#include <WiFiType.h>
#include <WiFiUdp.h>

#include <M5GFX.h>

#include <UNIT_8ENCODER.h>
#include <M5Unified.h>

M5GFX display;
M5Canvas canvas(&display);
UNIT_8ENCODER sensor;
WiFiClient espClient;
PubSubClient client(espClient);

const bool DEBUG = true;         // enables logging to Serial
const int numberOfEncoders = 8;  // number of ENcoders (This is 8 as it is intended for use with the 8Encoder by m5stack)
const int correctionFactor = 4;  // multiplies output by this number to enable easier scaling

const long delayBetweenReads = 200;           // delay between two reads of the 8Encoder
const long delayBetweenSends = 1000;          // delay that a reading should be stable before sending to MQTT, must be greater than delayBetweenReads
const long delayBetweenBatteryReads = 40000;  // delay between displaying battery readouts

int32_t encoder[numberOfEncoders] = { 0, 0, 0, 0, 0, 0, 0, 0 };
bool btn_status[numberOfEncoders] = { 0, 0, 0, 0, 0, 0, 0, 0 };
bool switch_status = false;
bool change[numberOfEncoders] = { false, false, false, false, false, false, false, false };
bool changeStopped[numberOfEncoders] = { false, false, false, false, false, false, false, false };
bool buttonDown[numberOfEncoders] = { false, false, false, false, false, false, false, false };

const char* ssid = !ssid;             // home setup
const char* password = !WifiPW;
const char* mqtt_server = !IPMQTT;
const char* mqtt_clientid = !clientid;
const char* mqtt_user = !mqtt_user;
const char* mqtt_pw = !mqtt_pw;

int32_t encoderCurrentValue[numberOfEncoders] = { 0 };
int32_t encoderValueOnChange[numberOfEncoders] = { 0 };
int32_t encoderLatestValue[numberOfEncoders] = { 0 };

unsigned long lastReadout[numberOfEncoders] = { 0 };
unsigned long lastChange[numberOfEncoders] = { 0 };
unsigned long lastBatteryReadout = 0;

long lastMsg = 0;
char msg[50];
int value = 0;

void callback(char* topic, byte* message, unsigned int length) {              // currently unused
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;

  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic esp32/output, you check if the message is either "on" or "off".
  // Changes the output state according to the message
  if (String(topic) == "esp32/output") {
    Serial.print("Changing output to ");
    if (messageTemp == "on") {
    } else if (messageTemp == "off") {
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(mqtt_clientid, mqtt_user, mqtt_pw)) {
      Serial.println("connected");
      // Subscribe
      client.subscribe("esp32/output");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void sendEncoderData(int kanaal) {
  if (DEBUG) {
    Serial.print("Sending Data(int) op kanaal: ");
    Serial.print(kanaal);
    Serial.print(", met data: ");
    Serial.println(encoderLatestValue[kanaal]);
  }
  char changeString[8] = "";
  ltoa(encoderLatestValue[0], changeString, 10);
  char msg[100] = "";
  int direction = 0;
  if (encoderLatestValue[kanaal] > 0) { direction = 1; };
  sprintf(msg, "{\"kanaal\": %d, \"value\": %d, \"direction\": %d}", kanaal, abs(encoderLatestValue[kanaal]) * correctionFactor, direction);
  if (DEBUG) {
    Serial.print(kanaal);
    Serial.print(": ");
    Serial.print(changeString);
    Serial.print(" msg: ");
    Serial.println(msg);
  }
  char topic[50] = { 0 };
  strcat(topic, mqtt_clientid);
  strcat(topic, "/encoder/out");
  if (DEBUG) {
    Serial.println(topic);
    Serial.println(changeString);
  }

  client.publish(topic, msg);
}

void sendButtonData(int kanaal) {
  if (DEBUG) {
    Serial.print("Sending ButtonData(int) op kanaal: ");
    Serial.print(kanaal);
    Serial.print(", met data: ");
    Serial.println(sensor.getButtonStatus(kanaal));
  }
  char changeString[8] = "";
  ltoa(encoderLatestValue[0], changeString, 10);
  char msg[100] = "";
  sprintf(msg, "{\"kanaal\": %d, \"value\": %d}", kanaal, sensor.getButtonStatus(kanaal));
  if (DEBUG) {
    Serial.print(kanaal);
    Serial.print(": ");
    Serial.print(changeString);
    Serial.print(" msg: ");
    Serial.println(msg);
  }
  char topic[50] = { 0 };
  strcat(topic, mqtt_clientid);
  strcat(topic, "/button/out");
  if (DEBUG) {
    Serial.println(topic);
    Serial.println(changeString);
  }

  client.publish(topic, msg);
}
void setup() {
  // put your setup code here, to run once:
  M5.begin();
  M5.Power.begin();
  display.begin();
  sensor.begin(&Wire, ENCODER_ADDR, 32, 33, 100000UL);
  Serial.begin(115200);
  if (DEBUG) {
    Serial.println('Setup start');
  }
  sensor.setLEDColor(0, 0xff000f);
  sensor.setAllLEDColor(0x111111);
  lastReadout[0] = 0;
  lastChange[0] = 0;
  WiFi.begin(ssid, password);  //  Begin Wifi-blok
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    M5.Display.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  M5.Display.println("");
  M5.Display.println("WiFi Connected!");
  M5.Display.println(WiFi.localIP());  // Einde Wifi-blok

  for (int i = 0; i < numberOfEncoders; i++) {
    encoderCurrentValue[i] = sensor.getEncoderValue(i);
    encoderLatestValue[i] = 0;
    sensor.setEncoderValue(i, 0);
    btn_status[i] = sensor.getButtonStatus(i);
    if (DEBUG) {
      M5.Display.print(millis());
      M5.Display.print(": Current: ");
      M5.Display.print(encoderCurrentValue[i]);
      M5.Display.print(": Latest: ");
      M5.Display.print(encoderLatestValue[i]);
      M5.Display.print(": Button: ");
      M5.Display.println(btn_status[i]);
    }
  }
  M5.Display.print("Battery level: ");
  M5.Display.println(M5.Power.getBatteryLevel());
  switch_status = sensor.getSwitchStatus();

  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  if (DEBUG) {
    Serial.println('Setup end');
  }
}

void loop() {
  M5.update();

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  bool error = false;
  for (int i = 0; i < numberOfEncoders; i++) {
    if (sensor.getEncoderValue(i) == -1) {
      error = true;
    }
  }

  if (error) {
    for (int i = 0; i < numberOfEncoders; i++) {
      sensor.setEncoderValue(i, 0);
    }
  }

  for (int kanaal = 0; kanaal < numberOfEncoders; kanaal++) {                                                         // loop for reading encoders
    if (millis() - lastReadout[kanaal] > delayBetweenReads) {
      encoderCurrentValue[kanaal] = sensor.getEncoderValue(kanaal);
      if ((encoderCurrentValue[kanaal] == encoderLatestValue[kanaal])) {
        // if (DEBUG) {
        //   Serial.print("Stap 1: ");
        //   Serial.print(change[kanaal] == true);
        //   Serial.print(" / ");
        //   Serial.print(millis() - lastChange[kanaal]);
        //   Serial.println("");
        // }
        if ((change[kanaal] == true) && (millis() - lastChange[kanaal] > delayBetweenSends)) {
          // if (DEBUG) {
          //   Serial.println("Stap 2");
          // }
          sendEncoderData(kanaal);
          change[kanaal] = false;
          sensor.setEncoderValue(kanaal, 0);
          encoderCurrentValue[kanaal] = 0;
        } else {
          // if (DEBUG) { Serial.println("Stap 3"); }
        }
      } else {
        // if (DEBUG) { Serial.println("Stap 4"); }
        change[kanaal] = true;
        lastChange[kanaal] = millis();
      }
      // if (DEBUG) {
      //   Serial.print(millis());
      //   Serial.print(" ");
      //   Serial.print(encoderCurrentValue[kanaal]);
      //   Serial.print(" ");
      //   Serial.println(encoderLatestValue[kanaal]);
      // }
      encoderLatestValue[kanaal] = encoderCurrentValue[kanaal];
      lastReadout[kanaal] = millis();
    }
  }

  for (int kanaal = 0; kanaal < numberOfEncoders; kanaal++) {                                                         // loop for reading button presses
    if (sensor.getButtonStatus(kanaal) == 0) {
      buttonDown[kanaal] = true;
      Serial.printf("Button %d has been pressed.\n", kanaal);
    }
    if (sensor.getButtonStatus(kanaal) == 1 && buttonDown[kanaal] == true) {
      sendButtonData(kanaal);
      buttonDown[kanaal] = false;
    }
  }

  if (millis() - lastBatteryReadout > delayBetweenBatteryReads) {
    M5.Display.println(M5.Power.getBatteryLevel());
    lastBatteryReadout = millis();
  }

  if (M5.BtnA.wasPressed()) {                                                     // Action when button A was pressed
    M5.Display.println(M5.Power.getBatteryLevel());
    display.sleep();
    //    M5.Display.println("Button A was pressed.");
  }
  if (M5.BtnB.wasPressed()) {                                                     // Action when button B was pressed
    //    M5.Display.println("Button B was pressed.");
    M5.Display.clear();
    M5.Display.setCursor(0, 0);
    M5.Display.println(M5.Power.getBatteryLevel());
  }
  if (M5.BtnC.wasPressed()) {                                                     // Action when button C was pressed
    display.wakeup();
    //    M5.Display.println("Button C was pressed.");
  }

  if (!sensor.getSwitchStatus()) {
    sensor.setAllLEDColor(0x000000);
  }
}
Er staan nog redelijk veel debug-statements in.

Voor degene die de code willen volgen: voor de draaifunctie van de rotary encoders wordt gewacht voor data te verzenden tot een bepaalde draaiknop stabiel wordt? Dit om te beletten dat de server overbelast wordt.

Voor de knoppen (waarbij 0 ingedrukt betekent en 1 los) wordt elke loop gekeken of de knop ingedrukt is, zo ja, wordt een variabele gezet. De boodschap wordt pas verzonden als de knop wordt losgelaten.

Voor de knoppen btnA, btnB en btnC (virtuele knoppen op de Core2): die worden gebruikt om energie te besparen zodat de batterij een beetje gespaard blijft.

Ik ben nog aan het kijken om nog verder te kunnen besparen op energie, dus alle input is welkom.