Created
November 10, 2018 01:09
-
-
Save maesoser/645d886cbc73b20381d1031e0f6df94e to your computer and use it in GitHub Desktop.
Door sensor code, both server and client.
This file contains 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
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() | |
} |
This file contains 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
#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