mealsowheels

TallyWhacker

Jun 4th, 2021
2,985
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //TallyWhacker over WIFI using MQTT and Home Assistant with OTA Support
  2. //Tried to get deep sleep operational but couldn't. Got other shit I want to sniff..
  3. //Most of the code was copied from:
  4. //https://community.home-assistant.io/t/report-the-temperature-with-esp8266-to-mqtt/42489
  5.  
  6. //Requires PubSubClient library found here: https://github.com/knolleary/pubsubclient
  7. #include <PubSubClient.h>
  8. #include <ESP8266WiFi.h>
  9. #include <ArduinoOTA.h>
  10. #include <Servo.h>
  11.  
  12. void callback(char* topic, byte* payload, unsigned int length);
  13.  
  14. #define MQTT_SERVER "<IP_ADDY>" //IP addy of MQTT broker eg. 192.168.1.4
  15. const char* ssid = "<SSID>"; //name of broadcasted wifi access point
  16. const char* password = "<SSID_PW>"; //wifi password
  17.  
  18. int relayOutput = 15; //GPIO15, labeled as D8 on nodemcu esp8266
  19. int servoOutput = 12; //GPIO12, labeled as D6 on nodemcu esp8266
  20. //servoSetLeft indicates if the servo arm is over the ESP or over the batteries
  21. //0xffff indicates its over the ESP; 0x0000 for batteries
  22. uint32_t servoSetLeft;
  23. //tallyOn indicates if the TallyWhacker should be operating
  24. //0xffff indicates it's operating; 0x0000 for not
  25. uint32_t tallyOn;
  26. //random number used for the whacking interval formula
  27. long randomNumber = 0;
  28. //last time tallywhacked
  29. uint32_t previousWhack = 0;
  30.  
  31. //This is the MQTT Topic that will be used to manage if the TallyWhacker should be whacking
  32. //Refer to my YAML component entry
  33. char const* tallywhacker = "home/tallywhacker";
  34.  
  35. Servo servo;
  36. WiFiClient wifiClient;
  37. PubSubClient client(MQTT_SERVER, 1883, callback, wifiClient);
  38.  
  39. void setup()
  40. {
  41. //initialize the switch as an output and set to LOW (off)
  42. pinMode(relayOutput, OUTPUT);
  43. digitalWrite(relayOutput, HIGH);
  44. servo.attach(servoOutput);
  45. servo.write(90);
  46. delay(500);
  47. digitalWrite(relayOutput, LOW);
  48.  
  49. //A name given to your ESP8266 module when discovering it as a port in ARDUINO IDE
  50. ArduinoOTA.setHostname("TallyWhacker");
  51. ArduinoOTA.begin(); // OTA initialization
  52.  
  53. // Serial.begin(115200);
  54. // delay(100);
  55. // Serial.println("Serial initialized.");
  56.  
  57. //start wifi subsystem
  58. WiFi.begin(ssid, password);
  59. //attempt to connect to the WIFI network and then connect to the MQTT server
  60. reconnect();
  61.  
  62. //check if random number is inRTC memory; if so, initialize it to 0x0000
  63. ESP.rtcUserMemoryRead(33, &servoSetLeft, sizeof(servoSetLeft));
  64. delay(10);
  65. // Serial.print("Memread servoSetLeft: "); Serial.println(servoSetLeft);
  66. if(servoSetLeft != 0x0000 || servoSetLeft != 0xffff)
  67. {
  68. servoSetLeft = 0x0000;
  69. ESP.rtcUserMemoryWrite(33, &servoSetLeft, sizeof(servoSetLeft));
  70. delay(10);
  71. }
  72.  
  73. //check if random number is inRTC memory; if so, initialize it to 0x0000
  74. ESP.rtcUserMemoryRead(34, &tallyOn, sizeof(tallyOn));
  75. delay(10);
  76. // Serial.print("Memread tallyOn: "); Serial.println(tallyOn);
  77. if(tallyOn != 0x0000 || tallyOn != 0xffff)
  78. {
  79. tallyOn = 0x0000;
  80. ESP.rtcUserMemoryWrite(34, &tallyOn, sizeof(tallyOn));
  81. delay(15);
  82. }
  83.  
  84. randomSeed(ESP.random());
  85.  
  86. //wait a bit before starting the main loop
  87. delay(500);
  88. }
  89.  
  90.  
  91. void loop()
  92. {
  93. //reconnect if connection is lost
  94. if (!client.connected() && WiFi.status() == 3) {reconnect();}
  95.  
  96. ArduinoOTA.handle();
  97.  
  98. //maintain MQTT connection
  99. client.loop();
  100. //MUST delay to allow ESP8266 WIFI functions to run
  101. delay(10);
  102.  
  103. ESP.rtcUserMemoryRead(34, &tallyOn, sizeof(tallyOn));
  104. delay(10);
  105. // Serial.print("Loop memread tallyOn: "); Serial.println(tallyOn);
  106. if(tallyOn == 0xffff)
  107. {
  108. uint32_t time = millis();
  109. if(time - previousWhack >= randomNumber)
  110. {
  111. previousWhack = millis();
  112. //set random interval between whackings
  113. randomNumber= random(4, 34)*1000; //5-35 seconds
  114.  
  115. //read from RTC memory, the last position of the servo.
  116. //servoSetLeft == 0xffff means the servo arm is over the ESP
  117. ESP.rtcUserMemoryRead(33, &servoSetLeft, sizeof(servoSetLeft));
  118. delay(10);
  119. if(servoSetLeft == 0x0000)
  120. {
  121. digitalWrite(relayOutput, HIGH);
  122. servo.write(50);
  123. delay(1500);
  124. digitalWrite(relayOutput, LOW);
  125. servoSetLeft = 0xffff;
  126. ESP.rtcUserMemoryWrite(33, &servoSetLeft, sizeof(servoSetLeft));
  127. delay(10);
  128. }
  129. else if(servoSetLeft == 0xffff)
  130. {
  131. digitalWrite(relayOutput, HIGH);
  132. servo.write(140);
  133. delay(1500);
  134. digitalWrite(relayOutput, LOW);
  135. servoSetLeft = 0x0000;
  136. ESP.rtcUserMemoryWrite(33, &servoSetLeft, sizeof(servoSetLeft));
  137. delay(10);
  138. }
  139. }
  140. }
  141. }
  142.  
  143. void callback(char* topic, byte* payload, unsigned int length)
  144. {
  145.  
  146. //convert topic to string to make it easier to work with
  147. //Note: the "topic" value gets overwritten everytime it receives confirmation (callback) message from MQTT
  148. String topicStr = topic;
  149. Serial.println(topicStr);
  150.  
  151. Serial.println(servoSetLeft);
  152. if (topicStr == "home/tallywhacker")
  153. {
  154. //turn the tallywhacker on if the payload is '1' and publish to the MQTT server a confirmation message
  155. if(payload[0] == '1')
  156. {
  157. tallyOn = 0xffff;
  158. ESP.rtcUserMemoryWrite(34, &tallyOn, sizeof(tallyOn));
  159. delay(10);
  160. Serial.print("Callback tallyOn: "); Serial.println(tallyOn);
  161. client.publish("home/tallyConfirm", "1");
  162. }
  163. //turn the tallywhacker off if the payload is '0' and publish to the MQTT server a confirmation message
  164. else if (payload[0] == '0')
  165. {
  166. tallyOn = 0x0000;
  167. ESP.rtcUserMemoryWrite(34, &tallyOn, sizeof(tallyOn));
  168. delay(10);
  169. Serial.print("Callback tallyOn: "); Serial.println(tallyOn);
  170. client.publish("home/tallyConfirm", "0");
  171. }
  172. }
  173. }
  174.  
  175.  
  176. void reconnect()
  177. {
  178. //attempt to connect to the wifi if connection is lost
  179. if(WiFi.status() != WL_CONNECTED)
  180. {
  181. //loop while we wait for connection
  182. while (WiFi.status() != WL_CONNECTED)
  183. {
  184. delay(500);
  185. }
  186. }
  187.  
  188. //make sure we are connected to WIFI before attemping to reconnect to MQTT
  189. if(WiFi.status() == WL_CONNECTED)
  190. {
  191. // Loop until we're reconnected to the MQTT server
  192. while (!client.connected())
  193. {
  194. // Generate client name based on MAC address and last 8 bits of microsecond counter
  195. String clientName;
  196. clientName += "tallywhacker-";
  197. uint8_t mac[6];
  198. WiFi.macAddress(mac);
  199. clientName += macToStr(mac);
  200.  
  201. //if connected, subscribe to the topic(s) we want to be notified about
  202. //Delete "tallywhacker", and "<password>" here if you are not using any password
  203. //<password> is for your MQTT broker
  204. if (client.connect((char*) clientName.c_str(), "tallywhacker", "<password>"))
  205. { //Update accordingly with your MQTT account
  206. client.subscribe(tallywhacker);
  207. }
  208. //otherwise print failed for debugging
  209. else{abort();}
  210. }
  211. }
  212. }
  213.  
  214. //generate unique name from MAC addr
  215. String macToStr(const uint8_t* mac)
  216. {
  217. String result;
  218.  
  219. for (int i = 0; i < 6; ++i)
  220. {
  221. result += String(mac[i], 16);
  222. if (i < 5)
  223. {
  224. result += ':';
  225. }
  226. }
  227. return result;
  228. }
RAW Paste Data