├── .gitignore ├── images └── sensor.PNG ├── errors.go ├── Makefile ├── go.mod ├── radio.go ├── sensors.go ├── gps.go ├── LICENSE ├── verify ├── scd40 │ └── main.go └── gps │ └── main.go ├── main.go ├── lorawan.go ├── go.sum └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # exclude everything 2 | keys/* 3 | 4 | 5 | -------------------------------------------------------------------------------- /images/sensor.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManoloEvans/firewatch-sensor/HEAD/images/sensor.PNG -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | var ( 6 | errUnableToJoin = errors.New("Unable to join LoRaWAN network") 7 | errNoKeys = errors.New("No LoRaWAN keys provided") 8 | ) 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | flash: 2 | @tinygo flash -size short -target lorae5 -ldflags="-X main.appEUI='$(shell cat ./keys/appeui)' -X main.devEUI='$(shell cat ./keys/deveui)' -X main.appKey='$(shell cat ./keys/appkey)'" . 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ManoloEvans/firewatch-sensor 2 | 3 | go 1.20 4 | 5 | require tinygo.org/x/drivers v0.24.1-0.20230328175227-cba17ef125b0 6 | 7 | require github.com/TheThingsNetwork/go-cayenne-lib v1.1.0 8 | 9 | replace github.com/TheThingsNetwork/go-cayenne-lib => github.com/tinygo-org/go-cayenne-lib v0.0.0-20230116181903-6c670c96018c 10 | -------------------------------------------------------------------------------- /radio.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | 6 | "errors" 7 | 8 | "tinygo.org/x/drivers/lora" 9 | "tinygo.org/x/drivers/sx126x" 10 | ) 11 | 12 | var ( 13 | spi = machine.SPI3 14 | 15 | loraRadio *sx126x.Device 16 | ) 17 | 18 | // do sx126x setup here 19 | func setupLora() (lora.Radio, error) { 20 | loraRadio = sx126x.New(spi) 21 | loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262) 22 | 23 | loraRadio.SetRadioController(sx126x.NewRadioControl()) 24 | 25 | if state := loraRadio.DetectDevice(); !state { 26 | return nil, errors.New("LoRa radio not found") 27 | } 28 | 29 | return loraRadio, nil 30 | } 31 | -------------------------------------------------------------------------------- /sensors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | 6 | "tinygo.org/x/drivers/scd4x" 7 | ) 8 | 9 | var ( 10 | // devices 11 | co2Sensor *scd4x.Device 12 | 13 | // readings 14 | co2SensorReading int32 15 | temperature int32 16 | humidity int32 17 | ) 18 | 19 | // startSensors initializes the sensors. 20 | func startSensors() { 21 | machine.I2C0.Configure(machine.I2CConfig{}) 22 | 23 | co2Sensor = scd4x.New(machine.I2C0) 24 | co2Sensor.Configure() 25 | 26 | if err := co2Sensor.StartPeriodicMeasurement(); err != nil { 27 | println(err) 28 | } 29 | } 30 | 31 | // readSensors reads the sensors. 32 | func readSensors() { 33 | temperature, _ = co2Sensor.ReadTemperature() 34 | co2SensorReading, _ = co2Sensor.ReadCO2() 35 | humidity, _ = co2Sensor.ReadHumidity() 36 | 37 | println("Temperature", temperature, "°C") 38 | println("Humidity", humidity, "%") 39 | println("CO2", co2SensorReading, "ppm") 40 | 41 | } 42 | -------------------------------------------------------------------------------- /gps.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | 6 | "time" 7 | 8 | "tinygo.org/x/drivers/gps" 9 | ) 10 | 11 | var fix gps.Fix 12 | 13 | // startGPS initializes the GPS. 14 | func startGPS() { 15 | machine.UART2.Configure(machine.UARTConfig{BaudRate: 9600, TX: machine.UART2_TX_PIN, RX: machine.UART2_RX_PIN}) 16 | ublox := gps.NewUART(machine.UART2) 17 | parser := gps.NewParser() 18 | for { 19 | s, err := ublox.NextSentence() 20 | if err != nil { 21 | continue 22 | } 23 | 24 | newfix, err := parser.Parse(s) 25 | if err != nil { 26 | continue 27 | } 28 | if newfix.Valid { 29 | fix = newfix 30 | print(fix.Time.Format("15:04:05")) 31 | print(", lat=") 32 | print(fix.Latitude) 33 | print(", long=") 34 | print(fix.Longitude) 35 | print(", altitude=", fix.Altitude) 36 | print(", satellites=", fix.Satellites) 37 | if fix.Speed != 0 { 38 | print(", speed=") 39 | print(fix.Speed) 40 | } 41 | if fix.Heading != 0 { 42 | print(", heading=") 43 | print(fix.Heading) 44 | } 45 | println() 46 | } 47 | time.Sleep(200 * time.Millisecond) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Manolo Rosario Evans Diaz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /verify/scd40/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | "time" 6 | 7 | "tinygo.org/x/drivers/scd4x" 8 | ) 9 | 10 | var ( 11 | i2c = machine.I2C0 12 | sensor = scd4x.New(i2c) 13 | ) 14 | 15 | func main() { 16 | time.Sleep(1500 * time.Millisecond) 17 | 18 | machine.PA9.Configure(machine.PinConfig{Mode: machine.PinOutput}) 19 | machine.PA9.High() 20 | 21 | time.Sleep(1500 * time.Millisecond) 22 | 23 | i2c.Configure(machine.I2CConfig{}) 24 | 25 | time.Sleep(1500 * time.Millisecond) 26 | if err := sensor.Configure(); err != nil { 27 | println(err) 28 | } 29 | 30 | time.Sleep(1500 * time.Millisecond) 31 | 32 | if err := sensor.StartPeriodicMeasurement(); err != nil { 33 | println(err) 34 | } 35 | 36 | time.Sleep(1500 * time.Millisecond) 37 | 38 | for { 39 | co2, err := sensor.ReadCO2() 40 | humidity, err := sensor.ReadHumidity() 41 | temperature, err := sensor.ReadTemperature() 42 | temperature = temperature / 1000 43 | 44 | if err != nil { 45 | println(err) 46 | } 47 | println("Temperature", temperature, "°C") 48 | println("Humidity", humidity, "%") 49 | println("CO2", co2, "ppm") 50 | time.Sleep(time.Second) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /verify/gps/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "machine" 5 | "time" 6 | 7 | "tinygo.org/x/drivers/gps" 8 | ) 9 | 10 | func main() { 11 | println("GPS UART Example") 12 | // Turn on power to pins. 13 | machine.PA9.Configure(machine.PinConfig{Mode: machine.PinOutput}) 14 | machine.PA9.High() 15 | machine.UART2.Configure(machine.UARTConfig{BaudRate: 9600, TX: machine.UART2_TX_PIN, RX: machine.UART2_RX_PIN}) 16 | ublox := gps.NewUART(machine.UART2) 17 | parser := gps.NewParser() 18 | var fix gps.Fix 19 | for { 20 | s, err := ublox.NextSentence() 21 | if err != nil { 22 | println(err) 23 | continue 24 | } 25 | 26 | fix, err = parser.Parse(s) 27 | if err != nil { 28 | println(err) 29 | continue 30 | } 31 | if fix.Valid { 32 | print(fix.Time.Format("15:04:05")) 33 | print(", lat=") 34 | print(fix.Latitude) 35 | print(", long=") 36 | print(fix.Longitude) 37 | print(", altitude=", fix.Altitude) 38 | print(", satellites=", fix.Satellites) 39 | if fix.Speed != 0 { 40 | print(", speed=") 41 | print(fix.Speed) 42 | } 43 | if fix.Heading != 0 { 44 | print(", heading=") 45 | print(fix.Heading) 46 | } 47 | println() 48 | } else { 49 | println("No fix") 50 | } 51 | time.Sleep(200 * time.Millisecond) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "tinygo.org/x/drivers/lora" 7 | "tinygo.org/x/drivers/lora/lorawan" 8 | "tinygo.org/x/drivers/lora/lorawan/region" 9 | 10 | "machine" 11 | ) 12 | 13 | func main() { 14 | time.Sleep(5 * time.Second) 15 | println("*** Firewatch 1 starting... ***") 16 | 17 | machine.POWER_EN3V3.Configure(machine.PinConfig{Mode: machine.PinOutput}) 18 | machine.POWER_EN3V3.High() 19 | 20 | time.Sleep(1500 * time.Millisecond) 21 | 22 | // setup LoRa radio 23 | var err error 24 | radio, err = setupLora() 25 | if err != nil { 26 | failMessage(err) 27 | } 28 | 29 | // Connect LoRaWAN to use the LoRa Radio device. 30 | lorawan.UseRadio(radio) 31 | 32 | // use EU868 DR2 spreading factor for high-altitude 33 | settings := region.EU868() 34 | settings.UplinkChannel().SpreadingFactor = lora.SpreadingFactor10 35 | lorawan.UseRegionSettings(settings) 36 | 37 | // Try to connect to the LoRaWAN network 38 | if err := lorawanJoin(); err != nil { 39 | failMessage(err) 40 | } 41 | 42 | go startGPS() 43 | 44 | startSensors() 45 | 46 | for { 47 | println("Sleeping for", uplinkDelaySeconds, "seconds") 48 | time.Sleep(time.Second * uplinkDelaySeconds) 49 | 50 | readSensors() 51 | 52 | payload, err := createPayload() 53 | if err != nil { 54 | println("Payload error:", err) 55 | continue 56 | } 57 | 58 | if err := lorawan.SendUplink(payload, session); err != nil { 59 | println("Uplink error:", err) 60 | continue 61 | } 62 | 63 | println("Uplink complete, msglen=", len(payload)) 64 | } 65 | } 66 | 67 | func failMessage(err error) { 68 | for { 69 | println("FATAL:", err) 70 | time.Sleep(time.Second) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lorawan.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "time" 6 | 7 | "tinygo.org/x/drivers/lora" 8 | "tinygo.org/x/drivers/lora/lorawan" 9 | 10 | cayennelpp "github.com/TheThingsNetwork/go-cayenne-lib" 11 | ) 12 | 13 | const ( 14 | joinTimeoutSeconds = 180 15 | reconnectDelaySeconds = 15 16 | uplinkDelaySeconds = 30 17 | ) 18 | 19 | var ( 20 | radio lora.Radio 21 | 22 | session *lorawan.Session = &lorawan.Session{} 23 | otaa *lorawan.Otaa = &lorawan.Otaa{} 24 | 25 | encoder cayennelpp.Encoder = cayennelpp.NewEncoder() 26 | ) 27 | 28 | // lorawanJoin joins the LoRaWAN network. 29 | func lorawanJoin() error { 30 | // Configure AppEUI, DevEUI, APPKey 31 | if err := setLorawanKeys(); err != nil { 32 | return err 33 | } 34 | 35 | start := time.Now() 36 | for time.Since(start) < joinTimeoutSeconds*time.Second { 37 | println("Trying to join LoRaWAN network") 38 | err := lorawan.Join(otaa, session) 39 | if err == nil { 40 | println("Connected to LoRaWAN network!") 41 | 42 | return nil 43 | } 44 | println("Join error:", err, "retrying in", reconnectDelaySeconds, "sec") 45 | time.Sleep(time.Second * reconnectDelaySeconds) 46 | } 47 | 48 | err := errUnableToJoin 49 | println(err.Error()) 50 | return err 51 | } 52 | 53 | // createPayload creates the payload to send with CayenneLPP. 54 | func createPayload() ([]byte, error) { 55 | encoder.Reset() 56 | 57 | // SCD40 Humidity 58 | encoder.AddRelativeHumidity(1, float64(humidity)) 59 | 60 | // SCD40 Temperature 61 | encoder.AddTemperature(2, float64(temperature)/1000) 62 | 63 | // SCD40 CO2 64 | encoder.AddAnalogInput(3, float64(co2SensorReading)/1000) 65 | 66 | // GPS 67 | if fix.Valid { 68 | println(float64(fix.Latitude), float64(fix.Longitude), float64(fix.Altitude)) 69 | encoder.AddGPS(7, float64(fix.Latitude), float64(fix.Longitude), float64(fix.Altitude)) 70 | } 71 | 72 | payload := encoder.Bytes() 73 | println(hex.EncodeToString(payload)) 74 | return payload, nil 75 | 76 | } 77 | 78 | var ( 79 | appEUI string 80 | devEUI string 81 | appKey string 82 | ) 83 | 84 | // setLorawanKeys sets the LoRaWAN keys. 85 | func setLorawanKeys() error { 86 | if appEUI == "" || devEUI == "" || appKey == "" { 87 | return errNoKeys 88 | } 89 | 90 | appEUIData, err := hex.DecodeString(appEUI) 91 | if err != nil { 92 | return err 93 | } 94 | otaa.SetAppEUI(appEUIData) 95 | 96 | devEUIData, err := hex.DecodeString(devEUI) 97 | if err != nil { 98 | return err 99 | } 100 | otaa.SetDevEUI(devEUIData) 101 | 102 | appKeyData, err := hex.DecodeString(appKey) 103 | if err != nil { 104 | return err 105 | } 106 | otaa.SetAppKey(appKeyData) 107 | 108 | lorawan.SetPublicNetwork(true) 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bgould/http v0.0.0-20190627042742-d268792bdee7/go.mod h1:BTqvVegvwifopl4KTEDth6Zezs9eR+lCWhvGKvkxJHE= 2 | github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= 3 | github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk= 4 | github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= 5 | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= 6 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 7 | github.com/hajimehoshi/go-jisx0208 v1.0.0/go.mod h1:yYxEStHL7lt9uL+AbdWgW9gBumwieDoZCiB1f/0X0as= 8 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 9 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 10 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 11 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 12 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 13 | github.com/sago35/go-bdf v0.0.0-20200313142241-6c17821c91c4/go.mod h1:rOebXGuMLsXhZAC6mF/TjxONsm45498ZyzVhel++6KM= 14 | github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= 15 | github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= 16 | github.com/tinygo-org/go-cayenne-lib v0.0.0-20230116181903-6c670c96018c h1:dT/cBU6zI10Hz/Euz4ymhKjyTen4XjVZJzzp3va84qM= 17 | github.com/tinygo-org/go-cayenne-lib v0.0.0-20230116181903-6c670c96018c/go.mod h1:HTTus7UIBhXKvLIeNGybbG1o7wr4zwwVbbUXwFqrtp0= 18 | github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= 19 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 20 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 21 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 23 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 24 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 25 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 26 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 27 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 28 | tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= 29 | tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= 30 | tinygo.org/x/drivers v0.16.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= 31 | tinygo.org/x/drivers v0.19.0/go.mod h1:uJD/l1qWzxzLx+vcxaW0eY464N5RAgFi1zTVzASFdqI= 32 | tinygo.org/x/drivers v0.24.1-0.20230328175227-cba17ef125b0 h1:aIOuViTLrJ7qM0yoZT4QgQj3NHhvdicX3FH4bD/3Eqw= 33 | tinygo.org/x/drivers v0.24.1-0.20230328175227-cba17ef125b0/go.mod h1:J4+51Li1kcfL5F93kmnDWEEzQF3bLGz0Am3Q7E2a8/E= 34 | tinygo.org/x/tinyfont v0.2.1/go.mod h1:eLqnYSrFRjt5STxWaMeOWJTzrKhXqpWw7nU3bPfKOAM= 35 | tinygo.org/x/tinyfont v0.3.0/go.mod h1:+TV5q0KpwSGRWnN+ITijsIhrWYJkoUCp9MYELjKpAXk= 36 | tinygo.org/x/tinyfs v0.1.0/go.mod h1:ysc8Y92iHfhTXeyEM9+c7zviUQ4fN9UCFgSOFfMWv20= 37 | tinygo.org/x/tinyfs v0.2.0/go.mod h1:6ZHYdvB3sFYeMB3ypmXZCNEnFwceKc61ADYTYHpep1E= 38 | tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4= 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # firewatch-sensor 2 | This repository contains the code for a firewatch module(all components of a sensor put together). The code initializes the co2 sensor, which can also measure humidity and temperature, starts and secures a lorawan connection to the things network, and creates and sends a payload to the things network. The code is in tinygo, which I have had no previous experience in until I started this project. 3 | 4 | ## Table of Contents 5 | 6 | - [Aim of the Project](#aim-of-the-project) 7 | - [Hardware Requirements](#hardware-requirements) 8 | - [Software Requirements](#software-requirements) 9 | - [Wiring the Sensor Module](#wiring-the-sensor-module) 10 | - [Sensor Module](#sensor-module) 11 | - [Setup](#setup) 12 | - [Usage](#usage) 13 | 14 | 15 | ## Aim of the Project 16 | The goal of this project is to create a prototype for what would be a cluster of sensors in remote areas to detect wildfires. Nowadays there are ever increasing wildfire occurring throughout the world causing billions of dollars in damages. Hopefully this project will be able to notify respective authorities of wildfires and save resources. 17 | 18 | ## Hardware Requirements 19 | The following components are required to build the sensor module: 20 | 21 | - Lora E5 Devboard: I am using this board since it has a lora RF and mcu chip installed. And for the sake of creating a prototype sensor makes everything easier to have it all in one. 22 | - ST-LINK V2: This is a debugger and programmer for the lora e5 devboard. It is used to flash the code onto the devboard. 23 | - SCD40 Co2, humidity, temperature sensor: The SCD40 is an accurrate sensor that can be used outdoors. It measures co2 directly whereas other sensors approximate it from VOC gas concentration. It also measures humidity and temperature. 24 | - GPS: A GPS module is required to obtain the sensors location. This is important for the sensor to be able to send its location to the things network. 25 | 26 | ## Software Requirements 27 | 28 | The following software tools are required to build the sensor module: 29 | 30 | - Tinygo: Tinygo is a go compiler for microcontrollers. It is used to compile the code for the lora e5 devboard. 31 | - Make: Make is a tool that is used to automate the build process. It is used to compile the code for the lora e5 devboard. 32 | - TTN Console: This is a cloud based platform for the LoRaWAN network, which allows the sensor module to communicate with the cloud. The TTN console is used to register the sensor and to obtain the necessary keys to connect to the things network. 33 | - VS Code: VS Code is a code editor that is used to write the code for the sensor module. It is also used to debug the code. 34 | 35 | 36 | ## Wiring the Sensor Module 37 | ### SCD40 Sensor 38 | | SCD40 | Lora E5 IIC | 39 | | --- | -------------- | 40 | | VIN | 3v3 | 41 | | GND | GND | 42 | | SCL | SCL | 43 | | SDA | SDA | 44 | 45 | ### GPS 46 | | GPS | Lora E5 USART | 47 | | --- | ------------- | 48 | | VCC | 3v3 | 49 | | GND | GND | 50 | | TX | RX2 | 51 | | RX | TX2 | 52 | 53 | ### ST-LINK V2 54 | | ST-LINK V2 | Lora E5 SWD/SWIM | 55 | | ---------- | ---------------- | 56 | | SWCLK | CLK | 57 | | SWDIO | DIO | 58 | | GND | GND | 59 | | 3v3 | VCC | 60 | 61 | ## Sensor Module 62 | 63 | This is what it looks like: 64 | 65 | ![Sensor](images/sensor.PNG) 66 | 67 | ## Setup 68 | 69 | Follow these steps to setup the environment: 70 | 1. Install Tinygo 71 | 2. Connect the ST-LINK V2 to the Lora E5 devboard 72 | 3. Connect the SCD40 sensor to the Lora E5 devboard 73 | 4. Connect the GPS to the Lora E5 devboard 74 | 5. Clone this repository 75 | 6. Create a TTN application and register the a gateway and end device. Follow the instructions [here](https://www.thethingsnetwork.org/docs/gateways/registration.html) 76 | 7. Copy the appkey, appeui, and the deveui keys from the TTN console into a folder called keys, a file with corresponding labels for each key. 77 | 8. Run the following command to flash the code onto the lora e5 devboard: 78 | 79 | ```make``` 80 | 81 | ## Usage 82 | 83 | The Firewatch sensor module will continuously measure CO2, temperature, humidity, and location, sending the data to the Things Network every 30 seconds using the LoRaWAN protocol. You can view the data on the TTN Console dashboard. 84 | --------------------------------------------------------------------------------