Skip to content

Instantly share code, notes, and snippets.

@maesoser
Created November 10, 2018 01:09
Show Gist options
  • Save maesoser/645d886cbc73b20381d1031e0f6df94e to your computer and use it in GitHub Desktop.
Save maesoser/645d886cbc73b20381d1031e0f6df94e to your computer and use it in GitHub Desktop.
Door sensor code, both server and client.
package main
import (
"encoding/json"
"fmt"
tb "gopkg.in/tucnak/telebot.v2"
"io/ioutil"
"log"
"math/rand"
"net"
"os"
"strconv"
"strings"
"time"
)
const (
VERSION = "0.1"
)
type config struct {
Id string `json:"id"`
Token string `json:"token"`
Pin int `json:"pin"`
}
func (r config) Recipient() string {
return r.Id
}
func (r config) Save() {
configString, err := json.Marshal(r)
CheckError(err)
confFile, err := os.Create("config.json")
CheckError(err)
defer confFile.Close()
confFile.WriteString(string(configString))
}
func (r config) Load() (config, error) {
content, err := ioutil.ReadFile("config.json")
CheckError(err)
var c config
err = json.Unmarshal(content, &c)
CheckError(err)
return c, err
}
func (r config) isValid(id string) bool {
if len(r.Id) == 0 {
return false
} else if id != r.Id {
return false
}
return true
}
/* A Simple function to verify error */
func CheckError(err error) {
if err != nil {
//fmt.Println("Error: " , err)
log.Fatal(err)
os.Exit(-1)
}
}
func cleanEvents(events []time.Time, tw float64) []time.Time {
nt := time.Now()
deleted := 0
for i := range events {
j := i - deleted
diff := nt.Sub(events[j]).Seconds()
if diff > tw {
events = events[:j+copy(events[j:], events[j+1:])]
deleted++
}
}
return events
}
func checkEvents(events []time.Time, thr int) bool {
l := len(events)
if l >= thr {
return true
}
return false
}
func help() {
fmt.Printf("Usage:\n\t %s [token]", os.Args[0])
os.Exit(-1)
}
func main() {
rand.Seed(time.Now().UTC().UnixNano())
var c config
if len(os.Args) == 1 {
c, _ = c.Load()
if c.Token == "" {
log.Println("Unable to load config, please restart the server")
help()
}
}
if len(os.Args) == 2 {
log.Println("Cold start...")
c.Id = ""
c.Pin = rand.Intn(999999)
c.Token = os.Args[1]
}
if len(os.Args) > 2 {
help()
}
log.Println("Bot token is ", c.Token)
log.Println("ACTIVATION PIN is ", c.Pin)
c.Save()
events := make([]time.Time, 3)
ServerAddr, err := net.ResolveUDPAddr("udp", ":4864")
CheckError(err)
ServerConn, err := net.ListenUDP("udp", ServerAddr)
CheckError(err)
defer ServerConn.Close()
bot, err := tb.NewBot(tb.Settings{
Token: c.Token,
Poller: &tb.LongPoller{Timeout: 10 * time.Second},
})
CheckError(err)
histFile, err := os.Create("history.db")
CheckError(err)
defer histFile.Close()
bot.Handle("/start", func(m *tb.Message) {
bot.Send(m.Sender, "Hello! I'm your door sensor!")
})
bot.Handle("/hist", func(m *tb.Message) {
if c.isValid(m.Sender.Recipient()) == true {
if len(events) > 0 {
last_event := events[len(events)-1]
datestr := last_event.Format("02-01-2006 15:04:05")
log.Printf("Last event at %s", datestr)
bot.Send(m.Sender, "Last event at:", datestr)
}
} else {
bot.Send(m.Sender, "Sensor is not activated yet. Please, use \n\"/activate [PIN]\" to register the sensor.")
}
})
bot.Handle("/activate", func(m *tb.Message) {
if c.isValid(m.Sender.Recipient()) {
bot.Send(m.Sender, "Sensor is already active")
return
}
pinstr := strconv.Itoa(c.Pin)
if strings.Contains(m.Payload, pinstr) {
c.Id = m.Sender.Recipient()
bot.Send(m.Sender, "Sensor is now active!")
log.Println("Sensor has been activated to user", m.Sender.Recipient())
c.Save()
} else {
bot.Send(m.Sender, "Wrong activation code:")
}
})
serve := func() {
log.Println("Server Ready")
buf := make([]byte, 4)
for {
_, addr, err := ServerConn.ReadFromUDP(buf)
CheckError(err)
now := time.Now()
events = append(events, now)
events = cleanEvents(events, 30)
log.Printf("[%s] Sensor triggered!! %d\n", addr, len(events))
if checkEvents(events, 3) {
events = events[len(events):]
log.Printf(" --- Warning user ---\n")
bot.Send(c, "Sensor triggered!")
nowStr := now.Format("02-01-2006 15:04:05")
_, err := histFile.WriteString(nowStr + "\n")
CheckError(err)
}
}
}
go serve()
bot.Start()
}
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#define WLAN_SSID ""
#define WLAN_PASSWD ""
#define FAST_CONN_RETRIES 60
#define MAX_CONN_RETRIES 100
// The ESP8266 RTC memory is arranged into blocks of 4 bytes. The access methods read and write 4 bytes at a time,
// so the RTC data structure should be padded to a 4-byte multiple.
struct {
uint32_t crc32; // 4 bytes
uint8_t channel; // 1 byte, 5 in total
uint8_t ap_mac[6]; // 6 bytes, 11 in total
uint8_t padding; // 1 byte, 12 in total
} rtcData;
uint32_t calculateCRC32( const uint8_t *data, size_t length ) {
uint32_t crc = 0xffffffff;
while( length-- ) {
uint8_t c = *data++;
for( uint32_t i = 0x80; i > 0; i >>= 1 ) {
bool bit = crc & 0x80000000;
if( c & i ) {
bit = !bit;
}
crc <<= 1;
if( bit ) {
crc ^= 0x04c11db7;
}
}
}
return crc;
}
void setup() {
// Declare addresses
IPAddress broadcast( 192, 168, 0, 255 );
IPAddress ip( 192, 168, 0, 248 );
IPAddress gateway( 192, 168, 0, 1 );
IPAddress subnet( 255, 255, 255, 0 );
int target_port = 4864;
// Try to read WiFi settings from RTC memory
bool rtcValid = false;
if( ESP.rtcUserMemoryRead( 0, (uint32_t*)&rtcData, sizeof( rtcData ) ) ) {
// Calculate the CRC of what we just read from RTC memory, but skip the first 4 bytes as that's the checksum itself.
uint32_t crc = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 );
if( crc == rtcData.crc32 ) {
rtcValid = true;
}
}
// Configure an connect to AP
WiFi.persistent( false );
WiFi.mode( WIFI_STA );
WiFi.config( ip, gateway, subnet );
if( rtcValid ) {
// The RTC data was good, make a quick connection
WiFi.begin( WLAN_SSID, WLAN_PASSWD, rtcData.channel, rtcData.ap_mac, true );
}
else {
// The RTC data was not valid, so make a regular connection
WiFi.begin( WLAN_SSID, WLAN_PASSWD );
}
int retries = 0;
int wifiStatus = WiFi.status();
while( wifiStatus != WL_CONNECTED ) {
retries++;
if( retries == FAST_CONN_RETRIES ) {
WiFi.disconnect();
delay( 10 );
WiFi.forceSleepBegin();
delay( 10 );
WiFi.forceSleepWake();
delay( 10 );
WiFi.begin( WLAN_SSID, WLAN_PASSWD );
}
if( retries == MAX_CONN_RETRIES ) {
ESP.deepSleep( 0 );
}
delay( 50 );
wifiStatus = WiFi.status();
}
// Write current connection info back to RTC
rtcData.channel = WiFi.channel();
memcpy( rtcData.ap_mac, WiFi.BSSID(), 6 ); // Copy 6 bytes of BSSID (AP's MAC address)
rtcData.crc32 = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 );
ESP.rtcUserMemoryWrite( 0, (uint32_t*)&rtcData, sizeof( rtcData ) );
// Send UDP Packet to broadcast
WiFiUDP Udp;
Udp.beginPacket(broadcast, target_port);
Udp.write("");
Udp.endPacket();
delay(5);
/*
WiFi.mode( WIFI_OFF );
WiFi.forceSleepBegin();
delay( 4000 );
*/
ESP.deepSleep(0);
}
void loop() {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment