diff --git a/.gitignore b/.gitignore
index 06256b71..6b2c0902 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,4 +17,11 @@
.idea/
.vs
.vscode
-docs/
\ No newline at end of file
+docs/
+*.kicad_sch-bak
+*-backups/
+*.bak
+_autosave*
+fp-info-cache
+.~*
+*.plan
diff --git a/PCB/Sensor Boards/BMI270-SensorBoard.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Community/BMI270-SensorBoard.brd
similarity index 100%
rename from PCB/Sensor Boards/BMI270-SensorBoard.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Community/BMI270-SensorBoard.brd
diff --git a/PCB/Sensor Boards/BMI270-SensorBoard.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Community/BMI270-SensorBoard.sch
similarity index 100%
rename from PCB/Sensor Boards/BMI270-SensorBoard.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Community/BMI270-SensorBoard.sch
diff --git a/PCB/Sensor Boards/OSO-SWAB-A1-00 Temperature + Test Points.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Shipped/OSO-SWAB-A1-00 Temperature + Test Points.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-SWAB-A1-00 Temperature + Test Points.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Shipped/OSO-SWAB-A1-00 Temperature + Test Points.brd
diff --git a/PCB/Sensor Boards/OSO-SWAB-A1-00 Temperature + Test Points.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Shipped/OSO-SWAB-A1-00 Temperature + Test Points.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-SWAB-A1-00 Temperature + Test Points.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Shipped/OSO-SWAB-A1-00 Temperature + Test Points.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-002 Sensor Watch Environment.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-002 Sensor Watch Environment.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-002 Sensor Watch Environment.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-002 Sensor Watch Environment.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-002 Sensor Watch Environment.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-002 Sensor Watch Environment.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-002 Sensor Watch Environment.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-002 Sensor Watch Environment.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-016 Power Test.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-016 Power Test.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-016 Power Test.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-016 Power Test.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-016 Power Test.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-016 Power Test.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-016 Power Test.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-016 Power Test.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-017 Sensor Watch Flash.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-017 Sensor Watch Flash.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-017 Sensor Watch Flash.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-017 Sensor Watch Flash.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-017 Sensor Watch Flash.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-017 Sensor Watch Flash.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-017 Sensor Watch Flash.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-017 Sensor Watch Flash.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-019 Sensor Watch Test.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-019 Sensor Watch Test.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-019 Sensor Watch Test.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-019 Sensor Watch Test.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-019 Sensor Watch Test.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-019 Sensor Watch Test.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-019 Sensor Watch Test.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-019 Sensor Watch Test.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-021 Sensor Watch Motion II.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-021 Sensor Watch Motion II.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-021 Sensor Watch Motion II.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-021 Sensor Watch Motion II.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-021 Sensor Watch Motion II.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-021 Sensor Watch Motion II.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-021 Sensor Watch Motion II.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-21-021 Sensor Watch Motion II.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-22-005 UART Breakout.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-22-005 UART Breakout.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-22-005 UART Breakout.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-22-005 UART Breakout.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-22-005 UART Breakout.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-22-005 UART Breakout.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-22-005 UART Breakout.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Tested/OSO-MISC-22-005 UART Breakout.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-001 Sensor Watch Express.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-001 Sensor Watch Express.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-001 Sensor Watch Express.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-001 Sensor Watch Express.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-001 Sensor Watch Express.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-001 Sensor Watch Express.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-001 Sensor Watch Express.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-001 Sensor Watch Express.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-003 Sensor Watch Motion.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-003 Sensor Watch Motion.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-003 Sensor Watch Motion.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-003 Sensor Watch Motion.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-003 Sensor Watch Motion.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-003 Sensor Watch Motion.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-003 Sensor Watch Motion.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-003 Sensor Watch Motion.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-004 Sensor Watch Testpoints.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-004 Sensor Watch Testpoints.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-004 Sensor Watch Testpoints.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-004 Sensor Watch Testpoints.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-004 Sensor Watch Testpoints.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-004 Sensor Watch Testpoints.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-004 Sensor Watch Testpoints.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-004 Sensor Watch Testpoints.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-008 Hiking Log.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-008 Hiking Log.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-008 Hiking Log.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-008 Hiking Log.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-008 Hiking Log.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-008 Hiking Log.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-008 Hiking Log.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-008 Hiking Log.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-009 Simple Thermistor Board.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-009 Simple Thermistor Board.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-009 Simple Thermistor Board.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-009 Simple Thermistor Board.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-009 Simple Thermistor Board.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-009 Simple Thermistor Board.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-009 Simple Thermistor Board.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-009 Simple Thermistor Board.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-010 Light Sensor.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-010 Light Sensor.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-010 Light Sensor.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-010 Light Sensor.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-010 Light Sensor.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-010 Light Sensor.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-010 Light Sensor.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-010 Light Sensor.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-011 RFID.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-011 RFID.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-011 RFID.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-011 RFID.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-011 RFID.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-011 RFID.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-011 RFID.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-011 RFID.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-012 Sensor Watch Testpoints v2.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-012 Sensor Watch Testpoints v2.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-012 Sensor Watch Testpoints v2.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-012 Sensor Watch Testpoints v2.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-012 Sensor Watch Testpoints v2.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-012 Sensor Watch Testpoints v2.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-012 Sensor Watch Testpoints v2.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-012 Sensor Watch Testpoints v2.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-015 Temperature and Light Board.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-015 Temperature and Light Board.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-015 Temperature and Light Board.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-015 Temperature and Light Board.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-015 Temperature and Light Board.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-015 Temperature and Light Board.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-015 Temperature and Light Board.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-015 Temperature and Light Board.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-018 Temperature Board.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-018 Temperature Board.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-018 Temperature Board.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-018 Temperature Board.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-018 Temperature Board.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-018 Temperature Board.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-018 Temperature Board.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-018 Temperature Board.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-020 Temperature + GPIO Board.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-020 Temperature + GPIO Board.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-020 Temperature + GPIO Board.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-020 Temperature + GPIO Board.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-020 Temperature + GPIO Board.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-020 Temperature + GPIO Board.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-020 Temperature + GPIO Board.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-020 Temperature + GPIO Board.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-022 Temperature + GPIO + I2C Board.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-022 Temperature + GPIO + I2C Board.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-022 Temperature + GPIO + I2C Board.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-022 Temperature + GPIO + I2C Board.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-022 Temperature + GPIO + I2C Board.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-022 Temperature + GPIO + I2C Board.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-022 Temperature + GPIO + I2C Board.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-022 Temperature + GPIO + I2C Board.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-023 Temperature + GPIO + I2C Alternate.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-023 Temperature + GPIO + I2C Alternate.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-023 Temperature + GPIO + I2C Alternate.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-023 Temperature + GPIO + I2C Alternate.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-023 Temperature + GPIO + I2C Alternate.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-023 Temperature + GPIO + I2C Alternate.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-023 Temperature + GPIO + I2C Alternate.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-023 Temperature + GPIO + I2C Alternate.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-21-024 Temperature + GPIO + I2C Alternate 2.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-024 Temperature + GPIO + I2C Alternate 2.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-024 Temperature + GPIO + I2C Alternate 2.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-024 Temperature + GPIO + I2C Alternate 2.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-21-024 Temperature + GPIO + I2C Alternate 2.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-024 Temperature + GPIO + I2C Alternate 2.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-21-024 Temperature + GPIO + I2C Alternate 2.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-21-024 Temperature + GPIO + I2C Alternate 2.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-22-001 Temperature + GPIO + I2C.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-001 Temperature + GPIO + I2C.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-22-001 Temperature + GPIO + I2C.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-001 Temperature + GPIO + I2C.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-22-001 Temperature + GPIO + I2C.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-001 Temperature + GPIO + I2C.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-22-001 Temperature + GPIO + I2C.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-001 Temperature + GPIO + I2C.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-22-002 Sensor Watch Motion Express.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-002 Sensor Watch Motion Express.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-22-002 Sensor Watch Motion Express.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-002 Sensor Watch Motion Express.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-22-002 Sensor Watch Motion Express.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-002 Sensor Watch Motion Express.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-22-002 Sensor Watch Motion Express.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-002 Sensor Watch Motion Express.sch
diff --git a/PCB/Sensor Boards/OSO-MISC-22-004 WIP-NFC.brd b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-004 WIP-NFC.brd
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-22-004 WIP-NFC.brd
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-004 WIP-NFC.brd
diff --git a/PCB/Sensor Boards/OSO-MISC-22-004 WIP-NFC.sch b/PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-004 WIP-NFC.sch
similarity index 100%
rename from PCB/Sensor Boards/OSO-MISC-22-004 WIP-NFC.sch
rename to PCB/Sensor Boards/Eagle (Legacy Boards)/Untested - WIP - Not Recommended/OSO-MISC-22-004 WIP-NFC.sch
diff --git a/PCB/Sensor Boards/OSO-SWAB-A1-00 Temperature + Test Points_2022-01-26.zip b/PCB/Sensor Boards/Gerbers/OSO-SWAB-A1-00 Temperature + Test Points_2022-01-26.zip
similarity index 100%
rename from PCB/Sensor Boards/OSO-SWAB-A1-00 Temperature + Test Points_2022-01-26.zip
rename to PCB/Sensor Boards/Gerbers/OSO-SWAB-A1-00 Temperature + Test Points_2022-01-26.zip
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_dru b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_dru
new file mode 100644
index 00000000..6ac34d2f
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_dru
@@ -0,0 +1 @@
+(version 1)
\ No newline at end of file
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_pcb b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_pcb
new file mode 100644
index 00000000..e32ca94b
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_pcb
@@ -0,0 +1,493 @@
+(kicad_pcb (version 20211014) (generator pcbnew)
+
+ (general
+ (thickness 0.19)
+ )
+
+ (paper "A4")
+ (layers
+ (0 "F.Cu" signal)
+ (31 "B.Cu" signal)
+ (32 "B.Adhes" user "B.Adhesive")
+ (33 "F.Adhes" user "F.Adhesive")
+ (34 "B.Paste" user)
+ (35 "F.Paste" user)
+ (36 "B.SilkS" user "B.Silkscreen")
+ (37 "F.SilkS" user "F.Silkscreen")
+ (38 "B.Mask" user)
+ (39 "F.Mask" user)
+ (40 "Dwgs.User" user "User.Drawings")
+ (41 "Cmts.User" user "User.Comments")
+ (42 "Eco1.User" user "User.Eco1")
+ (43 "Eco2.User" user "User.Eco2")
+ (44 "Edge.Cuts" user)
+ (45 "Margin" user)
+ (46 "B.CrtYd" user "B.Courtyard")
+ (47 "F.CrtYd" user "F.Courtyard")
+ (48 "B.Fab" user)
+ (49 "F.Fab" user)
+ (50 "User.1" user)
+ (51 "User.2" user)
+ (52 "User.3" user)
+ (53 "User.4" user)
+ (54 "User.5" user)
+ (55 "User.6" user)
+ (56 "User.7" user)
+ (57 "User.8" user)
+ (58 "User.9" user)
+ )
+
+ (setup
+ (stackup
+ (layer "F.SilkS" (type "Top Silk Screen") (color "White"))
+ (layer "F.Paste" (type "Top Solder Paste"))
+ (layer "F.Mask" (type "Top Solder Mask") (color "#CC66004D") (thickness 0.01))
+ (layer "F.Cu" (type "copper") (thickness 0.035))
+ (layer "dielectric 1" (type "core") (thickness 0.1) (material "Polyimide") (epsilon_r 3.2) (loss_tangent 0.004))
+ (layer "B.Cu" (type "copper") (thickness 0.035))
+ (layer "B.Mask" (type "Bottom Solder Mask") (color "#CC66004D") (thickness 0.01))
+ (layer "B.Paste" (type "Bottom Solder Paste"))
+ (layer "B.SilkS" (type "Bottom Silk Screen") (color "White"))
+ (copper_finish "None")
+ (dielectric_constraints no)
+ )
+ (pad_to_mask_clearance 0)
+ (pcbplotparams
+ (layerselection 0x00010fc_ffffffff)
+ (disableapertmacros false)
+ (usegerberextensions false)
+ (usegerberattributes true)
+ (usegerberadvancedattributes true)
+ (creategerberjobfile true)
+ (svguseinch false)
+ (svgprecision 6)
+ (excludeedgelayer true)
+ (plotframeref false)
+ (viasonmask false)
+ (mode 1)
+ (useauxorigin false)
+ (hpglpennumber 1)
+ (hpglpenspeed 20)
+ (hpglpendiameter 15.000000)
+ (dxfpolygonmode true)
+ (dxfimperialunits true)
+ (dxfusepcbnewfont true)
+ (psnegative false)
+ (psa4output false)
+ (plotreference true)
+ (plotvalue true)
+ (plotinvisibletext false)
+ (sketchpadsonfab false)
+ (subtractmaskfromsilk false)
+ (outputformat 1)
+ (mirror false)
+ (drillshape 0)
+ (scaleselection 1)
+ (outputdirectory "OSO-SWAB-B1-00")
+ )
+ )
+
+ (net 0 "")
+ (net 1 "/A1_RX")
+ (net 2 "/A2_TX")
+ (net 3 "/A4_INT1")
+ (net 4 "GND")
+ (net 5 "/A3_INT2")
+ (net 6 "/SDA")
+ (net 7 "/SCL")
+ (net 8 "VCC")
+ (net 9 "/A0_UNUSED")
+
+ (footprint "OSO-MISC-21-021 Sensor Watch Motion II:FH19C9S05SH10-FFC" (layer "F.Cu")
+ (tedit 0) (tstamp c450d786-1278-4a96-8a62-a0d27859c515)
+ (at 153.2255 106.7461)
+ (descr "FH19C-9S-0.5SH(10)-1
\n")
+ (property "Sheetfile" "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch")
+ (property "Sheetname" "")
+ (path "/4dc3cbb6-fb37-42a6-81e4-ab57e8e35700")
+ (fp_text reference "U$3" (at 0 0) (layer "F.SilkS") hide
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ (tstamp 9040ae45-373a-45fc-adb3-fcfaa02764b2)
+ )
+ (fp_text value "FH19C-9S-0.5SH_10-FFC" (at 0 0) (layer "F.Fab") hide
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ (tstamp 5af4c90c-a654-4d3a-80bb-293a78da42b9)
+ )
+ (fp_poly (pts
+ (xy 2.56543 3.556)
+ (xy -2.56537 3.556)
+ (xy -2.56643 0.898)
+ (xy 2.56437 0.898)
+ ) (layer "B.Mask") (width 0) (fill solid) (tstamp 21283db5-1009-43c5-a3ef-4954974a3a30))
+ (pad "1" smd rect (at 2 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 8 "VCC") (pinfunction "1") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp b2e7f160-81a2-4f3a-9cac-d6cba543336c))
+ (pad "2" smd rect (at 1.5 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 4 "GND") (pinfunction "2") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp 4a8a2808-da08-4211-a839-b545ca46843d))
+ (pad "3" smd rect (at 1 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 3 "/A4_INT1") (pinfunction "3") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp 1ad63a96-1ef2-463e-9ea7-81ba46599172))
+ (pad "4" smd rect (at 0.5 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 5 "/A3_INT2") (pinfunction "4") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp 44970b45-f7b0-47cc-a114-209df0456d8f))
+ (pad "5" smd rect (at 0 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 2 "/A2_TX") (pinfunction "5") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp 16a60cfb-4140-483f-9675-22643c201dcd))
+ (pad "6" smd rect (at -0.5 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 1 "/A1_RX") (pinfunction "6") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp edf03a5b-8be1-48f8-8f16-4719d937182d))
+ (pad "7" smd rect (at -1 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 6 "/SDA") (pinfunction "7") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp b54a21f9-d0ee-47bd-8a33-6d2ccb2f1964))
+ (pad "8" smd rect (at -1.5 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 7 "/SCL") (pinfunction "8") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp 496964dd-0d25-423f-9576-ddcd7d92b9c0))
+ (pad "9" smd rect (at -2 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (net 9 "/A0_UNUSED") (pinfunction "9") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp 6a847b13-3d48-4df7-8dd7-d515f51aacbf))
+ )
+
+ (footprint "OSO-MISC-21-021 Sensor Watch Motion II:LGA12R50P4X4_200X200X70" (layer "B.Cu")
+ (tedit 0) (tstamp 38449293-6eb3-4fce-a813-980a83af30a5)
+ (at 145.3261 101.9201 180)
+ (property "Sheetfile" "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch")
+ (property "Sheetname" "")
+ (path "/f61a4c5e-e4e7-40df-976e-1899d4c8a12a")
+ (fp_text reference "U2" (at 0.02175 2.6201) (layer "B.SilkS")
+ (effects (font (size 0.406899 0.406899) (thickness 0.101101)) (justify left mirror))
+ (tstamp 2fb12f0c-a2cb-4979-8ff3-7261f4205a8f)
+ )
+ (fp_text value "LIS2DW12TR" (at -1.501141 -2.001519) (layer "B.Fab")
+ (effects (font (size 0.406974 0.406974) (thickness 0.101026)) (justify left mirror))
+ (tstamp 79ae9206-147d-40c8-b439-c5e1aa71ef7c)
+ )
+ (fp_line (start -1.05 -0.555) (end -1.05 -1.05) (layer "B.SilkS") (width 0.127) (tstamp 02ae2e5e-0358-428d-9b47-799126b254ae))
+ (fp_line (start 1.05 -1.05) (end 1.05 -0.555) (layer "B.SilkS") (width 0.127) (tstamp 29d50b3c-acc8-4899-ab05-b18d21ab3b3c))
+ (fp_line (start 0.555 1.05) (end 1.05 1.05) (layer "B.SilkS") (width 0.127) (tstamp 3ccbbde2-8e04-400e-bf38-caa624eba487))
+ (fp_line (start -1.05 0.555) (end -1.05 1.05) (layer "B.SilkS") (width 0.127) (tstamp 418272bc-4a1c-4292-abcc-02abb40a91b1))
+ (fp_line (start 1.05 1.05) (end 1.05 0.555) (layer "B.SilkS") (width 0.127) (tstamp 6b1bdfd4-bd5f-4649-8e19-6c646bd31d41))
+ (fp_line (start -1.05 1.05) (end -0.555 1.05) (layer "B.SilkS") (width 0.127) (tstamp b9246ad8-1e96-4d28-8417-be94c523f7fa))
+ (fp_line (start 0.555 -1.05) (end 1.05 -1.05) (layer "B.SilkS") (width 0.127) (tstamp bfa111e0-c472-4d7f-9f9f-eb6d8452f0bc))
+ (fp_line (start -1.05 -1.05) (end -0.555 -1.05) (layer "B.SilkS") (width 0.127) (tstamp ed58876d-2aff-4c39-a693-85bc921546dc))
+ (fp_circle (center -1.5 0.75) (end -1.45 0.75) (layer "B.SilkS") (width 0.1) (fill none) (tstamp eed4a4a1-5971-4468-a0eb-4adabc23ee91))
+ (fp_line (start -1.25 -1.25) (end -1.25 1.25) (layer "B.CrtYd") (width 0.05) (tstamp 0c936eb0-2bed-42c5-a43a-a8c2138d10f6))
+ (fp_line (start 1.25 1.25) (end 1.25 -1.25) (layer "B.CrtYd") (width 0.05) (tstamp b9af5acd-a7ff-4d1f-a5af-28344932ba28))
+ (fp_line (start -1.25 1.25) (end 1.25 1.25) (layer "B.CrtYd") (width 0.05) (tstamp df602652-97d0-42fe-8c45-0c9e9cdbab8a))
+ (fp_line (start 1.25 -1.25) (end -1.25 -1.25) (layer "B.CrtYd") (width 0.05) (tstamp e6004e06-b498-4cbf-a2cd-4065cdd9ac30))
+ (fp_line (start 1 1) (end 1 -1) (layer "B.Fab") (width 0.127) (tstamp 04b92bea-33f5-431d-87c9-1b951c5a0895))
+ (fp_line (start 1 -1) (end -1 -1) (layer "B.Fab") (width 0.127) (tstamp 8c6e9c84-beb1-4bc6-b6c0-93f5915e6d31))
+ (fp_line (start -1 -1) (end -1 1) (layer "B.Fab") (width 0.127) (tstamp c65c8660-c3aa-4e44-a24a-c0839e96ffe1))
+ (fp_line (start -1 1) (end 1 1) (layer "B.Fab") (width 0.127) (tstamp c9623f6f-a650-4e25-8bd7-692927ca8f9d))
+ (fp_circle (center -0.75 0.75) (end -0.7 0.75) (layer "B.Fab") (width 0.1) (fill none) (tstamp 06bb0748-9b94-4559-8b9c-a59aa1c8ab55))
+ (pad "1" smd rect (at -0.76 0.75 180) (size 0.4 0.35) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 7 "/SCL") (pinfunction "SCL/SPC") (pintype "input") (solder_mask_margin 0.0635) (tstamp 3db7e36b-889f-4f74-9156-b304ec17949b))
+ (pad "2" smd rect (at -0.76 0.25 180) (size 0.4 0.35) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 8 "VCC") (pinfunction "CS") (pintype "input") (solder_mask_margin 0.0635) (tstamp 358727b9-955e-4b0e-8f93-faac9b591622))
+ (pad "3" smd rect (at -0.76 -0.25 180) (size 0.4 0.35) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 8 "VCC") (pinfunction "SDO/SDA0") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp bf192f28-5a1f-4b77-af0f-de5ad059ed8c))
+ (pad "4" smd rect (at -0.76 -0.75 180) (size 0.4 0.35) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 6 "/SDA") (pinfunction "SDA/SDI/SDO") (pintype "bidirectional") (solder_mask_margin 0.0635) (tstamp ec19640d-d714-4214-b3c1-ade84ffe1184))
+ (pad "5" smd rect (at -0.25 -0.76 180) (size 0.35 0.4) (layers "B.Cu" "B.Paste" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp 98922bc7-e895-48ee-8f8c-8718a306a6e9))
+ (pad "6" smd rect (at 0.25 -0.76 180) (size 0.35 0.4) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 4 "GND") (pinfunction "GND") (pintype "power_in") (solder_mask_margin 0.0635) (tstamp 1ae80af8-dac6-46a2-88cc-ea6877872716))
+ (pad "7" smd rect (at 0.76 -0.75 180) (size 0.4 0.35) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 4 "GND") (pinfunction "RES") (pintype "passive") (solder_mask_margin 0.0635) (tstamp ed3d45a3-b988-41b3-8d80-5013f0b90226))
+ (pad "8" smd rect (at 0.76 -0.25 180) (size 0.4 0.35) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 4 "GND") (pinfunction "GND") (pintype "power_in") (solder_mask_margin 0.0635) (tstamp afff67d6-aa97-4848-86fb-883a57bddf5a))
+ (pad "9" smd rect (at 0.76 0.25 180) (size 0.4 0.35) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 8 "VCC") (pinfunction "VDD") (pintype "power_in") (solder_mask_margin 0.0635) (tstamp a4de6280-2a67-4f62-9a7b-e57bae02df74))
+ (pad "10" smd rect (at 0.76 0.75 180) (size 0.4 0.35) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 8 "VCC") (pinfunction "VDD_IO") (pintype "power_in") (solder_mask_margin 0.0635) (tstamp 861bccff-e93d-4aab-b5fb-31ba7ca49f23))
+ (pad "11" smd rect (at 0.25 0.76 180) (size 0.35 0.4) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 5 "/A3_INT2") (pinfunction "INT2") (pintype "output") (solder_mask_margin 0.0635) (tstamp 8581ef53-a74b-453a-a79a-1ad12a5b60be))
+ (pad "12" smd rect (at -0.25 0.76 180) (size 0.35 0.4) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 3 "/A4_INT1") (pinfunction "INT1") (pintype "output") (solder_mask_margin 0.0635) (tstamp 651bf078-2bd8-4627-9a4d-396860ade5d0))
+ )
+
+ (footprint "OSO-MISC-21-021 Sensor Watch Motion II:_0603MP" (layer "B.Cu")
+ (tedit 0) (tstamp 52f3f076-84fe-4d87-b520-c9ec8ac43cd8)
+ (at 142.4051 101.3359 90)
+ (descr "0603 MicroPitch")
+ (property "Sheetfile" "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch")
+ (property "Sheetname" "")
+ (path "/dbf111d5-5941-4c1b-8fad-675981ce34f4")
+ (fp_text reference "C2" (at 2.0359 0.3949 180) (layer "B.SilkS")
+ (effects (font (size 0.388031 0.388031) (thickness 0.119969)) (justify left mirror))
+ (tstamp 9bd4d62d-7a56-42ce-b3f5-7a6d01978996)
+ )
+ (fp_text value "10uF" (at -0.9525 -0.9525 90) (layer "B.Fab")
+ (effects (font (size 0.442163 0.442163) (thickness 0.065837)) (justify left mirror))
+ (tstamp dd43d085-ec5b-4b38-8a0a-a3e36d67876d)
+ )
+ (fp_poly (pts
+ (xy -0.1999 -0.25)
+ (xy 0.1999 -0.25)
+ (xy 0.1999 0.25)
+ (xy -0.1999 0.25)
+ ) (layer "B.Adhes") (width 0) (fill solid) (tstamp bf433100-c31b-441a-977d-11c8dfea5e5d))
+ (fp_line (start 0 0.254) (end 0 -0.254) (layer "B.SilkS") (width 0.2032) (tstamp c0b3ca6d-cbf4-4a4a-829a-bd12e3d6f696))
+ (fp_line (start 0.432 0.306) (end -0.432 0.306) (layer "B.Fab") (width 0.1016) (tstamp 62361dc9-703b-4d4c-9ead-64531412f98d))
+ (fp_line (start -0.432 -0.306) (end 0.432 -0.306) (layer "B.Fab") (width 0.1016) (tstamp f78d3c08-172c-4642-a357-be3cd6805e97))
+ (fp_poly (pts
+ (xy 0.4318 -0.4)
+ (xy 0.8 -0.4)
+ (xy 0.8 0.4)
+ (xy 0.4318 0.4)
+ ) (layer "B.Fab") (width 0) (fill solid) (tstamp 430dd9e0-a77c-40c2-a434-88901e1d63f9))
+ (fp_poly (pts
+ (xy -0.8 -0.4)
+ (xy -0.4318 -0.4)
+ (xy -0.4318 0.4)
+ (xy -0.8 0.4)
+ ) (layer "B.Fab") (width 0) (fill solid) (tstamp e99999ac-764f-42aa-af5f-f6b490544976))
+ (pad "1" smd rect (at -0.762 0 90) (size 0.8 0.8) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 4 "GND") (pinfunction "1") (pintype "passive") (solder_mask_margin 0.0635) (tstamp 05f47ce8-fbff-4c88-a713-924ebec9cd7f))
+ (pad "2" smd rect (at 0.762 0 90) (size 0.8 0.8) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 8 "VCC") (pinfunction "2") (pintype "passive") (solder_mask_margin 0.0635) (tstamp 3418d403-467d-4e3f-8239-51594743a971))
+ )
+
+ (footprint "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (layer "B.Cu")
+ (tedit 0) (tstamp 53397a02-3122-469f-8a6e-5586e1d48a70)
+ (at 144.8435 104.4601 180)
+ (descr "TEST PAD")
+ (property "Sheetfile" "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch")
+ (property "Sheetname" "")
+ (path "/1936aa8e-84b5-486e-a2d5-81ae44fd41c7")
+ (fp_text reference "TP3" (at -1.0668 0.3302) (layer "B.SilkS") hide
+ (effects (font (size 0.30226 0.30226) (thickness 0.075565)) (justify left mirror))
+ (tstamp a286de89-e15e-41ca-972a-92b40dbbd887)
+ )
+ (fp_text value "TPB1,27" (at -0.635 -0.762) (layer "B.Fab")
+ (effects (font (size 0.503794 0.503794) (thickness 0.004206)) (justify left mirror))
+ (tstamp 0f58640a-48e4-4c58-8582-c0d0f03f67c9)
+ )
+ (pad "TP" smd roundrect (at 0 0 180) (size 1.27 1.27) (layers "B.Cu" "B.Mask") (roundrect_rratio 0.5)
+ (net 2 "/A2_TX") (pinfunction "TP") (pintype "input") (solder_mask_margin 0.0635) (tstamp 37a22dff-bef4-4a54-aa97-606b44b0539d))
+ )
+
+ (footprint "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (layer "B.Cu")
+ (tedit 0) (tstamp 8492a13d-c2e7-4353-90d5-a0194203e30e)
+ (at 146.2659 105.0951 180)
+ (descr "TEST PAD")
+ (property "Sheetfile" "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch")
+ (property "Sheetname" "")
+ (path "/a4b4c4fb-1c78-4a88-b6d6-c95ced14e6a1")
+ (fp_text reference "TP4" (at -1.0668 0) (layer "B.SilkS") hide
+ (effects (font (size 0.30226 0.30226) (thickness 0.075565)) (justify left mirror))
+ (tstamp ec765bc0-6a42-4727-9951-7e00656f3a23)
+ )
+ (fp_text value "TPB1,27" (at -0.635 -0.762) (layer "B.Fab")
+ (effects (font (size 0.503794 0.503794) (thickness 0.004206)) (justify left mirror))
+ (tstamp 4d040dbd-23bd-4b9e-9905-fecd7f7a7e91)
+ )
+ (pad "TP" smd roundrect (at 0 0 180) (size 1.27 1.27) (layers "B.Cu" "B.Mask") (roundrect_rratio 0.5)
+ (net 1 "/A1_RX") (pinfunction "TP") (pintype "input") (solder_mask_margin 0.0635) (tstamp 964091ca-8bbe-4ac8-aaf6-8ee79894dac0))
+ )
+
+ (footprint "OSO-MISC-21-021 Sensor Watch Motion II:_0603MP" (layer "B.Cu")
+ (tedit 0) (tstamp 92f1bd8b-af09-4166-bc64-aef654c0183a)
+ (at 143.6243 101.3359 -90)
+ (descr "0603 MicroPitch")
+ (property "Sheetfile" "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch")
+ (property "Sheetname" "")
+ (path "/81f117f8-2e22-4d95-9843-a6438cc9b8fd")
+ (fp_text reference "C1" (at -2.0359 0.3243 -180) (layer "B.SilkS")
+ (effects (font (size 0.388031 0.388031) (thickness 0.119969)) (justify right mirror))
+ (tstamp 16e2b64b-b1f1-4a7b-a067-eb93b95619a3)
+ )
+ (fp_text value "0.1uF" (at -0.9525 -0.9525 -270) (layer "B.Fab")
+ (effects (font (size 0.442163 0.442163) (thickness 0.065837)) (justify right mirror))
+ (tstamp e0be7d00-bf2d-45a1-865e-97f1376ca4dd)
+ )
+ (fp_poly (pts
+ (xy -0.1999 -0.25)
+ (xy 0.1999 -0.25)
+ (xy 0.1999 0.25)
+ (xy -0.1999 0.25)
+ ) (layer "B.Adhes") (width 0) (fill solid) (tstamp 737141da-8a5b-4eb5-a94c-2d98ea03ea2b))
+ (fp_line (start 0 0.254) (end 0 -0.254) (layer "B.SilkS") (width 0.2032) (tstamp 3cb29946-354e-4d69-8954-0cb5df5feb34))
+ (fp_line (start -0.432 -0.306) (end 0.432 -0.306) (layer "B.Fab") (width 0.1016) (tstamp 58356900-2195-4f68-aa48-0fbeed8a0258))
+ (fp_line (start 0.432 0.306) (end -0.432 0.306) (layer "B.Fab") (width 0.1016) (tstamp d1fc816b-12b7-4411-becf-b39225ef5a06))
+ (fp_poly (pts
+ (xy 0.4318 -0.4)
+ (xy 0.8 -0.4)
+ (xy 0.8 0.4)
+ (xy 0.4318 0.4)
+ ) (layer "B.Fab") (width 0) (fill solid) (tstamp 372669d9-06c5-4965-b34a-bd1ca4aba83d))
+ (fp_poly (pts
+ (xy -0.8 -0.4)
+ (xy -0.4318 -0.4)
+ (xy -0.4318 0.4)
+ (xy -0.8 0.4)
+ ) (layer "B.Fab") (width 0) (fill solid) (tstamp 3d2b07eb-66f2-4ca0-8b52-77b42ce79ea8))
+ (pad "1" smd rect (at -0.762 0 270) (size 0.8 0.8) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 8 "VCC") (pinfunction "1") (pintype "passive") (solder_mask_margin 0.0635) (tstamp f408e8b3-d3b1-4975-afa3-6edd4111b541))
+ (pad "2" smd rect (at 0.762 0 270) (size 0.8 0.8) (layers "B.Cu" "B.Paste" "B.Mask")
+ (net 4 "GND") (pinfunction "2") (pintype "passive") (solder_mask_margin 0.0635) (tstamp 5fefcc14-df85-4c73-a324-fe9d5f1fa478))
+ )
+
+ (footprint "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (layer "B.Cu")
+ (tedit 0) (tstamp d47e36a3-5090-4cf4-bf91-6b011b5fab8a)
+ (at 142.3035 103.3044 180)
+ (descr "TEST PAD")
+ (property "Sheetfile" "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch")
+ (property "Sheetname" "")
+ (path "/6ea9dd18-adcc-4aba-8efa-d45943bee5d6")
+ (fp_text reference "TP1" (at 0.4064 -1.2954 270) (layer "B.SilkS") hide
+ (effects (font (size 0.30226 0.30226) (thickness 0.075565)) (justify right mirror))
+ (tstamp 8643f50e-4c54-42e3-8f4e-9e7c565345a4)
+ )
+ (fp_text value "TPB1,27" (at -0.635 -0.762) (layer "B.Fab")
+ (effects (font (size 0.503794 0.503794) (thickness 0.004206)) (justify left mirror))
+ (tstamp 0ca4bbc5-6d45-40ae-a4b8-b0264327e89b)
+ )
+ (pad "TP" smd roundrect (at 0 0 180) (size 1.27 1.27) (layers "B.Cu" "B.Mask") (roundrect_rratio 0.5)
+ (net 4 "GND") (pinfunction "TP") (pintype "input") (solder_mask_margin 0.0635) (tstamp b69db92a-8dc0-4427-8729-230fe7829975))
+ )
+
+ (footprint "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (layer "B.Cu")
+ (tedit 0) (tstamp d5562657-0bb5-4045-b7d6-e98fd391f4a3)
+ (at 143.3195 104.4601 180)
+ (descr "TEST PAD")
+ (property "Sheetfile" "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch")
+ (property "Sheetname" "")
+ (path "/ec24969b-8391-44db-a79f-3fa50f7e4597")
+ (fp_text reference "TP2" (at -0.4064 0.8636) (layer "B.SilkS") hide
+ (effects (font (size 0.30226 0.30226) (thickness 0.075565)) (justify left mirror))
+ (tstamp 3eb69f50-8679-4783-841b-f3e56c6658ed)
+ )
+ (fp_text value "TPB1,27" (at -0.635 -0.762) (layer "B.Fab")
+ (effects (font (size 0.503794 0.503794) (thickness 0.004206)) (justify left mirror))
+ (tstamp de2ea6ae-7e35-4c49-af6d-0ddbb267d18e)
+ )
+ (pad "TP" smd roundrect (at 0 0 180) (size 1.27 1.27) (layers "B.Cu" "B.Mask") (roundrect_rratio 0.5)
+ (net 8 "VCC") (pinfunction "TP") (pintype "input") (solder_mask_margin 0.0635) (tstamp 1b224beb-e21a-4340-ac2c-31b7fa1f7f8e))
+ )
+
+ (gr_line (start 155.7147 105.0951) (end 147.5105 105.0951) (layer "F.SilkS") (width 0.127) (tstamp 09b40b31-a75f-48c5-94ff-dac04c6dfaf8))
+ (gr_line (start 147.0025 100.5231) (end 147.0025 104.5871) (layer "F.SilkS") (width 0.127) (tstamp 59eee44c-9efd-402f-80e1-02593f15460b))
+ (gr_arc (start 146.2405 99.7611) (mid 146.779315 99.984285) (end 147.0025 100.5231) (layer "F.SilkS") (width 0.127) (tstamp a8fc3609-a41b-4706-8233-14786995acaa))
+ (gr_arc (start 147.5105 105.0951) (mid 147.15129 104.94631) (end 147.0025 104.5871) (layer "F.SilkS") (width 0.127) (tstamp d204339c-8036-43d2-8aa8-268d985f6138))
+ (gr_line (start 144.2 105.9) (end 144.7 105.9) (layer "F.Mask") (width 0.1) (tstamp 45263c40-98a9-4f74-a47a-9075d46d8655))
+ (gr_line (start 150.7363 106.7461) (end 150.7363 110.0961) (layer "Edge.Cuts") (width 0.05) (tstamp 2af795e8-c9a1-416f-b2d3-8be4310d296e))
+ (gr_arc (start 150.8863 110.2461) (mid 150.780234 110.202166) (end 150.7363 110.0961) (layer "Edge.Cuts") (width 0.05) (tstamp 58502658-0694-45f6-94e7-5aca0f57af62))
+ (gr_line (start 150.1013 106.1111) (end 145.3515 106.1111) (layer "Edge.Cuts") (width 0.05) (tstamp 5e539d5b-e49f-4f77-9569-2a4261264736))
+ (gr_line (start 144.7165 105.4761) (end 142.1765 105.4761) (layer "Edge.Cuts") (width 0.05) (tstamp 6ac0bb29-69f1-4b1d-b893-957a8916a1bb))
+ (gr_arc (start 144.7165 105.4761) (mid 145.165513 105.662087) (end 145.3515 106.1111) (layer "Edge.Cuts") (width 0.05) (tstamp 6b9e5d6b-5222-465f-9b1e-93af00265572))
+ (gr_arc (start 150.1013 106.1111) (mid 150.550313 106.297087) (end 150.7363 106.7461) (layer "Edge.Cuts") (width 0.05) (tstamp 7f938717-4068-4e5c-92a0-7210bbdd7788))
+ (gr_arc (start 155.3337 99.7611) (mid 155.603108 99.872692) (end 155.7147 100.1421) (layer "Edge.Cuts") (width 0.05) (tstamp 9b7def2b-3536-42b1-8e65-4b8e423042d7))
+ (gr_arc (start 142.1765 105.4761) (mid 141.547882 105.215718) (end 141.2875 104.5871) (layer "Edge.Cuts") (width 0.05) (tstamp 9b9870d6-49b4-40a8-b847-cb5468c63035))
+ (gr_line (start 155.7147 100.1421) (end 155.7147 106.7461) (layer "Edge.Cuts") (width 0.05) (tstamp a6bc6301-abbd-4acf-a77d-1f4e4d107e79))
+ (gr_line (start 155.5647 110.2461) (end 150.8863 110.2461) (layer "Edge.Cuts") (width 0.05) (tstamp ae418f09-9594-49fa-ba14-c5ebf3097fc7))
+ (gr_line (start 142.1765 99.7611) (end 155.3337 99.7611) (layer "Edge.Cuts") (width 0.05) (tstamp b5d214ca-8d2c-4892-a145-ebcdf525979d))
+ (gr_arc (start 141.2875 100.6501) (mid 141.547882 100.021482) (end 142.1765 99.7611) (layer "Edge.Cuts") (width 0.05) (tstamp c4d82428-36d7-43c8-9bfb-b2beed9359df))
+ (gr_line (start 155.7147 106.7461) (end 155.7147 110.0961) (layer "Edge.Cuts") (width 0.05) (tstamp dc2cbabd-f370-406b-b50e-bb3846cd95ff))
+ (gr_arc (start 155.7147 110.0961) (mid 155.670766 110.202166) (end 155.5647 110.2461) (layer "Edge.Cuts") (width 0.05) (tstamp e1f2d86b-fcc6-43fe-bbcc-417ac237e0bd))
+ (gr_line (start 141.2875 104.5871) (end 141.2875 100.6501) (layer "Edge.Cuts") (width 0.05) (tstamp e960ff40-d2dc-4f31-aa56-c3592606d2b4))
+ (gr_text "TX" (at 144.9 103.3) (layer "B.SilkS") (tstamp 2cd2031e-bdc6-4b09-a5d6-627ebbcee21c)
+ (effects (font (size 0.4 0.4) (thickness 0.075)) (justify top mirror))
+ )
+ (gr_text "RX" (at 146.3 103.9) (layer "B.SilkS") (tstamp 5e112429-2dc1-437d-8792-2c6553eb0f93)
+ (effects (font (size 0.4 0.4) (thickness 0.075)) (justify top mirror))
+ )
+ (gr_text "GND" (at 141.8 104.4 -90) (layer "B.SilkS") (tstamp 72b454f6-3d75-4170-9db1-78a51d4c0e67)
+ (effects (font (size 0.4 0.4) (thickness 0.075)) (justify top mirror))
+ )
+ (gr_text "3V" (at 142.4 105) (layer "B.SilkS") (tstamp f65b8e04-fbde-4466-addc-36be3d61856b)
+ (effects (font (size 0.4 0.4) (thickness 0.075)) (justify top mirror))
+ )
+ (gr_text "I²C: LIS2DW12 (0x19)\nA1/A2: UART RX/TX\nA3/A4: INT2/INT1\n" (at 151.4475 102.8853) (layer "F.SilkS") (tstamp 06718050-7404-4593-8900-5367d4556e53)
+ (effects (font (size 0.44704 0.44704) (thickness 0.06096)) (justify top))
+ )
+ (gr_text "OSO-SWAB-B2-00\nSW Accelerometer:\nDeveloper Preview" (at 151.4475 100.0913) (layer "F.SilkS") (tstamp 1a8b05a9-bccd-4ebd-8312-af5738af914c)
+ (effects (font (size 0.499872 0.499872) (thickness 0.109728)) (justify top))
+ )
+
+ (segment (start 149.73135 105.23315) (end 149.5933 105.0951) (width 0.2032) (layer "F.Cu") (net 1) (tstamp 0bae9b38-d794-4972-b292-0160f8d78e32))
+ (segment (start 148.0693 105.0951) (end 149.5933 105.0951) (width 0.2032) (layer "F.Cu") (net 1) (tstamp 46023c51-300f-4e42-afb4-010034d44dac))
+ (segment (start 151.20785 105.23315) (end 149.73135 105.23315) (width 0.2032) (layer "F.Cu") (net 1) (tstamp 52c0f5dc-54c3-478f-9860-ccb50104bb0b))
+ (via (at 148.0693 105.0951) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 1) (tstamp e39680a8-e74a-46a6-a545-ac5ad0aa7f34))
+ (via (at 151.20785 105.23315) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 1) (tstamp fbd986c1-ed36-4026-b812-e3868a5e2ad8))
+ (segment (start 146.2659 105.0951) (end 148.0693 105.0951) (width 0.2032) (layer "B.Cu") (net 1) (tstamp 3a4f4d1c-4c47-490a-b760-933c1c17288d))
+ (segment (start 152.7255 106.7508) (end 151.20785 105.23315) (width 0.2032) (layer "B.Cu") (net 1) (tstamp 5a7537b8-356a-483f-afb1-7f541c63a2bb))
+ (segment (start 152.7255 108.9961) (end 152.7255 106.7508) (width 0.2032) (layer "B.Cu") (net 1) (tstamp 8ff819c8-6284-4bec-859f-4051f056dd5d))
+ (segment (start 150.77605 104.69975) (end 150.2316 104.1553) (width 0.2032) (layer "F.Cu") (net 2) (tstamp 64f42c98-92a4-4baa-b4b3-05e7803c7f63))
+ (segment (start 150.2316 104.1553) (end 146.8501 104.1553) (width 0.2032) (layer "F.Cu") (net 2) (tstamp 8306564e-5a9a-4496-9d00-07c1bd5d26b9))
+ (via (at 150.77605 104.69975) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 2) (tstamp 99809845-4da0-4cf1-bc62-c9244dba4647))
+ (via (at 146.8501 104.1553) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 2) (tstamp e676c03d-34a8-4263-88a7-40dae634a5e2))
+ (segment (start 144.8435 104.4601) (end 145.1483 104.1553) (width 0.2032) (layer "B.Cu") (net 2) (tstamp 37789217-9964-4e49-8f73-d44995c5fc7e))
+ (segment (start 151.51265 104.69975) (end 150.77605 104.69975) (width 0.2032) (layer "B.Cu") (net 2) (tstamp 5857a90c-f5a9-4b24-a98c-479fad8de298))
+ (segment (start 153.2255 108.9961) (end 153.2255 106.4126) (width 0.2032) (layer "B.Cu") (net 2) (tstamp cae21fe2-e35b-4a44-bcf5-92de0998264c))
+ (segment (start 146.8501 104.1553) (end 145.1483 104.1553) (width 0.2032) (layer "B.Cu") (net 2) (tstamp f4f1004d-dd60-429f-8283-9f030f526130))
+ (segment (start 153.2255 106.4126) (end 151.51265 104.69975) (width 0.2032) (layer "B.Cu") (net 2) (tstamp fbcc5301-bbbf-4c9f-b660-fd98838053a6))
+ (segment (start 154.2255 107.3959) (end 154.0985 107.2689) (width 0.2032) (layer "B.Cu") (net 3) (tstamp 7ebf6bd5-822c-4407-8d1a-08d43c2d0678))
+ (segment (start 154.0985 106.079475) (end 154.0985 107.2689) (width 0.2032) (layer "B.Cu") (net 3) (tstamp 8a4485c7-f427-446f-af34-1128a47c425d))
+ (segment (start 145.780813 100.7009) (end 148.719925 100.7009) (width 0.2032) (layer "B.Cu") (net 3) (tstamp b08ebf16-1549-411d-8fab-75c6d1bd76cb))
+ (segment (start 145.5761 100.905613) (end 145.780813 100.7009) (width 0.2032) (layer "B.Cu") (net 3) (tstamp b21f3bbb-f1c1-4554-ac75-e53c16d27653))
+ (segment (start 154.0985 106.079475) (end 148.719925 100.7009) (width 0.2032) (layer "B.Cu") (net 3) (tstamp e5d7d599-d647-4e18-96d9-808d318d07c5))
+ (segment (start 154.2255 108.9961) (end 154.2255 107.3959) (width 0.2032) (layer "B.Cu") (net 3) (tstamp e9b07d7a-e899-4ca0-9369-e0d0ebb1ab10))
+ (segment (start 145.5761 101.1601) (end 145.5761 100.905613) (width 0.2032) (layer "B.Cu") (net 3) (tstamp febdebfd-50f2-4c7a-baca-c05d26479bd3))
+ (segment (start 146.8534 103.1139) (end 146.7772 103.1901) (width 0.3048) (layer "F.Cu") (net 4) (tstamp 06fcdb8f-2704-46cd-b30d-dcb7b02e58da))
+ (segment (start 154.61145 106.83335) (end 150.892 103.1139) (width 0.3048) (layer "F.Cu") (net 4) (tstamp 1fa3df21-bfa8-4c91-9bb0-d56e9306f2bf))
+ (segment (start 150.892 103.1139) (end 146.8534 103.1139) (width 0.3048) (layer "F.Cu") (net 4) (tstamp 8f443325-1180-471c-a3e1-92e2115c8bf9))
+ (via (at 154.61145 106.83335) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 4) (tstamp 1b9c6030-7343-4ac8-a6d0-411c1c118821))
+ (via (at 146.7772 103.1901) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 4) (tstamp 7c1347fe-33ec-4ab3-b26c-77a73b0799f2))
+ (segment (start 142.4051 102.0979) (end 143.6243 102.0979) (width 0.3048) (layer "B.Cu") (net 4) (tstamp 07ac1533-fe72-4f36-a863-388b14a75fc5))
+ (segment (start 142.3035 103.3044) (end 142.4051 103.2028) (width 0.3048) (layer "B.Cu") (net 4) (tstamp 0ae5de39-695f-4104-bcec-54b83e33bae2))
+ (segment (start 144.5661 102.6701) (end 145.0661 102.6701) (width 0.2032) (layer "B.Cu") (net 4) (tstamp 391f44de-b221-459d-83d3-1c670da7efe7))
+ (segment (start 144.5661 102.1741) (end 144.5661 102.1701) (width 0.2032) (layer "B.Cu") (net 4) (tstamp 3d9119fb-5155-4df7-a94a-e63bdeca035d))
+ (segment (start 144.5661 102.6701) (end 144.5661 102.1741) (width 0.2032) (layer "B.Cu") (net 4) (tstamp 733bf9c6-aa1d-46f9-b366-35a9f20a2249))
+ (segment (start 154.7255 108.9961) (end 154.7255 106.9474) (width 0.3048) (layer "B.Cu") (net 4) (tstamp 8c334ef6-ea14-4d64-923e-8c370645175a))
+ (segment (start 145.28005 103.1901) (end 145.0761 102.98615) (width 0.3048) (layer "B.Cu") (net 4) (tstamp 903215f2-2ab4-494a-8eda-17fc50d3bed0))
+ (segment (start 145.0661 102.6701) (end 145.0761 102.6801) (width 0.2032) (layer "B.Cu") (net 4) (tstamp a077552a-b221-43cc-9938-5d4743e3399d))
+ (segment (start 143.6243 102.0979) (end 143.7005 102.1741) (width 0.3048) (layer "B.Cu") (net 4) (tstamp a26ae543-f877-4e55-8489-6ed81f3bfbf5))
+ (segment (start 145.0761 102.6801) (end 145.0761 102.98615) (width 0.3048) (layer "B.Cu") (net 4) (tstamp b424a39d-8974-4555-8ffe-a4b9f275773a))
+ (segment (start 142.4051 103.2028) (end 142.4051 102.0979) (width 0.3048) (layer "B.Cu") (net 4) (tstamp c2b57f9a-4cbb-409a-89ca-80ae105c720e))
+ (segment (start 154.7255 106.9474) (end 154.61145 106.83335) (width 0.3048) (layer "B.Cu") (net 4) (tstamp da906691-c7ce-473f-afa0-e74ab9381761))
+ (segment (start 145.28005 103.1901) (end 146.7772 103.1901) (width 0.3048) (layer "B.Cu") (net 4) (tstamp fcfeaa1c-0530-4b57-aa65-515779c79adc))
+ (segment (start 143.7005 102.1741) (end 144.5661 102.1741) (width 0.3048) (layer "B.Cu") (net 4) (tstamp fe08666b-5748-4b31-8ca5-897a10c1698b))
+ (segment (start 146.3675 101.7677) (end 145.4785 101.7677) (width 0.2032) (layer "F.Cu") (net 5) (tstamp 4b68e877-273f-48ca-a632-c41e8488ccf3))
+ (segment (start 146.5199 101.9201) (end 146.3675 101.7677) (width 0.2032) (layer "F.Cu") (net 5) (tstamp 918e83c5-b7c2-44c1-b47f-0ab29c1c70b5))
+ (segment (start 146.5199 101.9201) (end 147.3835 101.9201) (width 0.2032) (layer "F.Cu") (net 5) (tstamp ad51b439-098c-4e18-ae1f-9d44e116b8f7))
+ (via (at 147.3835 101.9201) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 5) (tstamp 434f1740-e8ea-48aa-819a-e8f0c67929c4))
+ (via (at 145.4785 101.7677) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 5) (tstamp 6ba8a963-4e52-4da9-b129-37389306d641))
+ (segment (start 145.4277 101.7677) (end 145.0761 101.4161) (width 0.2032) (layer "B.Cu") (net 5) (tstamp 10257638-748f-4792-830d-716a1c0db7ff))
+ (segment (start 145.0761 101.1601) (end 145.0761 101.4161) (width 0.2032) (layer "B.Cu") (net 5) (tstamp 365cff8c-a479-4d79-b362-fdfac89711c6))
+ (segment (start 150.2029 104.1807) (end 147.9423 101.9201) (width 0.2032) (layer "B.Cu") (net 5) (tstamp 90ce1c88-363b-45a2-919d-8827646fa909))
+ (segment (start 145.4785 101.7677) (end 145.4277 101.7677) (width 0.2032) (layer "B.Cu") (net 5) (tstamp be3923fc-ad03-412b-bb5d-3fdd829c673e))
+ (segment (start 147.9423 101.9201) (end 147.3835 101.9201) (width 0.2032) (layer "B.Cu") (net 5) (tstamp d182f9f8-e6a8-4202-9825-0d3dc441d54e))
+ (segment (start 153.7255 106.22845) (end 151.67775 104.1807) (width 0.2032) (layer "B.Cu") (net 5) (tstamp e273d4e7-8cf7-453c-9576-254ffc1b9a69))
+ (segment (start 151.67775 104.1807) (end 150.2029 104.1807) (width 0.2032) (layer "B.Cu") (net 5) (tstamp f428be95-0b2d-4a60-bceb-9e05112c3631))
+ (segment (start 153.7255 108.9961) (end 153.7255 106.22845) (width 0.2032) (layer "B.Cu") (net 5) (tstamp f706a32a-34b2-481f-abbc-a90a4713812a))
+ (segment (start 152.2255 106.978) (end 147.9176 102.6701) (width 0.2032) (layer "B.Cu") (net 6) (tstamp 3eea729f-d81e-4602-924f-df00dc9fa656))
+ (segment (start 152.2255 108.9961) (end 152.2255 106.978) (width 0.2032) (layer "B.Cu") (net 6) (tstamp 5e249130-5691-4735-95bd-6a35e7230671))
+ (segment (start 147.9176 102.6701) (end 146.0861 102.6701) (width 0.2032) (layer "B.Cu") (net 6) (tstamp 6a7b5cae-5706-4ae7-90cd-85b744bf9818))
+ (segment (start 151.71585 107.21435) (end 151.71585 104.90625) (width 0.2032) (layer "F.Cu") (net 7) (tstamp 5cd87d85-df22-4c10-9e4e-723432382705))
+ (segment (start 151.71585 104.90625) (end 150.4823 103.6727) (width 0.2032) (layer "F.Cu") (net 7) (tstamp 9e5e5b3d-da91-43f7-a8e1-1798b10f31c4))
+ (via (at 150.4823 103.6727) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 7) (tstamp a9964fed-7444-442b-82cc-b749621619ca))
+ (via (at 151.71585 107.21435) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 7) (tstamp c2434c2f-d941-4c49-8079-f193e7b68270))
+ (segment (start 151.7255 108.9961) (end 151.7255 107.224) (width 0.2032) (layer "B.Cu") (net 7) (tstamp 8ae15dfd-83d6-4de0-b635-cf161fade6d8))
+ (segment (start 147.9797 101.1701) (end 146.0861 101.1701) (width 0.2032) (layer "B.Cu") (net 7) (tstamp 9502ea06-b6dc-4669-9f41-6d0a72043887))
+ (segment (start 151.7255 107.224) (end 151.71585 107.21435) (width 0.2032) (layer "B.Cu") (net 7) (tstamp a3a7ea90-729d-4167-a73a-5b7df6440f1d))
+ (segment (start 150.4823 103.6727) (end 147.9797 101.1701) (width 0.2032) (layer "B.Cu") (net 7) (tstamp d8832711-350c-4ec6-a360-bb9e4c599453))
+ (segment (start 143.0147 101.3423) (end 143.5989 101.9265) (width 0.3048) (layer "F.Cu") (net 8) (tstamp 11b50766-7a82-4010-9094-79ac57ff803a))
+ (segment (start 143.5989 101.9265) (end 143.5989 103.1901) (width 0.3048) (layer "F.Cu") (net 8) (tstamp a307a9c7-a8ef-452f-a518-570df34d3971))
+ (via (at 143.0147 101.3423) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 8) (tstamp 4555beef-afd0-4a07-a374-49fd7a56b2b2))
+ (via (at 143.5989 103.1901) (size 0.508) (drill 0.254) (layers "F.Cu" "B.Cu") (net 8) (tstamp e4527f25-252e-4b65-948d-69c2f9252889))
+ (segment (start 143.6243 100.5739) (end 144.2593 100.5739) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 0255fcc1-949a-4f3e-9303-3ea178273985))
+ (segment (start 144.5661 100.86405) (end 144.557775 100.872375) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 0430d002-15bd-4a42-b0f9-df13695fcd41))
+ (segment (start 146.0861 102.1701) (end 146.0861 101.6701) (width 0.1524) (layer "B.Cu") (net 8) (tstamp 311f6061-4902-4cbc-adea-c404d4acbf4f))
+ (segment (start 144.2593 100.5739) (end 144.557775 100.872375) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 3cb471ac-4528-43b2-8cc2-c3ed9505edbb))
+ (segment (start 142.4051 100.5739) (end 142.4051 100.7327) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 4a55c467-28b2-400a-bb25-411d525e0c41))
+ (segment (start 146.0861 102.1701) (end 145.7586 102.1701) (width 0.1524) (layer "B.Cu") (net 8) (tstamp 57534e6f-4afa-4ff4-8fb8-efd13eca414d))
+ (segment (start 144.5661 101.1701) (end 144.5661 100.86405) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 5db439c0-d3e7-4734-b78a-7d0e5bb5ff74))
+ (segment (start 144.5661 101.6701) (end 144.5661 101.1701) (width 0.2032) (layer "B.Cu") (net 8) (tstamp 7ebfbee5-a62a-4d13-aa04-b904378145cd))
+ (segment (start 142.4051 100.5739) (end 143.6243 100.5739) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 81233f61-eb56-41e1-aff7-bb28c6de0f43))
+ (segment (start 145.13565 100.2945) (end 144.5661 100.86405) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 88b53bf2-a9d1-436f-9e73-361984ffadfd))
+ (segment (start 145.6784 102.2503) (end 145.2278 102.2503) (width 0.1524) (layer "B.Cu") (net 8) (tstamp 90b3147c-c625-4f91-99f2-b3e2525c4b58))
+ (segment (start 155.2255 108.9961) (end 155.2255 106.631737) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 99b20163-86e6-4035-8ae8-2076cd6324d5))
+ (segment (start 155.2255 106.631737) (end 148.888263 100.2945) (width 0.3048) (layer "B.Cu") (net 8) (tstamp 9a895394-1736-428b-8941-d281f03cb5c1))
+ (segment (start 144.5661 101.6701) (end 144.764388 101.6701) (width 0.1524) (layer "B.Cu") (net 8) (tstamp a3e3db84-e7c7-4654-839f-76d04cc0c896))
+ (segment (start 144.9947 102.0172) (end 144.9947 101.900413) (width 0.1524) (layer "B.Cu") (net 8) (tstamp a4d1b77a-1e80-4648-9ae9-519383b53778))
+ (segment (start 142.4051 100.7327) (end 143.0147 101.3423) (width 0.3048) (layer "B.Cu") (net 8) (tstamp a9338404-2317-4d4a-8561-2fc099b9516a))
+ (segment (start 143.5989 104.1807) (end 143.5989 103.1901) (width 0.3048) (layer "B.Cu") (net 8) (tstamp bece63a7-3dcb-4c76-846e-10153f6e0bc1))
+ (segment (start 144.764388 101.6701) (end 144.9947 101.900413) (width 0.1524) (layer "B.Cu") (net 8) (tstamp beeed655-48b9-475b-8276-e75e9ceba0c9))
+ (segment (start 143.3195 104.4601) (end 143.5989 104.1807) (width 0.3048) (layer "B.Cu") (net 8) (tstamp e5fe01a0-2862-474d-bb27-d55a88f6ecb0))
+ (segment (start 145.7586 102.1701) (end 145.6784 102.2503) (width 0.1524) (layer "B.Cu") (net 8) (tstamp e87db6bb-555e-4674-ad0d-38786a05b91b))
+ (segment (start 145.2278 102.2503) (end 144.9947 102.0172) (width 0.1524) (layer "B.Cu") (net 8) (tstamp ec19ba45-9ebe-4ac5-aa32-4f3cb5d1c05c))
+ (segment (start 148.888263 100.2945) (end 145.13565 100.2945) (width 0.3048) (layer "B.Cu") (net 8) (tstamp efab3462-3997-4e87-83fd-171e8d73a0ec))
+
+)
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_prl b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_prl
new file mode 100644
index 00000000..de494a3d
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_prl
@@ -0,0 +1,77 @@
+{
+ "board": {
+ "active_layer": 39,
+ "active_layer_preset": "All Layers",
+ "auto_track_width": true,
+ "hidden_netclasses": [],
+ "hidden_nets": [],
+ "high_contrast_mode": 0,
+ "net_color_mode": 1,
+ "opacity": {
+ "images": 0.6,
+ "pads": 1.0,
+ "tracks": 1.0,
+ "vias": 1.0,
+ "zones": 0.6
+ },
+ "ratsnest_display_mode": 0,
+ "selection_filter": {
+ "dimensions": true,
+ "footprints": true,
+ "graphics": true,
+ "keepouts": true,
+ "lockedItems": true,
+ "otherItems": true,
+ "pads": true,
+ "text": true,
+ "tracks": true,
+ "vias": true,
+ "zones": true
+ },
+ "visible_items": [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 32,
+ 33,
+ 34,
+ 35,
+ 36
+ ],
+ "visible_layers": "fffffff_ffffffff",
+ "zone_display_mode": 0
+ },
+ "meta": {
+ "filename": "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_prl",
+ "version": 3
+ },
+ "project": {
+ "files": []
+ }
+}
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_pro b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_pro
new file mode 100644
index 00000000..4bc54b58
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_pro
@@ -0,0 +1,527 @@
+{
+ "board": {
+ "3dviewports": [],
+ "design_settings": {
+ "defaults": {
+ "board_outline_line_width": 0.049999999999999996,
+ "copper_line_width": 0.19999999999999998,
+ "copper_text_italic": false,
+ "copper_text_size_h": 1.5,
+ "copper_text_size_v": 1.5,
+ "copper_text_thickness": 0.3,
+ "copper_text_upright": false,
+ "courtyard_line_width": 0.049999999999999996,
+ "dimension_precision": 4,
+ "dimension_units": 3,
+ "dimensions": {
+ "arrow_length": 1270000,
+ "extension_offset": 500000,
+ "keep_text_aligned": true,
+ "suppress_zeroes": false,
+ "text_position": 0,
+ "units_format": 1
+ },
+ "fab_line_width": 0.09999999999999999,
+ "fab_text_italic": false,
+ "fab_text_size_h": 1.0,
+ "fab_text_size_v": 1.0,
+ "fab_text_thickness": 0.15,
+ "fab_text_upright": false,
+ "other_line_width": 0.09999999999999999,
+ "other_text_italic": false,
+ "other_text_size_h": 1.0,
+ "other_text_size_v": 1.0,
+ "other_text_thickness": 0.15,
+ "other_text_upright": false,
+ "pads": {
+ "drill": 0.762,
+ "height": 1.524,
+ "width": 1.524
+ },
+ "silk_line_width": 0.12,
+ "silk_text_italic": false,
+ "silk_text_size_h": 1.0,
+ "silk_text_size_v": 1.0,
+ "silk_text_thickness": 0.15,
+ "silk_text_upright": false,
+ "zones": {
+ "45_degree_only": false,
+ "min_clearance": 0.508
+ }
+ },
+ "diff_pair_dimensions": [
+ {
+ "gap": 0.0,
+ "via_gap": 0.0,
+ "width": 0.0
+ }
+ ],
+ "drc_exclusions": [
+ "copper_edge_clearance|151052000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|6a847b13-3d48-4df7-8dd7-d515f51aacbf",
+ "copper_edge_clearance|151552000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|496964dd-0d25-423f-9576-ddcd7d92b9c0",
+ "copper_edge_clearance|152052000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|b54a21f9-d0ee-47bd-8a33-6d2ccb2f1964",
+ "copper_edge_clearance|152552000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|edf03a5b-8be1-48f8-8f16-4719d937182d",
+ "copper_edge_clearance|153052000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|16a60cfb-4140-483f-9675-22643c201dcd",
+ "copper_edge_clearance|153552000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|44970b45-f7b0-47cc-a114-209df0456d8f",
+ "copper_edge_clearance|154052000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|1ad63a96-1ef2-463e-9ea7-81ba46599172",
+ "copper_edge_clearance|154552000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|4a8a2808-da08-4211-a839-b545ca46843d",
+ "copper_edge_clearance|155052000|110246100|ae418f09-9594-49fa-ba14-c5ebf3097fc7|b2e7f160-81a2-4f3a-9cac-d6cba543336c",
+ "silk_over_copper|146240500|100142100|b5d214ca-8d2c-4892-a145-ebcdf525979d|a8fc3609-a41b-4706-8233-14786995acaa",
+ "silk_over_copper|155714700|105095100|a6bc6301-abbd-4acf-a77d-1f4e4d107e79|09b40b31-a75f-48c5-94ff-dac04c6dfaf8"
+ ],
+ "meta": {
+ "filename": "board_design_settings.json",
+ "version": 2
+ },
+ "rule_severities": {
+ "annular_width": "error",
+ "clearance": "error",
+ "copper_edge_clearance": "error",
+ "courtyards_overlap": "error",
+ "diff_pair_gap_out_of_range": "error",
+ "diff_pair_uncoupled_length_too_long": "error",
+ "drill_out_of_range": "error",
+ "duplicate_footprints": "warning",
+ "extra_footprint": "warning",
+ "footprint_type_mismatch": "error",
+ "hole_clearance": "error",
+ "hole_near_hole": "error",
+ "invalid_outline": "error",
+ "item_on_disabled_layer": "error",
+ "items_not_allowed": "error",
+ "length_out_of_range": "error",
+ "malformed_courtyard": "error",
+ "microvia_drill_out_of_range": "error",
+ "missing_courtyard": "ignore",
+ "missing_footprint": "warning",
+ "net_conflict": "warning",
+ "npth_inside_courtyard": "ignore",
+ "padstack": "error",
+ "pth_inside_courtyard": "ignore",
+ "shorting_items": "error",
+ "silk_over_copper": "warning",
+ "silk_overlap": "warning",
+ "skew_out_of_range": "error",
+ "through_hole_pad_without_hole": "error",
+ "too_many_vias": "error",
+ "track_dangling": "warning",
+ "track_width": "error",
+ "tracks_crossing": "error",
+ "unconnected_items": "error",
+ "unresolved_variable": "error",
+ "via_dangling": "warning",
+ "zone_has_empty_net": "error",
+ "zones_intersect": "error"
+ },
+ "rules": {
+ "allow_blind_buried_vias": false,
+ "allow_microvias": false,
+ "max_error": 0.005,
+ "min_clearance": 0.127,
+ "min_connection": 0.0,
+ "min_copper_edge_clearance": 0.024999999999999998,
+ "min_hole_clearance": 0.25,
+ "min_hole_to_hole": 0.25,
+ "min_microvia_diameter": 0.19999999999999998,
+ "min_microvia_drill": 0.09999999999999999,
+ "min_resolved_spokes": 2,
+ "min_silk_clearance": 0.0,
+ "min_text_height": 0.7999999999999999,
+ "min_text_thickness": 0.08,
+ "min_through_hole_diameter": 0.254,
+ "min_track_width": 0.127,
+ "min_via_annular_width": 0.049999999999999996,
+ "min_via_diameter": 0.39999999999999997,
+ "solder_mask_to_copper_clearance": 0.0,
+ "use_height_for_length_calcs": true
+ },
+ "teardrop_options": [
+ {
+ "td_allow_use_two_tracks": true,
+ "td_curve_segcount": 5,
+ "td_on_pad_in_zone": false,
+ "td_onpadsmd": true,
+ "td_onroundshapesonly": false,
+ "td_ontrackend": false,
+ "td_onviapad": true
+ }
+ ],
+ "teardrop_parameters": [
+ {
+ "td_curve_segcount": 0,
+ "td_height_ratio": 1.0,
+ "td_length_ratio": 0.5,
+ "td_maxheight": 2.0,
+ "td_maxlen": 1.0,
+ "td_target_name": "td_round_shape",
+ "td_width_to_size_filter_ratio": 0.9
+ },
+ {
+ "td_curve_segcount": 0,
+ "td_height_ratio": 1.0,
+ "td_length_ratio": 0.5,
+ "td_maxheight": 2.0,
+ "td_maxlen": 1.0,
+ "td_target_name": "td_rect_shape",
+ "td_width_to_size_filter_ratio": 0.9
+ },
+ {
+ "td_curve_segcount": 0,
+ "td_height_ratio": 1.0,
+ "td_length_ratio": 0.5,
+ "td_maxheight": 2.0,
+ "td_maxlen": 1.0,
+ "td_target_name": "td_track_end",
+ "td_width_to_size_filter_ratio": 0.9
+ }
+ ],
+ "track_widths": [
+ 0.0
+ ],
+ "via_dimensions": [
+ {
+ "diameter": 0.0,
+ "drill": 0.0
+ }
+ ],
+ "zones_allow_external_fillets": false,
+ "zones_use_no_outline": true
+ },
+ "layer_presets": [],
+ "viewports": []
+ },
+ "boards": [],
+ "cvpcb": {
+ "equivalence_files": []
+ },
+ "erc": {
+ "erc_exclusions": [],
+ "meta": {
+ "version": 0
+ },
+ "pin_map": [
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 0,
+ 2,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 2,
+ 2,
+ 2
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 2
+ ],
+ [
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 2,
+ 1,
+ 1,
+ 2
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2
+ ],
+ [
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 0,
+ 2,
+ 1,
+ 2,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 2,
+ 2,
+ 2
+ ],
+ [
+ 0,
+ 2,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 0,
+ 2,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 0,
+ 0,
+ 2
+ ],
+ [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+ ]
+ ],
+ "rule_severities": {
+ "bus_definition_conflict": "error",
+ "bus_entry_needed": "error",
+ "bus_label_syntax": "error",
+ "bus_to_bus_conflict": "error",
+ "bus_to_net_conflict": "error",
+ "different_unit_footprint": "error",
+ "different_unit_net": "error",
+ "duplicate_reference": "error",
+ "duplicate_sheet_names": "error",
+ "extra_units": "error",
+ "global_label_dangling": "warning",
+ "hier_label_mismatch": "error",
+ "label_dangling": "error",
+ "lib_symbol_issues": "warning",
+ "multiple_net_names": "warning",
+ "net_not_bus_member": "warning",
+ "no_connect_connected": "warning",
+ "no_connect_dangling": "warning",
+ "pin_not_connected": "error",
+ "pin_not_driven": "error",
+ "pin_to_pin": "warning",
+ "power_pin_not_driven": "error",
+ "similar_labels": "warning",
+ "unannotated": "error",
+ "unit_value_mismatch": "error",
+ "unresolved_variable": "error",
+ "wire_dangling": "error"
+ }
+ },
+ "libraries": {
+ "pinned_footprint_libs": [],
+ "pinned_symbol_libs": []
+ },
+ "meta": {
+ "filename": "OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_pro",
+ "version": 1
+ },
+ "net_settings": {
+ "classes": [
+ {
+ "bus_width": 12,
+ "clearance": 0.127,
+ "diff_pair_gap": 0.25,
+ "diff_pair_via_gap": 0.25,
+ "diff_pair_width": 0.2,
+ "line_style": 0,
+ "microvia_diameter": 0.3,
+ "microvia_drill": 0.1,
+ "name": "Default",
+ "pcb_color": "rgba(0, 0, 0, 0.000)",
+ "schematic_color": "rgba(0, 0, 0, 0.000)",
+ "track_width": 0.127,
+ "via_diameter": 0.8,
+ "via_drill": 0.4,
+ "wire_width": 6
+ },
+ {
+ "bus_width": 12,
+ "clearance": 0.127,
+ "diff_pair_gap": 0.25,
+ "diff_pair_via_gap": 0.25,
+ "diff_pair_width": 0.2,
+ "line_style": 0,
+ "microvia_diameter": 0.3,
+ "microvia_drill": 0.1,
+ "name": "gnd",
+ "pcb_color": "rgba(0, 0, 0, 0.000)",
+ "schematic_color": "rgba(0, 0, 0, 0.000)",
+ "track_width": 0.2032,
+ "via_diameter": 0.8,
+ "via_drill": 0.4,
+ "wire_width": 6
+ },
+ {
+ "bus_width": 12,
+ "clearance": 0.127,
+ "diff_pair_gap": 0.25,
+ "diff_pair_via_gap": 0.25,
+ "diff_pair_width": 0.2,
+ "line_style": 0,
+ "microvia_diameter": 0.3,
+ "microvia_drill": 0.1,
+ "name": "power",
+ "pcb_color": "rgba(0, 0, 0, 0.000)",
+ "schematic_color": "rgba(0, 0, 0, 0.000)",
+ "track_width": 0.2032,
+ "via_diameter": 0.8,
+ "via_drill": 0.4,
+ "wire_width": 6
+ }
+ ],
+ "meta": {
+ "version": 3
+ },
+ "net_colors": null,
+ "netclass_assignments": null,
+ "netclass_patterns": []
+ },
+ "pcbnew": {
+ "last_paths": {
+ "gencad": "",
+ "idf": "",
+ "netlist": "",
+ "specctra_dsn": "",
+ "step": "",
+ "vrml": ""
+ },
+ "page_layout_descr_file": ""
+ },
+ "schematic": {
+ "annotate_start_num": 0,
+ "drawing": {
+ "default_line_thickness": 6.0,
+ "default_text_size": 50.0,
+ "field_names": [],
+ "intersheets_ref_own_page": false,
+ "intersheets_ref_prefix": "",
+ "intersheets_ref_short": false,
+ "intersheets_ref_show": false,
+ "intersheets_ref_suffix": "",
+ "junction_size_choice": 3,
+ "label_size_ratio": 0.375,
+ "pin_symbol_size": 25.0,
+ "text_offset_ratio": 0.15
+ },
+ "legacy_lib_dir": "",
+ "legacy_lib_list": [],
+ "meta": {
+ "version": 1
+ },
+ "net_format_name": "",
+ "ngspice": {
+ "fix_include_paths": true,
+ "fix_passive_vals": false,
+ "meta": {
+ "version": 0
+ },
+ "model_mode": 0,
+ "workbook_filename": ""
+ },
+ "page_layout_descr_file": "empty.kicad_wks",
+ "plot_directory": "",
+ "spice_adjust_passive_values": false,
+ "spice_external_command": "spice \"%I\"",
+ "subpart_first_id": 65,
+ "subpart_id_separator": 0
+ },
+ "sheets": [
+ [
+ "677683ff-9f49-4367-8af2-04a801740db6",
+ ""
+ ]
+ ],
+ "text_variables": {}
+}
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch
new file mode 100644
index 00000000..b37bfdbf
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.kicad_sch
@@ -0,0 +1,1357 @@
+(kicad_sch (version 20211123) (generator eeschema)
+
+ (uuid 677683ff-9f49-4367-8af2-04a801740db6)
+
+ (paper "User" 300.507 217.881)
+
+ (lib_symbols
+ (symbol "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:A4L-LOC" (in_bom yes) (on_board yes)
+ (property "Reference" "#FRAME" (id 0) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "A4L-LOC" (id 1) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_locked" "" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (symbol "A4L-LOC_0_0"
+ (polyline
+ (pts
+ (xy 0 44.7675)
+ (xy 3.81 44.7675)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 89.535)
+ (xy 3.81 89.535)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 134.3025)
+ (xy 3.81 134.3025)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 3.81 3.81)
+ (xy 3.81 175.26)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 43.3917 0)
+ (xy 43.3917 3.81)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 43.3917 175.26)
+ (xy 43.3917 179.07)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 86.7833 0)
+ (xy 86.7833 3.81)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 86.7833 175.26)
+ (xy 86.7833 179.07)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 130.175 0)
+ (xy 130.175 3.81)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 130.175 175.26)
+ (xy 130.175 179.07)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 173.5667 0)
+ (xy 173.5667 3.81)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 173.5667 175.26)
+ (xy 173.5667 179.07)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 216.9583 0)
+ (xy 216.9583 3.81)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 216.9583 175.26)
+ (xy 216.9583 179.07)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 3.81)
+ (xy 3.81 3.81)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 3.81)
+ (xy 256.54 175.26)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 44.7675)
+ (xy 260.35 44.7675)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 89.535)
+ (xy 260.35 89.535)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 134.3025)
+ (xy 260.35 134.3025)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 175.26)
+ (xy 3.81 175.26)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 0)
+ (xy 260.35 0)
+ (xy 260.35 179.07)
+ (xy 0 179.07)
+ (xy 0 0)
+ )
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (text "1" (at 21.6958 1.905 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "1" (at 21.6958 177.165 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "2" (at 65.0875 1.905 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "2" (at 65.0875 177.165 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "3" (at 108.4792 1.905 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "3" (at 108.4792 177.165 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "4" (at 151.8708 1.905 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "4" (at 151.8708 177.165 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "5" (at 195.2625 1.905 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "5" (at 195.2625 177.165 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "6" (at 238.6542 1.905 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "6" (at 238.6542 177.165 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "A" (at 1.905 156.6863 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "A" (at 258.445 156.6863 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "B" (at 1.905 111.9188 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "B" (at 258.445 111.9188 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "C" (at 1.905 67.1513 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "C" (at 258.445 67.1513 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "D" (at 1.905 22.3838 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ (text "D" (at 258.445 22.3838 0)
+ (effects (font (size 2.54 2.286)))
+ )
+ )
+ (symbol "A4L-LOC_1_0"
+ (polyline
+ (pts
+ (xy 184.15 3.81)
+ (xy 184.15 24.13)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 184.15 24.13)
+ (xy 215.265 24.13)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 215.265 8.89)
+ (xy 215.265 3.81)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 215.265 8.89)
+ (xy 215.265 13.97)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 215.265 13.97)
+ (xy 215.265 19.05)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 215.265 13.97)
+ (xy 256.54 13.97)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 215.265 19.05)
+ (xy 215.265 24.13)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 215.265 19.05)
+ (xy 256.54 19.05)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 215.265 24.13)
+ (xy 256.54 24.13)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 234.95 3.81)
+ (xy 234.95 8.89)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 234.95 8.89)
+ (xy 215.265 8.89)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 234.95 8.89)
+ (xy 256.54 8.89)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 3.81)
+ (xy 256.54 8.89)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 8.89)
+ (xy 256.54 13.97)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 13.97)
+ (xy 256.54 19.05)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 256.54 19.05)
+ (xy 256.54 24.13)
+ )
+ (stroke (width 0.1016) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (text ">DRAWING_NAME" (at 217.17 15.24 0)
+ (effects (font (size 2.54 2.159)) (justify left bottom))
+ )
+ (text ">LAST_DATE_TIME" (at 217.17 10.16 0)
+ (effects (font (size 2.286 1.9431)) (justify left bottom))
+ )
+ (text ">SHEET" (at 228.473 5.08 0)
+ (effects (font (size 2.54 2.159)) (justify left bottom))
+ )
+ (text "by joey castillo" (at 217.17 20.32 0)
+ (effects (font (size 2.54 2.159)) (justify left bottom))
+ )
+ (text "cc-by-sa 4.0" (at 236.22 5.08 0)
+ (effects (font (size 2.54 2.159)) (justify left bottom))
+ )
+ (text "oddly_specific_objects" (at 185.42 5.08 0)
+ (effects (font (size 4.572 4.572)) (justify left bottom))
+ )
+ (text "Sheet:" (at 216.916 4.953 0)
+ (effects (font (size 2.54 2.159)) (justify left bottom))
+ )
+ )
+ )
+ (symbol "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:CAP_CERAMIC_0603MP" (in_bom yes) (on_board yes)
+ (property "Reference" "C" (id 0) (at -2.29 1.25 90)
+ (effects (font (size 1.27 1.27)))
+ )
+ (property "Value" "CAP_CERAMIC_0603MP" (id 1) (at 2.3 1.25 90)
+ (effects (font (size 1.27 1.27)))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:_0603MP" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_locked" "" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (symbol "CAP_CERAMIC_0603MP_1_0"
+ (rectangle (start -1.27 0.508) (end 1.27 1.016)
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type outline))
+ )
+ (rectangle (start -1.27 1.524) (end 1.27 2.032)
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (fill (type outline))
+ )
+ (polyline
+ (pts
+ (xy 0 0.762)
+ (xy 0 0)
+ )
+ (stroke (width 0.1524) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 2.54)
+ (xy 0 1.778)
+ )
+ (stroke (width 0.1524) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (pin passive line (at 0 5.08 270) (length 2.54)
+ (name "1" (effects (font (size 0 0))))
+ (number "1" (effects (font (size 0 0))))
+ )
+ (pin passive line (at 0 -2.54 90) (length 2.54)
+ (name "2" (effects (font (size 0 0))))
+ (number "2" (effects (font (size 0 0))))
+ )
+ )
+ )
+ (symbol "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:FH19C-9S-0.5SH_10-FFC" (in_bom yes) (on_board yes)
+ (property "Reference" "" (id 0) (at 8.89 17.78 0)
+ (effects (font (size 1.778 1.5113)) (justify left))
+ )
+ (property "Value" "FH19C-9S-0.5SH_10-FFC" (id 1) (at 8.89 15.24 0)
+ (effects (font (size 1.778 1.5113)) (justify left))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:FH19C9S05SH10-FFC" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_locked" "" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (symbol "FH19C-9S-0.5SH_10-FFC_1_0"
+ (polyline
+ (pts
+ (xy -5.08 12.7)
+ (xy -5.08 -12.7)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy -5.08 12.7)
+ (xy 7.62 12.7)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 7.62 -12.7)
+ (xy -5.08 -12.7)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 7.62 -12.7)
+ (xy 7.62 12.7)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (pin bidirectional line (at 12.7 10.16 180) (length 5.08)
+ (name "1" (effects (font (size 1.27 1.27))))
+ (number "1" (effects (font (size 1.27 1.27))))
+ )
+ (pin bidirectional line (at 12.7 7.62 180) (length 5.08)
+ (name "2" (effects (font (size 1.27 1.27))))
+ (number "2" (effects (font (size 1.27 1.27))))
+ )
+ (pin bidirectional line (at 12.7 5.08 180) (length 5.08)
+ (name "3" (effects (font (size 1.27 1.27))))
+ (number "3" (effects (font (size 1.27 1.27))))
+ )
+ (pin bidirectional line (at 12.7 2.54 180) (length 5.08)
+ (name "4" (effects (font (size 1.27 1.27))))
+ (number "4" (effects (font (size 1.27 1.27))))
+ )
+ (pin bidirectional line (at 12.7 0 180) (length 5.08)
+ (name "5" (effects (font (size 1.27 1.27))))
+ (number "5" (effects (font (size 1.27 1.27))))
+ )
+ (pin bidirectional line (at 12.7 -2.54 180) (length 5.08)
+ (name "6" (effects (font (size 1.27 1.27))))
+ (number "6" (effects (font (size 1.27 1.27))))
+ )
+ (pin bidirectional line (at 12.7 -5.08 180) (length 5.08)
+ (name "7" (effects (font (size 1.27 1.27))))
+ (number "7" (effects (font (size 1.27 1.27))))
+ )
+ (pin bidirectional line (at 12.7 -7.62 180) (length 5.08)
+ (name "8" (effects (font (size 1.27 1.27))))
+ (number "8" (effects (font (size 1.27 1.27))))
+ )
+ (pin bidirectional line (at 12.7 -10.16 180) (length 5.08)
+ (name "9" (effects (font (size 1.27 1.27))))
+ (number "9" (effects (font (size 1.27 1.27))))
+ )
+ )
+ )
+ (symbol "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:GND" (power) (in_bom yes) (on_board yes)
+ (property "Reference" "" (id 0) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "GND" (id 1) (at -1.524 -2.54 0)
+ (effects (font (size 1.27 1.0795)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_locked" "" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (symbol "GND_1_0"
+ (polyline
+ (pts
+ (xy -1.27 0)
+ (xy 1.27 0)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (pin power_in line (at 0 2.54 270) (length 2.54)
+ (name "GND" (effects (font (size 0 0))))
+ (number "1" (effects (font (size 0 0))))
+ )
+ )
+ )
+ (symbol "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:LIS2DW12TR" (in_bom yes) (on_board yes)
+ (property "Reference" "U" (id 0) (at -15.2439 12.7032 0)
+ (effects (font (size 1.7784 1.5116)) (justify left bottom))
+ )
+ (property "Value" "LIS2DW12TR" (id 1) (at -15.2494 -17.7909 0)
+ (effects (font (size 1.779 1.5121)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:LGA12R50P4X4_200X200X70" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_locked" "" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (symbol "LIS2DW12TR_1_0"
+ (polyline
+ (pts
+ (xy -15.24 -15.24)
+ (xy -15.24 12.7)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy -15.24 12.7)
+ (xy 15.24 12.7)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 15.24 -15.24)
+ (xy -15.24 -15.24)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 15.24 12.7)
+ (xy 15.24 -15.24)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (pin input clock (at -20.32 2.54 0) (length 5.08)
+ (name "SCL/SPC" (effects (font (size 1.27 1.27))))
+ (number "1" (effects (font (size 0 0))))
+ )
+ (pin power_in line (at 20.32 7.62 180) (length 5.08)
+ (name "VDD_IO" (effects (font (size 1.27 1.27))))
+ (number "10" (effects (font (size 0 0))))
+ )
+ (pin output line (at 20.32 0 180) (length 5.08)
+ (name "INT2" (effects (font (size 1.27 1.27))))
+ (number "11" (effects (font (size 0 0))))
+ )
+ (pin output line (at 20.32 2.54 180) (length 5.08)
+ (name "INT1" (effects (font (size 1.27 1.27))))
+ (number "12" (effects (font (size 0 0))))
+ )
+ (pin input line (at -20.32 0 0) (length 5.08)
+ (name "CS" (effects (font (size 1.27 1.27))))
+ (number "2" (effects (font (size 0 0))))
+ )
+ (pin bidirectional line (at -20.32 -5.08 0) (length 5.08)
+ (name "SDO/SDA0" (effects (font (size 1.27 1.27))))
+ (number "3" (effects (font (size 0 0))))
+ )
+ (pin bidirectional line (at -20.32 -7.62 0) (length 5.08)
+ (name "SDA/SDI/SDO" (effects (font (size 1.27 1.27))))
+ (number "4" (effects (font (size 0 0))))
+ )
+ (pin power_in line (at 20.32 -12.7 180) (length 5.08)
+ (name "GND" (effects (font (size 1.27 1.27))))
+ (number "6" (effects (font (size 0 0))))
+ )
+ (pin passive line (at 20.32 -5.08 180) (length 5.08)
+ (name "RES" (effects (font (size 1.27 1.27))))
+ (number "7" (effects (font (size 0 0))))
+ )
+ (pin power_in line (at 20.32 -12.7 180) (length 5.08)
+ (name "GND" (effects (font (size 1.27 1.27))))
+ (number "8" (effects (font (size 0 0))))
+ )
+ (pin power_in line (at 20.32 10.16 180) (length 5.08)
+ (name "VDD" (effects (font (size 1.27 1.27))))
+ (number "9" (effects (font (size 0 0))))
+ )
+ )
+ )
+ (symbol "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:TPB1,27" (in_bom yes) (on_board yes)
+ (property "Reference" "TP" (id 0) (at -1.27 1.27 0)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Value" "TPB1,27" (id 1) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_locked" "" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (symbol "TPB1,27_1_0"
+ (polyline
+ (pts
+ (xy -0.762 -0.762)
+ (xy 0 0)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 -1.524)
+ (xy -0.762 -0.762)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0 0)
+ (xy 0.762 -0.762)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 0.762 -0.762)
+ (xy 0 -1.524)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (text ">TP_SIGNAL_NAME" (at 1.27 -1.27 0)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (pin input line (at 0 -2.54 90) (length 2.54)
+ (name "TP" (effects (font (size 0 0))))
+ (number "TP" (effects (font (size 0 0))))
+ )
+ )
+ )
+ (symbol "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:VCC" (power) (in_bom yes) (on_board yes)
+ (property "Reference" "#P+" (id 0) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "VCC" (id 1) (at -2.54 -2.54 90)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "ki_locked" "" (id 4) (at 0 0 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (symbol "VCC_1_0"
+ (polyline
+ (pts
+ (xy 0 0)
+ (xy -1.27 -1.905)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (polyline
+ (pts
+ (xy 1.27 -1.905)
+ (xy 0 0)
+ )
+ (stroke (width 0.254) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ (pin power_in line (at 0 -2.54 90) (length 2.54)
+ (name "VCC" (effects (font (size 0 0))))
+ (number "1" (effects (font (size 0 0))))
+ )
+ )
+ )
+ )
+
+ (junction (at 111.76 116.84) (diameter 0) (color 0 0 0 0)
+ (uuid 3be9f4a3-8a02-4fd7-9f6c-da7d582dafea)
+ )
+ (junction (at 248.92 58.42) (diameter 0) (color 0 0 0 0)
+ (uuid 5b5c2da6-e127-4244-a9c7-89d3dd9b9d0e)
+ )
+ (junction (at 248.92 55.88) (diameter 0) (color 0 0 0 0)
+ (uuid 5e75c680-cd1f-4efb-9c88-ff8e2b2b1ff8)
+ )
+ (junction (at 167.64 66.04) (diameter 0) (color 0 0 0 0)
+ (uuid 6a9d2193-0f99-472a-8866-d4e6371e2972)
+ )
+ (junction (at 248.92 63.5) (diameter 0) (color 0 0 0 0)
+ (uuid 6ecd4c68-b046-4b0f-889a-5d41cd1dc2cb)
+ )
+ (junction (at 231.14 78.74) (diameter 0) (color 0 0 0 0)
+ (uuid b0ff7d85-ee52-44cc-ad09-698a1094d4cf)
+ )
+ (junction (at 111.76 114.3) (diameter 0) (color 0 0 0 0)
+ (uuid e8978151-5b61-49d5-b364-1825da55ca17)
+ )
+
+ (wire (pts (xy 248.92 58.42) (xy 248.92 63.5))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 00d829a1-a4cd-42f2-8807-7979f0cece5f)
+ )
+ (wire (pts (xy 182.88 66.04) (xy 167.64 66.04))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 0851e394-3c6e-47b6-a8d7-6f34d745ac74)
+ )
+ (wire (pts (xy 116.84 114.3) (xy 111.76 114.3))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 0f64423c-7737-49b8-8d8d-a03d155058f8)
+ )
+ (wire (pts (xy 223.52 78.74) (xy 231.14 78.74))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 154fdca3-d781-4b9f-b75d-d963b7d0e418)
+ )
+ (wire (pts (xy 182.88 73.66) (xy 172.72 73.66))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 156e755c-9071-45f4-b96c-f6dfdc03927b)
+ )
+ (wire (pts (xy 111.76 111.76) (xy 111.76 114.3))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 2e34a7f8-00f1-4817-860a-c76dcb24da8b)
+ )
+ (wire (pts (xy 182.88 71.12) (xy 167.64 71.12))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 2e815da2-028a-4df3-9e13-35757d155729)
+ )
+ (wire (pts (xy 167.64 66.04) (xy 167.64 71.12))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 319cc7d9-d4cf-4034-a187-92e0ce0f6924)
+ )
+ (wire (pts (xy 111.76 116.84) (xy 76.2 116.84))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 3472f09d-8a9d-4ef2-8905-1691eee48827)
+ )
+ (wire (pts (xy 182.88 63.5) (xy 172.72 63.5))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 3747cbe9-7a9d-41ff-946c-74d900957c2d)
+ )
+ (wire (pts (xy 99.06 129.54) (xy 76.2 129.54))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 389165f9-cc0a-4e0e-9b05-8ddd936597dc)
+ )
+ (wire (pts (xy 99.06 132.08) (xy 76.2 132.08))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 4afa6803-f447-4dd5-9d26-7e99519a7bc4)
+ )
+ (wire (pts (xy 248.92 63.5) (xy 248.92 71.12))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 5ef45c41-2f65-4741-9664-795626b404b6)
+ )
+ (wire (pts (xy 248.92 55.88) (xy 223.52 55.88))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 638ddb51-d601-4887-bed6-fce116198a39)
+ )
+ (wire (pts (xy 111.76 116.84) (xy 111.76 119.38))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 653bb1bb-8447-4e5a-87fd-d46e8613c608)
+ )
+ (wire (pts (xy 231.14 78.74) (xy 231.14 81.28))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 724d0af0-f852-47b9-b980-850731e5f9e5)
+ )
+ (wire (pts (xy 167.64 53.34) (xy 167.64 66.04))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 755d34bd-e50b-424e-87fa-d2c4acd2a268)
+ )
+ (wire (pts (xy 231.14 71.12) (xy 231.14 78.74))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid 9290f265-8570-4ab7-b46f-9e86358ae1bc)
+ )
+ (wire (pts (xy 248.92 55.88) (xy 248.92 58.42))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid a1fa7a94-7c89-4b7e-8762-b4a09f13c9ef)
+ )
+ (wire (pts (xy 99.06 121.92) (xy 76.2 121.92))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid aa45cb4e-b839-42a1-9a53-2492f871c18d)
+ )
+ (wire (pts (xy 241.3 78.74) (xy 241.3 81.28))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid b1159d88-bd16-4ca1-bf5a-cfff2c81d09c)
+ )
+ (wire (pts (xy 248.92 53.34) (xy 248.92 55.88))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid b466aaf1-3c2f-4060-b31f-ddf149d17782)
+ )
+ (wire (pts (xy 236.22 66.04) (xy 223.52 66.04))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid bb32e1ee-b162-47c5-8677-4117b6e7274d)
+ )
+ (wire (pts (xy 248.92 58.42) (xy 223.52 58.42))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid c3420623-f638-464d-8f0f-5edb87189e8b)
+ )
+ (wire (pts (xy 76.2 119.38) (xy 99.06 119.38))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid c6fdc16c-fe06-4b73-8abf-ac7c1a3293a2)
+ )
+ (wire (pts (xy 111.76 116.84) (xy 114.3 116.84))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid d79d922e-c8b5-4de7-b825-0dd36d6321d2)
+ )
+ (wire (pts (xy 111.76 114.3) (xy 76.2 114.3))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid db884c28-3707-4cc9-8c02-fb5a883e8a44)
+ )
+ (wire (pts (xy 99.06 127) (xy 76.2 127))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid e111b61b-2d82-48b0-a212-438605d810dd)
+ )
+ (wire (pts (xy 248.92 78.74) (xy 248.92 81.28))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid eef64e3f-263b-40e6-8ee4-f84dd60d3ea2)
+ )
+ (wire (pts (xy 223.52 71.12) (xy 231.14 71.12))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid ef2fed58-27a4-4522-9d1d-5ddee25dbff9)
+ )
+ (wire (pts (xy 101.6 124.46) (xy 76.2 124.46))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid f214933d-b730-4e16-8e97-075bcb21090f)
+ )
+ (wire (pts (xy 241.3 63.5) (xy 248.92 63.5))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid f2a585fb-25cb-45c6-8051-c610bbc42abe)
+ )
+ (wire (pts (xy 223.52 63.5) (xy 236.22 63.5))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid fdea05fe-b72a-46d3-b6da-d22f0cf2787d)
+ )
+ (wire (pts (xy 241.3 71.12) (xy 241.3 63.5))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid fecb65be-7f95-4d3c-aeea-59ad59501f67)
+ )
+ (wire (pts (xy 99.06 134.62) (xy 76.2 134.62))
+ (stroke (width 0) (type default) (color 0 0 0 0))
+ (uuid ff2311d6-deb7-4124-be16-42c951cc83c7)
+ )
+
+ (label "SDA" (at 99.06 129.54 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid 1f2ddf35-dc5b-48a9-9aeb-dae96a442071)
+ )
+ (label "SCL" (at 99.06 132.08 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid 21b53084-bdf2-4616-92c3-7aa46fbe795a)
+ )
+ (label "A1_RX" (at 99.06 127 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid 38d631f8-88fd-49dd-a9a6-91c3d66cbcd2)
+ )
+ (label "GND" (at 99.06 116.84 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid 38fc0840-7c4f-4f64-9c7e-eacf084ae5f7)
+ )
+ (label "A3_INT2" (at 236.22 66.04 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid 44269636-46bc-41ec-9446-1f65d73ec9b3)
+ )
+ (label "VCC" (at 226.06 55.88 0)
+ (effects (font (size 1.2446 1.2446)) (justify left bottom))
+ (uuid 46da8db1-4215-4027-8094-79fcf2ad9c58)
+ )
+ (label "A0_UNUSED" (at 99.06 134.62 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid 4d41f05b-1450-4e64-859b-23cf1aa567f4)
+ )
+ (label "VCC" (at 172.72 71.12 0)
+ (effects (font (size 1.2446 1.2446)) (justify left bottom))
+ (uuid 500c79ad-8e89-471f-8800-130c5e0e98dc)
+ )
+ (label "SDA" (at 172.72 73.66 0)
+ (effects (font (size 1.2446 1.2446)) (justify left bottom))
+ (uuid 6d2032ff-ae6e-4535-8987-a36cd0d680a2)
+ )
+ (label "A4_INT1" (at 236.22 63.5 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid 76858c84-c012-460a-9f40-e1ccef0b211e)
+ )
+ (label "A3_INT2" (at 99.06 121.92 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid 77150e1e-6cd5-4d60-9843-46ed9469effc)
+ )
+ (label "VCC" (at 226.06 58.42 0)
+ (effects (font (size 1.2446 1.2446)) (justify left bottom))
+ (uuid 96d46355-5a7b-4240-9b6b-58ab63ad3511)
+ )
+ (label "VCC" (at 172.72 66.04 0)
+ (effects (font (size 1.2446 1.2446)) (justify left bottom))
+ (uuid 9fa2f432-cd00-4f4f-ab3e-08976a805c8c)
+ )
+ (label "A2_TX" (at 99.06 124.46 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid bb351afb-4b35-478a-a1a1-26778717767e)
+ )
+ (label "A4_INT1" (at 99.06 119.38 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid c79ddd1b-c6b5-40f9-bc81-ad92b0f855e5)
+ )
+ (label "VCC" (at 99.06 114.3 180)
+ (effects (font (size 1.2446 1.2446)) (justify right bottom))
+ (uuid e0b7aef4-a3dc-4143-81f5-ca93c61a48a9)
+ )
+ (label "SCL" (at 172.72 63.5 0)
+ (effects (font (size 1.2446 1.2446)) (justify left bottom))
+ (uuid f5ef6b68-a5a1-41f8-9851-28fffa1a6b43)
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:GND") (at 248.92 83.82 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 08832b7f-d991-4c9c-8160-957a0d857246)
+ (property "Reference" "#U$014" (id 0) (at 248.92 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "GND" (id 1) (at 247.396 86.36 0)
+ (effects (font (size 1.27 1.0795)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 248.92 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 248.92 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 4709ba8c-472b-44ac-bfea-6c6701579d64))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:TPB1,27") (at 104.14 124.46 270) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 1936aa8e-84b5-486e-a2d5-81ae44fd41c7)
+ (property "Reference" "TP3" (id 0) (at 105.41 123.19 90)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Value" "TPB1,27" (id 1) (at 104.14 124.46 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (id 2) (at 104.14 124.46 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 104.14 124.46 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "TP" (uuid e469a65d-dae5-4bf9-89e0-b404b90b6762))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:GND") (at 231.14 83.82 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 1b04004e-9983-4fd4-af9a-0f3ede119d12)
+ (property "Reference" "#U$01" (id 0) (at 231.14 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "GND" (id 1) (at 229.616 86.36 0)
+ (effects (font (size 1.27 1.0795)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 231.14 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 231.14 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 0b3d80bf-3b39-43e0-bf5d-7a74ac6a44c9))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:VCC") (at 111.76 109.22 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 27a644f0-a528-464d-a151-576c61770f20)
+ (property "Reference" "#P+01" (id 0) (at 111.76 109.22 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "VCC" (id 1) (at 109.22 111.76 90)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 111.76 109.22 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 111.76 109.22 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 1d13ddfe-b7e3-4a22-8ae4-d21ab7865026))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:A4L-LOC") (at 17.78 198.12 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 3a5d2761-9f91-41e6-ada5-9ae9f8496ca2)
+ (property "Reference" "#FRAME1" (id 0) (at 17.78 198.12 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "A4L-LOC" (id 1) (at 17.78 198.12 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 17.78 198.12 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 17.78 198.12 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:FH19C-9S-0.5SH_10-FFC") (at 63.5 124.46 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 4dc3cbb6-fb37-42a6-81e4-ab57e8e35700)
+ (property "Reference" "U$3" (id 0) (at 72.39 106.68 0)
+ (effects (font (size 1.778 1.5113)) (justify left))
+ )
+ (property "Value" "FH19C-9S-0.5SH_10-FFC" (id 1) (at 72.39 109.22 0)
+ (effects (font (size 1.778 1.5113)) (justify left))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:FH19C9S05SH10-FFC" (id 2) (at 63.5 124.46 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 63.5 124.46 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 72b7908a-a147-4c13-9bc2-0f190c00bd00))
+ (pin "2" (uuid 93127296-6db8-4f2d-ab2e-70bcfbd19dc5))
+ (pin "3" (uuid 1d032ee1-419f-4e2d-b2bc-24b14f50ad69))
+ (pin "4" (uuid ded1e12f-6a29-45c8-aee8-823f6f972229))
+ (pin "5" (uuid 4e597a75-84c2-4e3a-9f69-f7f6e5fa1bda))
+ (pin "6" (uuid c1e17350-4910-4735-a63a-d0906e7c105c))
+ (pin "7" (uuid 7e6c73cc-1198-4aa9-a7a6-aa505f4a1a2a))
+ (pin "8" (uuid aca85423-5f80-4e94-8678-d85d20991330))
+ (pin "9" (uuid 3e4a8598-b196-4243-b6e1-7411945d1d18))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:VCC") (at 167.64 50.8 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 52195f21-2ebf-4931-bb78-0509347a85b2)
+ (property "Reference" "#P+03" (id 0) (at 167.64 50.8 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "VCC" (id 1) (at 165.1 53.34 90)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 167.64 50.8 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 167.64 50.8 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 1e5074b2-0daf-46ed-8b33-13c9df71009c))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:TPB1,27") (at 116.84 116.84 270) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 6ea9dd18-adcc-4aba-8efa-d45943bee5d6)
+ (property "Reference" "TP1" (id 0) (at 118.11 118.11 90)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Value" "TPB1,27" (id 1) (at 116.84 116.84 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (id 2) (at 116.84 116.84 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 116.84 116.84 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "TP" (uuid 81e7a0b7-339c-44c8-a5a6-474ef8c169b7))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:CAP_CERAMIC_0603MP") (at 248.92 76.2 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid 81f117f8-2e22-4d95-9843-a6438cc9b8fd)
+ (property "Reference" "C1" (id 0) (at 246.63 74.95 90))
+ (property "Value" "0.1uF" (id 1) (at 251.22 74.95 90))
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:_0603MP" (id 2) (at 248.92 76.2 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 248.92 76.2 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid ad164184-ecb9-4520-8bd1-3781f2bb9f35))
+ (pin "2" (uuid c52bd0b0-2c8d-4af6-b874-54b820cd2478))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:TPB1,27") (at 101.6 127 270) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid a4b4c4fb-1c78-4a88-b6d6-c95ced14e6a1)
+ (property "Reference" "TP4" (id 0) (at 102.87 128.27 90)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Value" "TPB1,27" (id 1) (at 101.6 127 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (id 2) (at 101.6 127 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 101.6 127 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "TP" (uuid 200c6f8a-c516-4d05-906f-a06de5c8f920))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:GND") (at 241.3 83.82 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid bba0ef42-0f18-42c4-9c14-469ed04b2b13)
+ (property "Reference" "#U$02" (id 0) (at 241.3 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "GND" (id 1) (at 239.776 86.36 0)
+ (effects (font (size 1.27 1.0795)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 241.3 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 241.3 83.82 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid d6779978-9459-4c86-a16f-794c7bc57caa))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:CAP_CERAMIC_0603MP") (at 241.3 73.66 180) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid dbf111d5-5941-4c1b-8fad-675981ce34f4)
+ (property "Reference" "C2" (id 0) (at 243.59 74.91 90))
+ (property "Value" "10uF" (id 1) (at 239 74.91 90))
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:_0603MP" (id 2) (at 241.3 73.66 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 241.3 73.66 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 00d2080f-6c63-4b6f-8f3d-d355189a4153))
+ (pin "2" (uuid 80a799d6-a88a-48f5-84e5-0598868f31f3))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:GND") (at 111.76 121.92 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid e3514b09-efc5-499f-bd79-ef5eca3c0b60)
+ (property "Reference" "#U$04" (id 0) (at 111.76 121.92 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "GND" (id 1) (at 110.236 124.46 0)
+ (effects (font (size 1.27 1.0795)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 111.76 121.92 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 111.76 121.92 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid e0dddb6b-1763-4e9f-b200-729d444448c1))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:VCC") (at 248.92 50.8 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid e9a74e60-54a5-4aa5-b8e6-4f07382f4dcb)
+ (property "Reference" "#P+02" (id 0) (at 248.92 50.8 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Value" "VCC" (id 1) (at 246.38 53.34 90)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:" (id 2) (at 248.92 50.8 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 248.92 50.8 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid e9de28fb-a205-454e-aed8-8c8fbcb7ffee))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:TPB1,27") (at 119.38 114.3 270) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid ec24969b-8391-44db-a79f-3fa50f7e4597)
+ (property "Reference" "TP2" (id 0) (at 120.65 113.03 90)
+ (effects (font (size 1.778 1.5113)) (justify left bottom))
+ )
+ (property "Value" "TPB1,27" (id 1) (at 119.38 114.3 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:B1,27" (id 2) (at 119.38 114.3 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 119.38 114.3 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "TP" (uuid 0632665e-cb0e-41a5-9b86-c608fcc0bf7b))
+ )
+
+ (symbol (lib_id "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import:LIS2DW12TR") (at 203.2 66.04 0) (unit 1)
+ (in_bom yes) (on_board yes)
+ (uuid f61a4c5e-e4e7-40df-976e-1899d4c8a12a)
+ (property "Reference" "U2" (id 0) (at 187.9561 53.3368 0)
+ (effects (font (size 1.7784 1.5116)) (justify left bottom))
+ )
+ (property "Value" "LIS2DW12TR" (id 1) (at 187.9506 83.8309 0)
+ (effects (font (size 1.779 1.5121)) (justify left bottom))
+ )
+ (property "Footprint" "OSO-MISC-21-021 Sensor Watch Motion II:LGA12R50P4X4_200X200X70" (id 2) (at 203.2 66.04 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 203.2 66.04 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (pin "1" (uuid 5437a445-76be-45a9-bb36-e8226cbb76a4))
+ (pin "10" (uuid aa2f966b-27db-4c46-b37b-8938b6e96a7f))
+ (pin "11" (uuid c232d3d2-179b-4c32-be66-1a8ae272c155))
+ (pin "12" (uuid 8730c793-1e0a-457f-a42f-dcfafda24f0c))
+ (pin "2" (uuid 2600904f-6913-4cf0-9bd0-d929f49ef6bf))
+ (pin "3" (uuid b6155be8-ddbb-47d3-8286-904b3aa12873))
+ (pin "4" (uuid 1791a69b-a561-4028-ac1a-19f15f51996d))
+ (pin "6" (uuid 8e4926ae-605c-49b2-b92b-7a1af989fde5))
+ (pin "7" (uuid 41069e2b-d371-466e-92f5-369959deaacf))
+ (pin "8" (uuid 27b027fb-59ee-4adb-b292-2d2f884c594e))
+ (pin "9" (uuid 4732e433-8b02-41d8-9a36-1db5b79ff2ad))
+ )
+
+ (sheet_instances
+ (path "/" (page "1"))
+ )
+
+ (symbol_instances
+ (path "/3a5d2761-9f91-41e6-ada5-9ae9f8496ca2"
+ (reference "#FRAME1") (unit 1) (value "A4L-LOC") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:")
+ )
+ (path "/27a644f0-a528-464d-a151-576c61770f20"
+ (reference "#P+01") (unit 1) (value "VCC") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:")
+ )
+ (path "/e9a74e60-54a5-4aa5-b8e6-4f07382f4dcb"
+ (reference "#P+02") (unit 1) (value "VCC") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:")
+ )
+ (path "/52195f21-2ebf-4931-bb78-0509347a85b2"
+ (reference "#P+03") (unit 1) (value "VCC") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:")
+ )
+ (path "/1b04004e-9983-4fd4-af9a-0f3ede119d12"
+ (reference "#U$01") (unit 1) (value "GND") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:")
+ )
+ (path "/bba0ef42-0f18-42c4-9c14-469ed04b2b13"
+ (reference "#U$02") (unit 1) (value "GND") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:")
+ )
+ (path "/e3514b09-efc5-499f-bd79-ef5eca3c0b60"
+ (reference "#U$04") (unit 1) (value "GND") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:")
+ )
+ (path "/08832b7f-d991-4c9c-8160-957a0d857246"
+ (reference "#U$014") (unit 1) (value "GND") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:")
+ )
+ (path "/81f117f8-2e22-4d95-9843-a6438cc9b8fd"
+ (reference "C1") (unit 1) (value "0.1uF") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:_0603MP")
+ )
+ (path "/dbf111d5-5941-4c1b-8fad-675981ce34f4"
+ (reference "C2") (unit 1) (value "10uF") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:_0603MP")
+ )
+ (path "/6ea9dd18-adcc-4aba-8efa-d45943bee5d6"
+ (reference "TP1") (unit 1) (value "TPB1,27") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:B1,27")
+ )
+ (path "/ec24969b-8391-44db-a79f-3fa50f7e4597"
+ (reference "TP2") (unit 1) (value "TPB1,27") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:B1,27")
+ )
+ (path "/1936aa8e-84b5-486e-a2d5-81ae44fd41c7"
+ (reference "TP3") (unit 1) (value "TPB1,27") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:B1,27")
+ )
+ (path "/a4b4c4fb-1c78-4a88-b6d6-c95ced14e6a1"
+ (reference "TP4") (unit 1) (value "TPB1,27") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:B1,27")
+ )
+ (path "/4dc3cbb6-fb37-42a6-81e4-ab57e8e35700"
+ (reference "U$3") (unit 1) (value "FH19C-9S-0.5SH_10-FFC") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:FH19C9S05SH10-FFC")
+ )
+ (path "/f61a4c5e-e4e7-40df-976e-1899d4c8a12a"
+ (reference "U2") (unit 1) (value "LIS2DW12TR") (footprint "OSO-MISC-21-021 Sensor Watch Motion II:LGA12R50P4X4_200X200X70")
+ )
+ )
+)
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/B1,27.kicad_mod b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/B1,27.kicad_mod
new file mode 100644
index 00000000..24c34863
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/B1,27.kicad_mod
@@ -0,0 +1,15 @@
+(footprint "B1,27" (version 20211014) (generator pcbnew)
+ (layer "F.Cu")
+ (tedit 0)
+ (descr "TEST PAD")
+ (fp_text reference "REF**" (at -0.635 -1.016) (layer "F.SilkS")
+ (effects (font (size 1.143 1.143) (thickness 0.127)) (justify left bottom))
+ (tstamp 66611b0e-db6e-4021-9427-c32ecc053115)
+ )
+ (fp_text value ">VALUE" (at -0.635 0.762) (layer "F.Fab")
+ (effects (font (size 0.023368 0.023368) (thickness 0.002032)) (justify left bottom))
+ (tstamp 4ac60ae9-6f70-4bce-8da5-6eac3cd3a3ad)
+ )
+ (pad "TP" smd roundrect (at 0 0) (size 1.27 1.27) (layers "F.Cu" "F.Mask") (roundrect_rratio 0.5)
+ (solder_mask_margin 0.0635) (tstamp b8f939e4-9cdb-4994-993d-3ae41bb30177))
+)
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/FH19C9S05SH10-FFC.kicad_mod b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/FH19C9S05SH10-FFC.kicad_mod
new file mode 100644
index 00000000..c3af1333
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/FH19C9S05SH10-FFC.kicad_mod
@@ -0,0 +1,42 @@
+(footprint "FH19C9S05SH10-FFC" (version 20211014) (generator pcbnew)
+ (layer "F.Cu")
+ (tedit 0)
+ (descr "FH19C-9S-0.5SH(10)-1
\n")
+ (fp_text reference "REF**" (at 0 0) (layer "F.SilkS")
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ (tstamp 2891e198-646d-4022-ba59-cc504d2b7d16)
+ )
+ (fp_text value "" (at 0 0) (layer "F.Fab")
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ (tstamp cb35e01a-ade6-4e76-8860-9691b680c03d)
+ )
+ (fp_poly (pts
+ (xy 2.56543 3.556)
+ (xy -2.56537 3.556)
+ (xy -2.56643 0.898)
+ (xy 2.56437 0.898)
+ ) (layer "B.Mask") (width 0) (fill solid) (tstamp 2b3c01e4-c750-4f56-8673-86cd61068635))
+ (fp_line (start -2.4892 0) (end -2.4892 3.35) (layer "Edge.Cuts") (width 0.05) (tstamp 593436ec-2ee2-4f89-a482-ad9d710ea79d))
+ (fp_line (start 2.4892 0) (end 2.4892 3.35) (layer "Edge.Cuts") (width 0.05) (tstamp 757ab00f-2565-4955-8ca5-247d2ab4a459))
+ (fp_line (start -2.3392 3.5) (end 2.3392 3.5) (layer "Edge.Cuts") (width 0.05) (tstamp be6a643e-0f3d-406b-82ce-bc5dd821e563))
+ (fp_arc (start -2.3392 3.5) (mid -2.445266 3.456066) (end -2.4892 3.35) (layer "Edge.Cuts") (width 0.05) (tstamp eeb7dd92-dd48-45ec-b09f-f99710ea3b88))
+ (fp_arc (start 2.4892 3.35) (mid 2.445266 3.456066) (end 2.3392 3.5) (layer "Edge.Cuts") (width 0.05) (tstamp eecbbc5b-a539-4c7c-8789-2616156ab52b))
+ (pad "1" smd rect (at 2 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp 2b902acc-4a08-4047-ad60-bbf1eeaab6ab))
+ (pad "2" smd rect (at 1.5 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp d8e62cc7-7124-4f2e-889d-3746b3d86a64))
+ (pad "3" smd rect (at 1 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp f999fa66-5f95-4c6d-9b5b-e8836c9dc89d))
+ (pad "4" smd rect (at 0.5 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp ab03249d-815c-4b6a-af6f-cad8e8308166))
+ (pad "5" smd rect (at 0 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp badef670-3ddf-4b49-83a7-fe6eb4b07cad))
+ (pad "6" smd rect (at -0.5 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp 102be87f-c42b-4e09-b634-d2882c35f5ae))
+ (pad "7" smd rect (at -1 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp 092cbced-a774-4b39-a3d1-c6d31915fb62))
+ (pad "8" smd rect (at -1.5 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp 1b3a5bc0-e9f9-47de-b2f0-4672614e470a))
+ (pad "9" smd rect (at -2 2.25 90) (size 2.5 0.347) (layers "B.Cu" "B.Mask")
+ (solder_mask_margin 0.0635) (tstamp b0acd8bd-47e9-4935-a884-665ee78af58f))
+)
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/LGA12R50P4X4_200X200X70.kicad_mod b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/LGA12R50P4X4_200X200X70.kicad_mod
new file mode 100644
index 00000000..5709a710
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/LGA12R50P4X4_200X200X70.kicad_mod
@@ -0,0 +1,54 @@
+(footprint "LGA12R50P4X4_200X200X70" (version 20211014) (generator pcbnew)
+ (layer "F.Cu")
+ (tedit 0)
+ (fp_text reference "REF**" (at -1.50225 -1.50225) (layer "F.SilkS")
+ (effects (font (size 0.561674 0.561674) (thickness 0.048841)) (justify left bottom))
+ (tstamp a841a941-dbd4-406e-9e83-16f988577ed7)
+ )
+ (fp_text value ">VALUE" (at -1.50114 2.001518) (layer "F.Fab")
+ (effects (font (size 0.561257 0.561257) (thickness 0.048805)) (justify left bottom))
+ (tstamp 0a9955a0-6068-4d38-b05e-1ba1984749d1)
+ )
+ (fp_line (start 0.555 -1.05) (end 1.05 -1.05) (layer "F.SilkS") (width 0.127) (tstamp 1ec22d83-cad4-4dee-9f07-ac20256d34bb))
+ (fp_line (start 0.555 1.05) (end 1.05 1.05) (layer "F.SilkS") (width 0.127) (tstamp 2ab2f09e-0c2e-40e1-8cad-8618b2964772))
+ (fp_line (start 1.05 1.05) (end 1.05 0.555) (layer "F.SilkS") (width 0.127) (tstamp 691adb00-c7cf-42cd-b3b3-3cb402f71864))
+ (fp_line (start -1.05 -0.555) (end -1.05 -1.05) (layer "F.SilkS") (width 0.127) (tstamp 693e0e56-7ca2-4e3c-943f-7d93d816dd88))
+ (fp_line (start -1.05 -1.05) (end -0.555 -1.05) (layer "F.SilkS") (width 0.127) (tstamp 6dd9b638-7d83-48ad-9c0b-d0a3b14ff194))
+ (fp_line (start 1.05 -1.05) (end 1.05 -0.555) (layer "F.SilkS") (width 0.127) (tstamp cfdfed39-3585-4f3d-af2c-df735f17f29f))
+ (fp_line (start -1.05 0.555) (end -1.05 1.05) (layer "F.SilkS") (width 0.127) (tstamp d5677caf-5ab2-407c-a936-e4226c17744c))
+ (fp_line (start -1.05 1.05) (end -0.555 1.05) (layer "F.SilkS") (width 0.127) (tstamp f1e92131-893d-407c-85ed-04a3d79dc8bc))
+ (fp_circle (center -1.5 -0.75) (end -1.45 -0.75) (layer "F.SilkS") (width 0.1) (fill none) (tstamp 6e7bb868-6df4-43cc-ae2f-1d2b706e4c2e))
+ (fp_line (start 1.25 -1.25) (end 1.25 1.25) (layer "F.CrtYd") (width 0.05) (tstamp 47f30fc1-7ed7-4645-8836-972c17bb4fe5))
+ (fp_line (start -1.25 1.25) (end -1.25 -1.25) (layer "F.CrtYd") (width 0.05) (tstamp 4c1ed0d1-05e7-40bc-b7c8-845b3478b8d5))
+ (fp_line (start 1.25 1.25) (end -1.25 1.25) (layer "F.CrtYd") (width 0.05) (tstamp 593de62e-58e0-4efc-8ba1-b886cd08985c))
+ (fp_line (start -1.25 -1.25) (end 1.25 -1.25) (layer "F.CrtYd") (width 0.05) (tstamp 75cee19c-8883-4eb2-bb54-93d3242790c4))
+ (fp_line (start 1 -1) (end 1 1) (layer "F.Fab") (width 0.127) (tstamp 0e31b15a-0bed-4c02-a998-422f761d6f21))
+ (fp_line (start 1 1) (end -1 1) (layer "F.Fab") (width 0.127) (tstamp 25e98454-7b51-4256-99ce-d71274a09ddd))
+ (fp_line (start -1 1) (end -1 -1) (layer "F.Fab") (width 0.127) (tstamp 9455eb49-e46c-4cc1-931f-245a8cf4484a))
+ (fp_line (start -1 -1) (end 1 -1) (layer "F.Fab") (width 0.127) (tstamp 96a4c6a7-70a8-4dc8-851d-90638be2de9c))
+ (fp_circle (center -0.75 -0.75) (end -0.7 -0.75) (layer "F.Fab") (width 0.1) (fill none) (tstamp 3ca22245-934d-45a7-84cc-c2d904109998))
+ (pad "1" smd rect (at -0.76 -0.75) (size 0.4 0.35) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 9b76f23f-a839-4e38-ab2f-47fe312a5451))
+ (pad "2" smd rect (at -0.76 -0.25) (size 0.4 0.35) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 084df340-c833-4415-b5ea-ddf2da7aeab1))
+ (pad "3" smd rect (at -0.76 0.25) (size 0.4 0.35) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 56e68fb3-7611-43b1-852f-2d9d710a10d4))
+ (pad "4" smd rect (at -0.76 0.75) (size 0.4 0.35) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp e1e62aad-3a36-476f-b704-a4b4240137d2))
+ (pad "5" smd rect (at -0.25 0.76) (size 0.35 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp cd41e53a-0c76-4c5f-800c-753fe980f311))
+ (pad "6" smd rect (at 0.25 0.76) (size 0.35 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 8d08899e-801e-464e-a053-80ece5d40773))
+ (pad "7" smd rect (at 0.76 0.75) (size 0.4 0.35) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 124b0827-61a2-4ca5-9e46-df5984bdff25))
+ (pad "8" smd rect (at 0.76 0.25) (size 0.4 0.35) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 4a93be6c-d59b-4ab5-b017-af8dd5c58e3a))
+ (pad "9" smd rect (at 0.76 -0.25) (size 0.4 0.35) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 0baa1d51-a8bc-4483-b400-80a9ff172160))
+ (pad "10" smd rect (at 0.76 -0.75) (size 0.4 0.35) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 09311f64-fad0-424e-8eae-c2a2bd2cac0a))
+ (pad "11" smd rect (at 0.25 -0.76) (size 0.35 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 41f01f34-006e-48b6-a52b-6948fb66b57f))
+ (pad "12" smd rect (at -0.25 -0.76) (size 0.35 0.4) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp f87a7419-f9cb-4939-a776-f1a6ae037a85))
+)
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/_0603MP.kicad_mod b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/_0603MP.kicad_mod
new file mode 100644
index 00000000..4954bc16
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty/_0603MP.kicad_mod
@@ -0,0 +1,38 @@
+(footprint "_0603MP" (version 20211014) (generator pcbnew)
+ (layer "F.Cu")
+ (tedit 0)
+ (descr "0603 MicroPitch")
+ (fp_text reference "REF**" (at -0.9525 -0.635) (layer "F.SilkS")
+ (effects (font (size 0.666496 0.666496) (thickness 0.146304)) (justify left bottom))
+ (tstamp 08ea7653-fcf2-4de6-bc2d-71c2559866c5)
+ )
+ (fp_text value ">VALUE" (at -0.9525 0.9525) (layer "F.Fab")
+ (effects (font (size 0.36576 0.36576) (thickness 0.04064)) (justify left bottom))
+ (tstamp c6f30152-8d59-4f78-bba8-2d4dbdc2be46)
+ )
+ (fp_poly (pts
+ (xy -0.1999 0.25)
+ (xy 0.1999 0.25)
+ (xy 0.1999 -0.25)
+ (xy -0.1999 -0.25)
+ ) (layer "F.Adhes") (width 0) (fill solid) (tstamp e4d4e5f7-6687-4207-9525-f44fa6947d43))
+ (fp_line (start 0 -0.254) (end 0 0.254) (layer "F.SilkS") (width 0.2032) (tstamp 8494f3d6-e422-4ed3-afab-572a517aba7f))
+ (fp_line (start -0.432 0.306) (end 0.432 0.306) (layer "F.Fab") (width 0.1016) (tstamp 3a69b14b-04ad-4165-ab2f-4ad1c270b812))
+ (fp_line (start 0.432 -0.306) (end -0.432 -0.306) (layer "F.Fab") (width 0.1016) (tstamp 590cef58-c4cf-4e7a-ac89-6a7a7801e6c8))
+ (fp_poly (pts
+ (xy 0.4318 0.4)
+ (xy 0.8 0.4)
+ (xy 0.8 -0.4)
+ (xy 0.4318 -0.4)
+ ) (layer "F.Fab") (width 0) (fill solid) (tstamp 2630aa28-dcfc-4f76-bf4f-eac1115a0083))
+ (fp_poly (pts
+ (xy -0.8 0.4)
+ (xy -0.4318 0.4)
+ (xy -0.4318 -0.4)
+ (xy -0.8 -0.4)
+ ) (layer "F.Fab") (width 0) (fill solid) (tstamp 6d4a5dc7-5dd7-4742-b5e4-d512d0913c9b))
+ (pad "1" smd rect (at -0.762 0) (size 0.8 0.8) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 5ce1ec75-6b24-4360-8dcb-9033548b2be2))
+ (pad "2" smd rect (at 0.762 0) (size 0.8 0.8) (layers "F.Cu" "F.Paste" "F.Mask")
+ (solder_mask_margin 0.0635) (tstamp 77d09e3e-6f43-44cb-8866-ea235200f8dc))
+)
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00.zip b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00.zip
new file mode 100644
index 00000000..76f29cb1
Binary files /dev/null and b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/OSO-SWAB-B1-00.zip differ
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/empty.kicad_wks b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/empty.kicad_wks
new file mode 100644
index 00000000..f728a623
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/empty.kicad_wks
@@ -0,0 +1,5 @@
+(kicad_wks (version 20210606) (generator pl_editor)
+(setup (textsize 1.5 1.5)(linewidth 0.15)(textlinewidth 0.15)
+(left_margin 10)(right_margin 10)(top_margin 10)(bottom_margin 10))
+(line (name "segm1:Line") (start 0 0) (end 0 0))
+)
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/fp-lib-table b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/fp-lib-table
new file mode 100644
index 00000000..30c232a7
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/fp-lib-table
@@ -0,0 +1,3 @@
+(fp_lib_table
+ (lib (name "OSO-MISC-21-021 Sensor Watch Motion II")(type "KiCad")(uri "$(KIPRJMOD)/OSO-SWAB-B1-00 Sensor Watch Accelerometer.pretty")(options "")(descr ""))
+)
diff --git a/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/sym-lib-table b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/sym-lib-table
new file mode 100644
index 00000000..31b072f6
--- /dev/null
+++ b/PCB/Sensor Boards/Kicad/OSO-SWAB-B1-00 Sensor Watch Accelerometer/sym-lib-table
@@ -0,0 +1,3 @@
+(sym_lib_table
+ (lib (name "OSO-MISC-21-021 Sensor Watch Motion II-eagle-import")(type "KiCad")(uri "${KIPRJMOD}/OSO-MISC-21-021 Sensor Watch Motion II-eagle-import.kicad_sym")(options "")(descr ""))
+)
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/.gitignore b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/.gitignore
new file mode 100644
index 00000000..88bb66f9
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/.gitignore
@@ -0,0 +1,2 @@
+OPT3001DNPT/*
+
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001-cache.lib b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001-cache.lib
new file mode 100644
index 00000000..90c56805
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001-cache.lib
@@ -0,0 +1,162 @@
+EESchema-LIBRARY Version 2.4
+#encoding utf-8
+#
+# OPT3001DNPT_OPT3001DNPT
+#
+DEF OPT3001DNPT_OPT3001DNPT U 0 40 Y Y 1 L N
+F0 "U" -400 550 50 H V L BNN
+F1 "OPT3001DNPT_OPT3001DNPT" -400 -450 50 H V L BNN
+F2 "SON65P200X200X65-7N" 0 0 50 H I L BNN
+F3 "" 0 0 50 H I L BNN
+F4 "0.65" 0 0 50 H I L BNN "ENOM"
+F5 "1.9" 0 0 50 H I L BNN "E_MIN"
+F6 "0.25" 0 0 50 H I L BNN "L_MIN"
+F7 "2.0" 0 0 50 H I L BNN "E_NOM"
+F8 "0.65" 0 0 50 H I L BNN "A_MAX"
+F9 "0.65" 0 0 50 H I L BNN "E2_NOM"
+F10 "IPC 7351B" 0 0 50 H I L BNN "STANDARD"
+F11 "01/2018" 0 0 50 H I L BNN "PARTREV"
+F12 "0.25" 0 0 50 H I L BNN "B_NOM"
+F13 "0.3" 0 0 50 H I L BNN "B_MAX"
+F14 "0.2" 0 0 50 H I L BNN "B_MIN"
+F15 "6.0" 0 0 50 H I L BNN "PIN_COUNT"
+F16 "2.1" 0 0 50 H I L BNN "E_MAX"
+F17 "0.35" 0 0 50 H I L BNN "L_MAX"
+F18 "Texas Instruments" 0 0 50 H I L BNN "MANUFACTURER"
+F19 "1.35" 0 0 50 H I L BNN "D2_NOM"
+F20 "0.3" 0 0 50 H I L BNN "L_NOM"
+F21 "2.1" 0 0 50 H I L BNN "D_MAX"
+F22 "2.0" 0 0 50 H I L BNN "D_NOM"
+F23 "1.9" 0 0 50 H I L BNN "D_MIN"
+DRAW
+S -400 -400 400 500 0 0 10 f
+X VDD 1 600 400 200 L 40 40 0 0 W
+X ADDR 2 -600 200 200 R 40 40 0 0 I
+X GND 3 600 -300 200 L 40 40 0 0 W
+X SCL 4 -600 0 200 R 40 40 0 0 I C
+X INT 5 600 200 200 L 40 40 0 0 O
+X SDA 6 -600 -100 200 R 40 40 0 0 B
+X EXP 7 600 -100 200 L 40 40 0 0 P
+ENDDRAW
+ENDDEF
+#
+# Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_A4L-LOC
+#
+DEF Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_A4L-LOC #FRAME 0 40 Y Y 1 L N
+F0 "#FRAME" 0 0 50 H I C CNN
+F1 "Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_A4L-LOC" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 8550 600 85 0 1 0 >DRAWING_NAME Normal 0 L B
+T 0 8550 400 76 0 1 0 >LAST_DATE_TIME Normal 0 L B
+T 0 8995 200 85 0 1 0 >SHEET Normal 0 L B
+T 0 8550 800 85 0 1 0 "by joey castillo" Normal 0 L B
+T 0 9300 200 85 0 1 0 "cc-by-sa 4.0" Normal 0 L B
+T 0 7300 200 180 0 1 0 oddly_specific_objects Normal 0 L B
+T 0 8540 195 85 0 1 0 Sheet: Normal 0 L B
+P 2 1 0 0 7250 150 7250 950 N
+P 2 1 0 0 7250 950 8475 950 N
+P 2 1 0 0 8475 350 8475 150 N
+P 2 1 0 0 8475 350 8475 550 N
+P 2 1 0 0 8475 550 8475 750 N
+P 2 1 0 0 8475 550 10100 550 N
+P 2 1 0 0 8475 750 8475 950 N
+P 2 1 0 0 8475 750 10100 750 N
+P 2 1 0 0 8475 950 10100 950 N
+P 2 1 0 0 9250 150 9250 350 N
+P 2 1 0 0 9250 350 8475 350 N
+P 2 1 0 0 9250 350 10100 350 N
+P 2 1 0 0 10100 150 10100 350 N
+P 2 1 0 0 10100 350 10100 550 N
+P 2 1 0 0 10100 550 10100 750 N
+P 2 1 0 0 10100 750 10100 950 N
+ENDDRAW
+ENDDEF
+#
+# Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_FH19C-9S-0.5SH_10-FFC
+#
+DEF Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_FH19C-9S-0.5SH_10-FFC ~ 0 40 Y Y 1 L N
+F0 "" 350 700 59 H V L CNN
+F1 "Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_FH19C-9S-0.5SH_10-FFC" 350 600 59 H V L CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -200 500 -200 -500 N
+P 2 1 0 0 -200 500 300 500 N
+P 2 1 0 0 300 -500 -200 -500 N
+P 2 1 0 0 300 -500 300 500 N
+X 1 1 500 400 200 L 50 50 1 0 B
+X 2 2 500 300 200 L 50 50 1 0 B
+X 3 3 500 200 200 L 50 50 1 0 B
+X 4 4 500 100 200 L 50 50 1 0 B
+X 5 5 500 0 200 L 50 50 1 0 B
+X 6 6 500 -100 200 L 50 50 1 0 B
+X 7 7 500 -200 200 L 50 50 1 0 B
+X 8 8 500 -300 200 L 50 50 1 0 B
+X 9 9 500 -400 200 L 50 50 1 0 B
+ENDDRAW
+ENDDEF
+#
+# Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_GND
+#
+DEF Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_GND #GND 0 40 Y Y 1 L P
+F0 "#GND" 0 0 50 H I C CNN
+F1 "Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_GND" -100 -100 59 H V L BNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -75 0 75 0 N
+X GND 1 0 100 100 D 0 0 1 0 W
+ENDDRAW
+ENDDEF
+#
+# Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_RESISTOR_0603MP
+#
+DEF Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_RESISTOR_0603MP R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_RESISTOR_0603MP" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_TPB1,27
+#
+DEF Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_TPB1,27 TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_TPB1,27" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_VCC
+#
+DEF Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_VCC #P+ 0 40 Y Y 1 L P
+F0 "#P+" 0 0 50 H I C CNN
+F1 "Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import_VCC" -100 -100 59 V V L BNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 0 0 -50 -75 N
+P 2 1 0 0 50 -75 0 0 N
+X VCC 1 0 -100 100 U 0 0 1 0 W
+ENDDRAW
+ENDDEF
+#
+#End Library
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.kicad_pcb b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.kicad_pcb
new file mode 100644
index 00000000..e13c73a5
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.kicad_pcb
@@ -0,0 +1,497 @@
+(kicad_pcb (version 20171130) (host pcbnew 5.1.9+dfsg1-1+deb11u1)
+
+ (general
+ (thickness 1.6)
+ (drawings 22)
+ (tracks 90)
+ (zones 0)
+ (modules 10)
+ (nets 12)
+ )
+
+ (page A4)
+ (layers
+ (0 Top signal)
+ (31 Bottom signal)
+ (32 B.Adhes user hide)
+ (33 F.Adhes user hide)
+ (34 B.Paste user hide)
+ (35 F.Paste user hide)
+ (36 B.SilkS user hide)
+ (37 F.SilkS user)
+ (38 B.Mask user hide)
+ (39 F.Mask user hide)
+ (40 Dwgs.User user hide)
+ (41 Cmts.User user hide)
+ (42 Eco1.User user hide)
+ (43 Eco2.User user)
+ (44 Edge.Cuts user)
+ (45 Margin user hide)
+ (46 B.CrtYd user hide)
+ (47 F.CrtYd user hide)
+ (48 B.Fab user hide)
+ (49 F.Fab user hide)
+ )
+
+ (setup
+ (last_trace_width 0.25)
+ (trace_clearance 0.1524)
+ (zone_clearance 0.508)
+ (zone_45_only no)
+ (trace_min 0.2)
+ (via_size 0.8)
+ (via_drill 0.4)
+ (via_min_size 0.1)
+ (via_min_drill 0.254)
+ (uvia_size 0.3)
+ (uvia_drill 0.1)
+ (uvias_allowed no)
+ (uvia_min_size 0.2)
+ (uvia_min_drill 0.1)
+ (edge_width 0.05)
+ (segment_width 0.2)
+ (pcb_text_width 0.3)
+ (pcb_text_size 1.5 1.5)
+ (mod_edge_width 0.12)
+ (mod_text_size 1 1)
+ (mod_text_width 0.15)
+ (pad_size 1.5 0.27)
+ (pad_drill 0)
+ (pad_to_mask_clearance 0)
+ (aux_axis_origin 0 0)
+ (visible_elements FFFFFF7F)
+ (pcbplotparams
+ (layerselection 0x010fc_ffffffff)
+ (usegerberextensions false)
+ (usegerberattributes true)
+ (usegerberadvancedattributes true)
+ (creategerberjobfile true)
+ (excludeedgelayer true)
+ (linewidth 0.100000)
+ (plotframeref false)
+ (viasonmask false)
+ (mode 1)
+ (useauxorigin false)
+ (hpglpennumber 1)
+ (hpglpenspeed 20)
+ (hpglpendiameter 15.000000)
+ (psnegative false)
+ (psa4output false)
+ (plotreference true)
+ (plotvalue true)
+ (plotinvisibletext false)
+ (padsonsilk false)
+ (subtractmaskfromsilk false)
+ (outputformat 1)
+ (mirror false)
+ (drillshape 1)
+ (scaleselection 1)
+ (outputdirectory ""))
+ )
+
+ (net 0 "")
+ (net 1 GND)
+ (net 2 VCC)
+ (net 3 /A0_TEMP_ENABLE)
+ (net 4 /SCL)
+ (net 5 /SDA)
+ (net 6 /A1_SERCOM3.3_TC3.1)
+ (net 7 /A2_TEMP_SENSE)
+ (net 8 /NC)
+ (net 9 /A4_SERCOM3.2_TC3.0)
+ (net 10 "Net-(U1-Pad7)")
+ (net 11 "Net-(U1-Pad5)")
+
+ (net_class Default "This is the default net class."
+ (clearance 0.1524)
+ (trace_width 0.25)
+ (via_dia 0.8)
+ (via_drill 0.4)
+ (uvia_dia 0.3)
+ (uvia_drill 0.1)
+ (add_net /A0_TEMP_ENABLE)
+ (add_net /A1_SERCOM3.3_TC3.1)
+ (add_net /A2_TEMP_SENSE)
+ (add_net /A4_SERCOM3.2_TC3.0)
+ (add_net /NC)
+ (add_net /SCL)
+ (add_net /SDA)
+ (add_net GND)
+ (add_net "Net-(U1-Pad5)")
+ (add_net "Net-(U1-Pad7)")
+ (add_net VCC)
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:FH19C9S05SH10-FFC" (layer Top) (tedit 0) (tstamp 63E6D203)
+ (at 153.2255 106.5556)
+ (descr "FH19C-9S-0.5SH(10)-1
\n")
+ (path /54307519)
+ (fp_text reference U$3 (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ )
+ (fp_text value FH19C-9S-0.5SH_10-FFC (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ )
+ (fp_poly (pts (xy -2.4902 0.9742) (xy 2.4882 0.9742) (xy 2.4892 3.4798) (xy -2.4892 3.4798)) (layer B.Mask) (width 0))
+ (fp_line (start 2.4892 0) (end 2.4892 3.35) (layer Edge.Cuts) (width 0.05))
+ (fp_line (start -2.3392 3.5) (end 2.3392 3.5) (layer Edge.Cuts) (width 0.05))
+ (fp_line (start -2.4892 0) (end -2.4892 3.35) (layer Edge.Cuts) (width 0.05))
+ (fp_arc (start -2.3392 3.35) (end -2.3392 3.5) (angle 90) (layer Edge.Cuts) (width 0.05))
+ (fp_arc (start 2.3392 3.35) (end 2.3392 3.5) (angle -90) (layer Edge.Cuts) (width 0.05))
+ (pad 1 smd rect (at 2 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 2 VCC) (solder_mask_margin 0.0635))
+ (pad 2 smd rect (at 1.5 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 1 GND) (solder_mask_margin 0.0635))
+ (pad 3 smd rect (at 1 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 9 /A4_SERCOM3.2_TC3.0) (solder_mask_margin 0.0635))
+ (pad 4 smd rect (at 0.5 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 8 /NC) (solder_mask_margin 0.0635))
+ (pad 5 smd rect (at 0 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 7 /A2_TEMP_SENSE) (solder_mask_margin 0.0635))
+ (pad 6 smd rect (at -0.5 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 6 /A1_SERCOM3.3_TC3.1) (solder_mask_margin 0.0635))
+ (pad 7 smd rect (at -1 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 5 /SDA) (solder_mask_margin 0.0635))
+ (pad 8 smd rect (at -1.5 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 4 /SCL) (solder_mask_margin 0.0635))
+ (pad 9 smd rect (at -2 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 3 /A0_TEMP_ENABLE) (solder_mask_margin 0.0635))
+ )
+
+ (module OPT3001DNPT:SON65P200X200X65-7N (layer Top) (tedit 63E6D415) (tstamp 63E73FD8)
+ (at 142.748 102.87 270)
+ (path /63E70E51)
+ (attr smd)
+ (fp_text reference U1 (at 0.54 -2.008 90) (layer F.SilkS) hide
+ (effects (font (size 0.8 0.8) (thickness 0.15)))
+ )
+ (fp_text value OPT3001DNPT (at 7.652 2.008 90) (layer F.Fab) hide
+ (effects (font (size 0.8 0.8) (thickness 0.15)))
+ )
+ (fp_line (start 1.615 -1.25) (end 1.615 1.25) (layer F.CrtYd) (width 0.05))
+ (fp_line (start -1.615 -1.25) (end -1.615 1.25) (layer F.CrtYd) (width 0.05))
+ (fp_line (start -1.615 1.25) (end 1.615 1.25) (layer F.CrtYd) (width 0.05))
+ (fp_line (start -1.615 -1.25) (end 1.615 -1.25) (layer F.CrtYd) (width 0.05))
+ (fp_line (start 1 -1) (end 1 1) (layer F.Fab) (width 0.127))
+ (fp_line (start -1 -1) (end -1 1) (layer F.Fab) (width 0.127))
+ (fp_line (start -1 1.099) (end 1 1.099) (layer F.SilkS) (width 0.127))
+ (fp_line (start -1 -1.099) (end 1 -1.099) (layer F.SilkS) (width 0.127))
+ (fp_line (start -1 1) (end 1 1) (layer F.Fab) (width 0.127))
+ (fp_line (start -1 -1) (end 1 -1) (layer F.Fab) (width 0.127))
+ (fp_circle (center -1.935 -0.65) (end -1.835 -0.65) (layer F.Fab) (width 0.2))
+ (fp_circle (center -1.935 -0.65) (end -1.835 -0.65) (layer F.SilkS) (width 0.2))
+ (fp_poly (pts (xy -0.21 -0.43) (xy 0.21 -0.43) (xy 0.21 0.43) (xy -0.21 0.43)) (layer F.Paste) (width 0.01))
+ (pad 1 smd roundrect (at -1.37 -0.65 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 2 VCC))
+ (pad 2 smd roundrect (at -1.37 0 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 1 GND))
+ (pad 3 smd roundrect (at -1.37 0.65 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 1 GND))
+ (pad 4 smd roundrect (at 1.37 0.65 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 4 /SCL))
+ (pad 5 smd roundrect (at 1.37 0 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 11 "Net-(U1-Pad5)"))
+ (pad 6 smd roundrect (at 1.37 -0.65 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 5 /SDA))
+ (pad 7 smd rect (at 0 0 270) (size 0.65 1.35) (layers Top F.Mask)
+ (net 10 "Net-(U1-Pad7)"))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:_0603MP" (layer Top) (tedit 0) (tstamp 63E759D2)
+ (at 146.2659 104.7522 270)
+ (descr "0603 MicroPitch")
+ (path /05CBC8DB)
+ (fp_text reference R1 (at -0.0635 1.7145 90) (layer F.SilkS)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_text value "1% 10K" (at -0.9525 0.9525 90) (layer F.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_line (start -0.432 0.306) (end 0.432 0.306) (layer F.Fab) (width 0.1016))
+ (fp_line (start 0.432 -0.306) (end -0.432 -0.306) (layer F.Fab) (width 0.1016))
+ (fp_line (start 0 -0.254) (end 0 0.254) (layer F.SilkS) (width 0.2032))
+ (fp_poly (pts (xy 0.4318 0.4) (xy 0.8 0.4) (xy 0.8 -0.4) (xy 0.4318 -0.4)) (layer F.Fab) (width 0))
+ (fp_poly (pts (xy -0.8 0.4) (xy -0.4318 0.4) (xy -0.4318 -0.4) (xy -0.8 -0.4)) (layer F.Fab) (width 0))
+ (fp_poly (pts (xy -0.1999 0.25) (xy 0.1999 0.25) (xy 0.1999 -0.25) (xy -0.1999 -0.25)) (layer F.Adhes) (width 0))
+ (pad 2 smd rect (at 0.762 0 270) (size 0.8 0.8) (layers Top F.Paste F.Mask)
+ (net 3 /A0_TEMP_ENABLE) (solder_mask_margin 0.0635))
+ (pad 1 smd rect (at -0.762 0 270) (size 0.8 0.8) (layers Top F.Paste F.Mask)
+ (net 7 /A2_TEMP_SENSE) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:_0603MP" (layer Top) (tedit 0) (tstamp 63E759F3)
+ (at 146.2659 102.1868 270)
+ (descr "0603 MicroPitch")
+ (path /6C7771A6)
+ (fp_text reference RT1 (at -0.127 1.778 90) (layer F.SilkS)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_text value "NTC 10K" (at -1.27 1.4923 90) (layer F.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_line (start -0.432 0.306) (end 0.432 0.306) (layer F.Fab) (width 0.1016))
+ (fp_line (start 0.432 -0.306) (end -0.432 -0.306) (layer F.Fab) (width 0.1016))
+ (fp_line (start 0 -0.254) (end 0 0.254) (layer F.SilkS) (width 0.2032))
+ (fp_poly (pts (xy 0.4318 0.4) (xy 0.8 0.4) (xy 0.8 -0.4) (xy 0.4318 -0.4)) (layer F.Fab) (width 0))
+ (fp_poly (pts (xy -0.8 0.4) (xy -0.4318 0.4) (xy -0.4318 -0.4) (xy -0.8 -0.4)) (layer F.Fab) (width 0))
+ (fp_poly (pts (xy -0.1999 0.25) (xy 0.1999 0.25) (xy 0.1999 -0.25) (xy -0.1999 -0.25)) (layer F.Adhes) (width 0))
+ (pad 2 smd rect (at 0.762 0 270) (size 0.8 0.8) (layers Top F.Paste F.Mask)
+ (net 7 /A2_TEMP_SENSE) (solder_mask_margin 0.0635))
+ (pad 1 smd rect (at -0.762 0 270) (size 0.8 0.8) (layers Top F.Paste F.Mask)
+ (net 2 VCC) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D22B)
+ (at 146.1135 102.8472 180)
+ (descr "TEST PAD")
+ (path /8E097FD0)
+ (fp_text reference GND1 (at -1.778 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 1 GND) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D232)
+ (at 144.0815 101.2216 180)
+ (descr "TEST PAD")
+ (path /E65641C5)
+ (fp_text reference A4 (at 1.524 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 9 /A4_SERCOM3.2_TC3.0) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D239)
+ (at 146.1135 101.2216 180)
+ (descr "TEST PAD")
+ (path /D0DD1D8E)
+ (fp_text reference VCC1 (at -1.778 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 2 VCC) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D240)
+ (at 146.1135 104.4728 180)
+ (descr "TEST PAD")
+ (path /252148C1)
+ (fp_text reference SCL1 (at -1.778 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 4 /SCL) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D247)
+ (at 144.0815 104.4728 180)
+ (descr "TEST PAD")
+ (path /943CED1C)
+ (fp_text reference SDA1 (at 1.778 -0.0127) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 5 /SDA) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D24E)
+ (at 144.0815 102.8472 180)
+ (descr "TEST PAD")
+ (path /E1B0505C)
+ (fp_text reference A1 (at 1.524 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 6 /A1_SERCOM3.3_TC3.1) (solder_mask_margin 0.0635))
+ )
+
+ (gr_arc (start 150.4823 106.5556) (end 150.7363 106.5556) (angle -90) (layer Edge.Cuts) (width 0.05) (tstamp 803D32C0))
+ (gr_line (start 150.4823 106.3016) (end 145.7325 106.3016) (layer Edge.Cuts) (width 0.05) (tstamp 803D3820))
+ (gr_line (start 145.0975 105.6666) (end 142.1765 105.6666) (layer Edge.Cuts) (width 0.05) (tstamp 803D3C70))
+ (gr_arc (start 142.1765 104.7776) (end 142.1765 105.6666) (angle 90) (layer Edge.Cuts) (width 0.05) (tstamp 63E7417A))
+ (gr_line (start 141.2875 104.7776) (end 141.2875 100.8406) (layer Edge.Cuts) (width 0.05) (tstamp 63E75941))
+ (gr_arc (start 142.1765 100.8406) (end 141.2875 100.8406) (angle 90) (layer Edge.Cuts) (width 0.05) (tstamp 63E74180))
+ (gr_line (start 142.1765 99.9516) (end 155.3337 99.9516) (layer Edge.Cuts) (width 0.05) (tstamp 7FB9B000))
+ (gr_arc (start 155.3337 100.3326) (end 155.3337 99.9516) (angle 90) (layer Edge.Cuts) (width 0.05) (tstamp 7FB9B410))
+ (gr_line (start 155.7147 100.3326) (end 155.7147 106.5556) (layer Edge.Cuts) (width 0.05) (tstamp 7FB9B950))
+ (gr_arc (start 145.0975 106.3016) (end 145.7325 106.3016) (angle -90) (layer Edge.Cuts) (width 0.05) (tstamp 7FB9BE00))
+ (gr_text "OPT3001 I2C\nSensor Board" (at 151.4475 100.2056) (layer F.SilkS) (tstamp 7FC58850)
+ (effects (font (size 0.57912 0.57912) (thickness 0.109728)) (justify top))
+ )
+ (gr_line (start 155.7147 105.2856) (end 147.5105 105.2856) (layer F.SilkS) (width 0.127) (tstamp 7FC58F50))
+ (gr_line (start 147.0025 100.7136) (end 147.0025 104.7776) (layer F.SilkS) (width 0.127) (tstamp 7FA4B140))
+ (gr_arc (start 147.5105 104.7776) (end 147.0025 104.7776) (angle -90) (layer F.SilkS) (width 0.127) (tstamp 7FA4B6B0))
+ (gr_arc (start 146.2405 100.7136) (end 147.0025 100.7136) (angle -90) (layer F.SilkS) (width 0.127) (tstamp 7FA4BCC0))
+ (gr_text "A0: Thermistor !EN\nA2: Temp. Sense\nA1/A4/SDA/SCL:\nTest Points" (at 151.4475 102.1106) (layer F.SilkS) (tstamp 7FA08AA0)
+ (effects (font (size 0.4826 0.4826) (thickness 0.06096)) (justify top))
+ )
+ (gr_text "Digital IO\nAnalog input\nPWM on TC3\nUART RX/TX\nInterrupt 1/0\nA4 ONLY: Ext. wake 0" (at 155.5115 101.2216) (layer B.SilkS) (tstamp 7FA09230)
+ (effects (font (size 0.4826 0.4826) (thickness 0.06096)) (justify left top mirror))
+ )
+ (gr_text "A1/A4 Uses" (at 155.5115 100.2056) (layer B.SilkS) (tstamp 7F9A10A0)
+ (effects (font (size 0.57912 0.57912) (thickness 0.109728)) (justify left top mirror))
+ )
+ (gr_text "" (at 142.9385 104.1172) (layer F.SilkS) (tstamp 7F9A17A0)
+ (effects (font (size 0.57912 0.57912) (thickness 0.073152)) (justify left))
+ )
+ (gr_text "" (at 142.9385 101.5518) (layer F.SilkS) (tstamp 7F9A1E50)
+ (effects (font (size 0.57912 0.57912) (thickness 0.073152)) (justify left))
+ )
+ (dimension 3 (width 0.12) (layer F.SilkS)
+ (gr_text "3.000 mm" (at 160.9575 108.5016 90) (layer F.SilkS)
+ (effects (font (size 1 1) (thickness 0.15)))
+ )
+ (feature1 (pts (xy 156.0875 107.0016) (xy 160.273921 107.0016)))
+ (feature2 (pts (xy 156.0875 110.0016) (xy 160.273921 110.0016)))
+ (crossbar (pts (xy 159.6875 110.0016) (xy 159.6875 107.0016)))
+ (arrow1a (pts (xy 159.6875 107.0016) (xy 160.273921 108.128104)))
+ (arrow1b (pts (xy 159.6875 107.0016) (xy 159.101079 108.128104)))
+ (arrow2a (pts (xy 159.6875 110.0016) (xy 160.273921 108.875096)))
+ (arrow2b (pts (xy 159.6875 110.0016) (xy 159.101079 108.875096)))
+ )
+ (gr_text "Stiffener only\nin this area" (at 159.5755 108.5876) (layer F.SilkS) (tstamp 7F95BC90)
+ (effects (font (size 0.57912 0.57912) (thickness 0.109728)))
+ )
+
+ (segment (start 148.082 100.457) (end 148.137 100.457) (width 0.25) (layer Bottom) (net 0))
+ (segment (start 142.098 101.5) (end 142.748 101.5) (width 0.25) (layer Top) (net 1))
+ (via (at 142.494 100.529) (size 0.8) (drill 0.4) (layers Top Bottom) (net 1))
+ (segment (start 142.748 100.783) (end 142.494 100.529) (width 0.25) (layer Top) (net 1))
+ (segment (start 142.748 101.5) (end 142.748 100.783) (width 0.25) (layer Top) (net 1))
+ (segment (start 142.494 100.529) (end 142.76899 100.25401) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 145.201089 100.256533) (end 145.201089 101.934789) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 142.76899 100.25401) (end 145.198566 100.25401) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 145.198566 100.25401) (end 145.201089 100.256533) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 145.201089 101.934789) (end 146.1135 102.8472) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 147.022102 101.938598) (end 146.1135 102.8472) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 154.7255 104.319944) (end 152.344154 101.938598) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 152.344154 101.938598) (end 147.022102 101.938598) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 154.7255 108.8056) (end 154.7255 104.319944) (width 0.25) (layer Bottom) (net 1))
+ (via (at 146.939 101.219) (size 0.8) (drill 0.4) (layers Top Bottom) (net 2))
+ (segment (start 152.129838 100.901187) (end 147.822498 100.901187) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 146.1161 101.219) (end 146.1135 101.2216) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 147.822498 100.901187) (end 147.504685 101.219) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 155.2255 103.996849) (end 152.129838 100.901187) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 146.939 101.219) (end 146.1161 101.219) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 155.2255 108.8056) (end 155.2255 103.996849) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 147.504685 101.219) (end 146.939 101.219) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 143.398 101.5) (end 143.573651 101.675651) (width 0.25) (layer Top) (net 2))
+ (segment (start 143.573651 101.675651) (end 146.142049 101.675651) (width 0.25) (layer Top) (net 2))
+ (segment (start 146.142049 101.675651) (end 146.3929 101.4248) (width 0.25) (layer Top) (net 2))
+ (segment (start 146.3929 101.4248) (end 146.7332 101.4248) (width 0.25) (layer Top) (net 2))
+ (segment (start 146.7332 101.4248) (end 146.939 101.219) (width 0.25) (layer Top) (net 2))
+ (via (at 147.828 105.664) (size 0.8) (drill 0.4) (layers Top Bottom) (net 3))
+ (segment (start 147.6782 105.5142) (end 147.828 105.664) (width 0.25) (layer Top) (net 3))
+ (segment (start 146.3929 105.5142) (end 147.6782 105.5142) (width 0.25) (layer Top) (net 3))
+ (segment (start 151.2255 106.512573) (end 151.2255 108.8056) (width 0.25) (layer Bottom) (net 3))
+ (segment (start 150.712117 105.99919) (end 151.2255 106.512573) (width 0.25) (layer Bottom) (net 3))
+ (segment (start 148.16319 105.99919) (end 150.712117 105.99919) (width 0.25) (layer Bottom) (net 3))
+ (segment (start 147.828 105.664) (end 148.16319 105.99919) (width 0.25) (layer Bottom) (net 3))
+ (segment (start 142.37541 105.26741) (end 145.075078 105.26741) (width 0.25) (layer Top) (net 4))
+ (via (at 145.243931 105.098557) (size 0.8) (drill 0.4) (layers Top Bottom) (net 4))
+ (segment (start 142.098 104.99) (end 142.37541 105.26741) (width 0.25) (layer Top) (net 4))
+ (segment (start 142.098 104.24) (end 142.098 104.99) (width 0.25) (layer Top) (net 4))
+ (segment (start 145.075078 105.26741) (end 145.243931 105.098557) (width 0.25) (layer Top) (net 4))
+ (segment (start 145.487743 105.098557) (end 146.1135 104.4728) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 145.243931 105.098557) (end 145.487743 105.098557) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 150.674396 105.392376) (end 151.7255 106.44348) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 150.421816 105.392376) (end 150.674396 105.392376) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 146.1135 104.4728) (end 149.50224 104.4728) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 151.7255 106.44348) (end 151.7255 108.8056) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 149.50224 104.4728) (end 150.421816 105.392376) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 143.398 104.24) (end 145.007 104.24) (width 0.25) (layer Top) (net 5))
+ (segment (start 145.007 104.24) (end 145.381654 104.24) (width 0.25) (layer Top) (net 5))
+ (segment (start 152.2255 108.8056) (end 152.2255 106.193504) (width 0.25) (layer Bottom) (net 5))
+ (segment (start 145.381654 104.24) (end 145.856628 104.714974) (width 0.25) (layer Top) (net 5))
+ (segment (start 145.856628 104.714974) (end 150.74697 104.714974) (width 0.25) (layer Top) (net 5))
+ (via (at 150.74697 104.714974) (size 0.8) (drill 0.4) (layers Top Bottom) (net 5))
+ (segment (start 152.2255 106.193504) (end 150.74697 104.714974) (width 0.25) (layer Bottom) (net 5))
+ (via (at 145.034 103.886) (size 0.8) (drill 0.4) (layers Top Bottom) (net 5))
+ (segment (start 145.034 103.886) (end 145.007 103.913) (width 0.25) (layer Top) (net 5))
+ (segment (start 144.4472 104.4728) (end 145.034 103.886) (width 0.25) (layer Bottom) (net 5))
+ (segment (start 144.0815 104.4728) (end 144.4472 104.4728) (width 0.25) (layer Bottom) (net 5))
+ (segment (start 145.007 103.913) (end 145.007 104.24) (width 0.25) (layer Top) (net 5))
+ (via (at 148.336 103.52461) (size 0.8) (drill 0.4) (layers Top Bottom) (net 6))
+ (segment (start 152.7255 106.045058) (end 151.490164 104.809722) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 151.490164 104.35972) (end 150.655054 103.52461) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 152.7255 108.8056) (end 152.7255 106.045058) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 151.490164 104.809722) (end 151.490164 104.35972) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 150.655054 103.52461) (end 148.336 103.52461) (width 0.25) (layer Bottom) (net 6))
+ (via (at 144.899309 102.94316) (size 0.8) (drill 0.4) (layers Top Bottom) (net 6))
+ (segment (start 144.803349 102.8472) (end 144.899309 102.94316) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 144.0815 102.8472) (end 144.803349 102.8472) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 144.899309 102.94316) (end 145.57107 102.271399) (width 0.25) (layer Top) (net 6))
+ (segment (start 145.57107 102.271399) (end 147.082789 102.271399) (width 0.25) (layer Top) (net 6))
+ (segment (start 147.082789 102.271399) (end 148.336 103.52461) (width 0.25) (layer Top) (net 6))
+ (segment (start 146.2659 103.9902) (end 146.2659 102.9488) (width 0.254) (layer Top) (net 7) (tstamp 63E759C5))
+ (segment (start 153.2255 105.415504) (end 152.231065 104.421069) (width 0.25) (layer Bottom) (net 7))
+ (segment (start 148.842094 104.02107) (end 151.831066 104.02107) (width 0.25) (layer Top) (net 7))
+ (via (at 152.231065 104.421069) (size 0.8) (drill 0.4) (layers Top Bottom) (net 7))
+ (segment (start 148.661153 104.202011) (end 148.842094 104.02107) (width 0.25) (layer Top) (net 7))
+ (segment (start 146.3929 103.9902) (end 146.604711 104.202011) (width 0.25) (layer Top) (net 7))
+ (segment (start 146.604711 104.202011) (end 148.661153 104.202011) (width 0.25) (layer Top) (net 7))
+ (segment (start 151.831066 104.02107) (end 152.231065 104.421069) (width 0.25) (layer Top) (net 7))
+ (segment (start 153.2255 108.8056) (end 153.2255 105.415504) (width 0.25) (layer Bottom) (net 7))
+ (via (at 152.019 102.616) (size 0.8) (drill 0.4) (layers Top Bottom) (net 9))
+ (segment (start 154.2255 104.8225) (end 152.019 102.616) (width 0.25) (layer Bottom) (net 9))
+ (segment (start 154.2255 108.8056) (end 154.2255 104.8225) (width 0.25) (layer Bottom) (net 9))
+ (segment (start 149.733 100.33) (end 147.574 100.33) (width 0.25) (layer Top) (net 9))
+ (segment (start 152.019 102.616) (end 149.733 100.33) (width 0.25) (layer Top) (net 9))
+ (segment (start 147.574 100.33) (end 147.955 100.33) (width 0.25) (layer Top) (net 9))
+ (via (at 144.523679 100.981868) (size 0.8) (drill 0.4) (layers Top Bottom) (net 9))
+ (segment (start 144.0815 101.2216) (end 144.283947 101.2216) (width 0.25) (layer Bottom) (net 9))
+ (segment (start 144.283947 101.2216) (end 144.523679 100.981868) (width 0.25) (layer Bottom) (net 9))
+ (segment (start 144.523679 100.981868) (end 145.175547 100.33) (width 0.25) (layer Top) (net 9))
+ (segment (start 145.175547 100.33) (end 147.574 100.33) (width 0.25) (layer Top) (net 9))
+
+)
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.kicad_pcb-bak b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.kicad_pcb-bak
new file mode 100644
index 00000000..bff4000c
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.kicad_pcb-bak
@@ -0,0 +1,497 @@
+(kicad_pcb (version 20171130) (host pcbnew 5.1.9+dfsg1-1+deb11u1)
+
+ (general
+ (thickness 1.6)
+ (drawings 22)
+ (tracks 90)
+ (zones 0)
+ (modules 10)
+ (nets 12)
+ )
+
+ (page A4)
+ (layers
+ (0 Top signal)
+ (31 Bottom signal)
+ (32 B.Adhes user hide)
+ (33 F.Adhes user hide)
+ (34 B.Paste user hide)
+ (35 F.Paste user hide)
+ (36 B.SilkS user hide)
+ (37 F.SilkS user)
+ (38 B.Mask user hide)
+ (39 F.Mask user hide)
+ (40 Dwgs.User user hide)
+ (41 Cmts.User user hide)
+ (42 Eco1.User user hide)
+ (43 Eco2.User user)
+ (44 Edge.Cuts user)
+ (45 Margin user hide)
+ (46 B.CrtYd user hide)
+ (47 F.CrtYd user hide)
+ (48 B.Fab user hide)
+ (49 F.Fab user hide)
+ )
+
+ (setup
+ (last_trace_width 0.25)
+ (trace_clearance 0.1524)
+ (zone_clearance 0.508)
+ (zone_45_only no)
+ (trace_min 0.2)
+ (via_size 0.8)
+ (via_drill 0.4)
+ (via_min_size 0.1)
+ (via_min_drill 0.254)
+ (uvia_size 0.3)
+ (uvia_drill 0.1)
+ (uvias_allowed no)
+ (uvia_min_size 0.2)
+ (uvia_min_drill 0.1)
+ (edge_width 0.05)
+ (segment_width 0.2)
+ (pcb_text_width 0.3)
+ (pcb_text_size 1.5 1.5)
+ (mod_edge_width 0.12)
+ (mod_text_size 1 1)
+ (mod_text_width 0.15)
+ (pad_size 1.5 0.27)
+ (pad_drill 0)
+ (pad_to_mask_clearance 0)
+ (aux_axis_origin 0 0)
+ (visible_elements FFFFFF7F)
+ (pcbplotparams
+ (layerselection 0x010fc_ffffffff)
+ (usegerberextensions false)
+ (usegerberattributes true)
+ (usegerberadvancedattributes true)
+ (creategerberjobfile true)
+ (excludeedgelayer true)
+ (linewidth 0.100000)
+ (plotframeref false)
+ (viasonmask false)
+ (mode 1)
+ (useauxorigin false)
+ (hpglpennumber 1)
+ (hpglpenspeed 20)
+ (hpglpendiameter 15.000000)
+ (psnegative false)
+ (psa4output false)
+ (plotreference true)
+ (plotvalue true)
+ (plotinvisibletext false)
+ (padsonsilk false)
+ (subtractmaskfromsilk false)
+ (outputformat 1)
+ (mirror false)
+ (drillshape 1)
+ (scaleselection 1)
+ (outputdirectory ""))
+ )
+
+ (net 0 "")
+ (net 1 GND)
+ (net 2 VCC)
+ (net 3 /A0_TEMP_ENABLE)
+ (net 4 /SCL)
+ (net 5 /SDA)
+ (net 6 /A1_SERCOM3.3_TC3.1)
+ (net 7 /A2_TEMP_SENSE)
+ (net 8 /NC)
+ (net 9 /A4_SERCOM3.2_TC3.0)
+ (net 10 "Net-(U1-Pad7)")
+ (net 11 "Net-(U1-Pad5)")
+
+ (net_class Default "This is the default net class."
+ (clearance 0.1524)
+ (trace_width 0.25)
+ (via_dia 0.8)
+ (via_drill 0.4)
+ (uvia_dia 0.3)
+ (uvia_drill 0.1)
+ (add_net /A0_TEMP_ENABLE)
+ (add_net /A1_SERCOM3.3_TC3.1)
+ (add_net /A2_TEMP_SENSE)
+ (add_net /A4_SERCOM3.2_TC3.0)
+ (add_net /NC)
+ (add_net /SCL)
+ (add_net /SDA)
+ (add_net GND)
+ (add_net "Net-(U1-Pad5)")
+ (add_net "Net-(U1-Pad7)")
+ (add_net VCC)
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:FH19C9S05SH10-FFC" (layer Top) (tedit 0) (tstamp 63E6D203)
+ (at 153.2255 106.5556)
+ (descr "FH19C-9S-0.5SH(10)-1
\n")
+ (path /54307519)
+ (fp_text reference U$3 (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ )
+ (fp_text value FH19C-9S-0.5SH_10-FFC (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ )
+ (fp_arc (start -2.3392 3.35) (end -2.3392 3.5) (angle 90) (layer Edge.Cuts) (width 0.05))
+ (fp_arc (start 2.3392 3.35) (end 2.3392 3.5) (angle -90) (layer Edge.Cuts) (width 0.05))
+ (fp_line (start -2.4892 0) (end -2.4892 3.35) (layer Edge.Cuts) (width 0.05))
+ (fp_line (start -2.3392 3.5) (end 2.3392 3.5) (layer Edge.Cuts) (width 0.05))
+ (fp_line (start 2.4892 0) (end 2.4892 3.35) (layer Edge.Cuts) (width 0.05))
+ (fp_poly (pts (xy -2.4902 0.9742) (xy 2.4882 0.9742) (xy 2.4892 3.4798) (xy -2.4892 3.4798)) (layer B.Mask) (width 0))
+ (pad 1 smd rect (at 2 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 2 VCC) (solder_mask_margin 0.0635))
+ (pad 2 smd rect (at 1.5 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 1 GND) (solder_mask_margin 0.0635))
+ (pad 3 smd rect (at 1 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 9 /A4_SERCOM3.2_TC3.0) (solder_mask_margin 0.0635))
+ (pad 4 smd rect (at 0.5 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 8 /NC) (solder_mask_margin 0.0635))
+ (pad 5 smd rect (at 0 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 7 /A2_TEMP_SENSE) (solder_mask_margin 0.0635))
+ (pad 6 smd rect (at -0.5 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 6 /A1_SERCOM3.3_TC3.1) (solder_mask_margin 0.0635))
+ (pad 7 smd rect (at -1 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 5 /SDA) (solder_mask_margin 0.0635))
+ (pad 8 smd rect (at -1.5 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 4 /SCL) (solder_mask_margin 0.0635))
+ (pad 9 smd rect (at -2 2.25 90) (size 2.5 0.347) (layers Bottom B.Mask)
+ (net 3 /A0_TEMP_ENABLE) (solder_mask_margin 0.0635))
+ )
+
+ (module OPT3001DNPT:SON65P200X200X65-7N (layer Top) (tedit 63E6D415) (tstamp 63E73FD8)
+ (at 142.748 102.87 270)
+ (path /63E70E51)
+ (attr smd)
+ (fp_text reference U1 (at 0.54 -2.008 90) (layer F.SilkS) hide
+ (effects (font (size 0.8 0.8) (thickness 0.15)))
+ )
+ (fp_text value OPT3001DNPT (at 7.652 2.008 90) (layer F.Fab) hide
+ (effects (font (size 0.8 0.8) (thickness 0.15)))
+ )
+ (fp_poly (pts (xy -0.21 -0.43) (xy 0.21 -0.43) (xy 0.21 0.43) (xy -0.21 0.43)) (layer F.Paste) (width 0.01))
+ (fp_circle (center -1.935 -0.65) (end -1.835 -0.65) (layer F.SilkS) (width 0.2))
+ (fp_circle (center -1.935 -0.65) (end -1.835 -0.65) (layer F.Fab) (width 0.2))
+ (fp_line (start -1 -1) (end 1 -1) (layer F.Fab) (width 0.127))
+ (fp_line (start -1 1) (end 1 1) (layer F.Fab) (width 0.127))
+ (fp_line (start -1 -1.099) (end 1 -1.099) (layer F.SilkS) (width 0.127))
+ (fp_line (start -1 1.099) (end 1 1.099) (layer F.SilkS) (width 0.127))
+ (fp_line (start -1 -1) (end -1 1) (layer F.Fab) (width 0.127))
+ (fp_line (start 1 -1) (end 1 1) (layer F.Fab) (width 0.127))
+ (fp_line (start -1.615 -1.25) (end 1.615 -1.25) (layer F.CrtYd) (width 0.05))
+ (fp_line (start -1.615 1.25) (end 1.615 1.25) (layer F.CrtYd) (width 0.05))
+ (fp_line (start -1.615 -1.25) (end -1.615 1.25) (layer F.CrtYd) (width 0.05))
+ (fp_line (start 1.615 -1.25) (end 1.615 1.25) (layer F.CrtYd) (width 0.05))
+ (pad 1 smd roundrect (at -1.37 -0.65 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 2 VCC))
+ (pad 2 smd roundrect (at -1.37 0 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 1 GND))
+ (pad 3 smd roundrect (at -1.37 0.65 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 1 GND))
+ (pad 4 smd roundrect (at 1.37 0.65 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 4 /SCL))
+ (pad 5 smd roundrect (at 1.37 0 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 11 "Net-(U1-Pad5)"))
+ (pad 6 smd roundrect (at 1.37 -0.65 270) (size 1.5 0.27) (layers Top F.Paste F.Mask) (roundrect_rratio 0.03)
+ (net 5 /SDA))
+ (pad 7 smd rect (at 0 0 270) (size 0.65 1.35) (layers Top F.Mask)
+ (net 10 "Net-(U1-Pad7)"))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:_0603MP" (layer Top) (tedit 0) (tstamp 63E759D2)
+ (at 146.2659 104.7522 270)
+ (descr "0603 MicroPitch")
+ (path /05CBC8DB)
+ (fp_text reference R1 (at -0.0635 1.7145 90) (layer F.SilkS)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_text value "1% 10K" (at -0.9525 0.9525 90) (layer F.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_poly (pts (xy -0.1999 0.25) (xy 0.1999 0.25) (xy 0.1999 -0.25) (xy -0.1999 -0.25)) (layer F.Adhes) (width 0))
+ (fp_poly (pts (xy -0.8 0.4) (xy -0.4318 0.4) (xy -0.4318 -0.4) (xy -0.8 -0.4)) (layer F.Fab) (width 0))
+ (fp_poly (pts (xy 0.4318 0.4) (xy 0.8 0.4) (xy 0.8 -0.4) (xy 0.4318 -0.4)) (layer F.Fab) (width 0))
+ (fp_line (start 0 -0.254) (end 0 0.254) (layer F.SilkS) (width 0.2032))
+ (fp_line (start 0.432 -0.306) (end -0.432 -0.306) (layer F.Fab) (width 0.1016))
+ (fp_line (start -0.432 0.306) (end 0.432 0.306) (layer F.Fab) (width 0.1016))
+ (pad 2 smd rect (at 0.762 0 270) (size 0.8 0.8) (layers Top F.Paste F.Mask)
+ (net 3 /A0_TEMP_ENABLE) (solder_mask_margin 0.0635))
+ (pad 1 smd rect (at -0.762 0 270) (size 0.8 0.8) (layers Top F.Paste F.Mask)
+ (net 7 /A2_TEMP_SENSE) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:_0603MP" (layer Top) (tedit 0) (tstamp 63E759F3)
+ (at 146.2659 102.1868 270)
+ (descr "0603 MicroPitch")
+ (path /6C7771A6)
+ (fp_text reference RT1 (at -0.127 1.778 90) (layer F.SilkS)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_text value "NTC 10K" (at -1.27 1.4923 90) (layer F.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_poly (pts (xy -0.1999 0.25) (xy 0.1999 0.25) (xy 0.1999 -0.25) (xy -0.1999 -0.25)) (layer F.Adhes) (width 0))
+ (fp_poly (pts (xy -0.8 0.4) (xy -0.4318 0.4) (xy -0.4318 -0.4) (xy -0.8 -0.4)) (layer F.Fab) (width 0))
+ (fp_poly (pts (xy 0.4318 0.4) (xy 0.8 0.4) (xy 0.8 -0.4) (xy 0.4318 -0.4)) (layer F.Fab) (width 0))
+ (fp_line (start 0 -0.254) (end 0 0.254) (layer F.SilkS) (width 0.2032))
+ (fp_line (start 0.432 -0.306) (end -0.432 -0.306) (layer F.Fab) (width 0.1016))
+ (fp_line (start -0.432 0.306) (end 0.432 0.306) (layer F.Fab) (width 0.1016))
+ (pad 2 smd rect (at 0.762 0 270) (size 0.8 0.8) (layers Top F.Paste F.Mask)
+ (net 7 /A2_TEMP_SENSE) (solder_mask_margin 0.0635))
+ (pad 1 smd rect (at -0.762 0 270) (size 0.8 0.8) (layers Top F.Paste F.Mask)
+ (net 2 VCC) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D22B)
+ (at 146.1135 102.8472 180)
+ (descr "TEST PAD")
+ (path /8E097FD0)
+ (fp_text reference GND1 (at -1.778 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 1 GND) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D232)
+ (at 144.0815 101.2216 180)
+ (descr "TEST PAD")
+ (path /E65641C5)
+ (fp_text reference A4 (at 1.524 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 9 /A4_SERCOM3.2_TC3.0) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D239)
+ (at 146.1135 101.2216 180)
+ (descr "TEST PAD")
+ (path /D0DD1D8E)
+ (fp_text reference VCC1 (at -1.778 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 2 VCC) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D240)
+ (at 146.1135 104.4728 180)
+ (descr "TEST PAD")
+ (path /252148C1)
+ (fp_text reference SCL1 (at -1.778 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 4 /SCL) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D247)
+ (at 144.0815 104.4728 180)
+ (descr "TEST PAD")
+ (path /943CED1C)
+ (fp_text reference SDA1 (at 1.778 -0.0127) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 5 /SDA) (solder_mask_margin 0.0635))
+ )
+
+ (module "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" (layer Bottom) (tedit 0) (tstamp 63E6D24E)
+ (at 144.0815 102.8472 180)
+ (descr "TEST PAD")
+ (path /E1B0505C)
+ (fp_text reference A1 (at 1.524 0) (layer B.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify right bottom mirror))
+ )
+ (fp_text value TPB1,27 (at -0.635 -0.762) (layer B.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify right bottom mirror))
+ )
+ (fp_line (start 0 0.635) (end 0 -0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 -1.905 180) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify right bottom))
+ )
+ (pad TP smd roundrect (at 0 0 180) (size 1.27 1.27) (layers Bottom B.Mask) (roundrect_rratio 0.5)
+ (net 6 /A1_SERCOM3.3_TC3.1) (solder_mask_margin 0.0635))
+ )
+
+ (gr_arc (start 150.4823 106.5556) (end 150.7363 106.5556) (angle -90) (layer Edge.Cuts) (width 0.05) (tstamp 803D32C0))
+ (gr_line (start 150.4823 106.3016) (end 145.7325 106.3016) (layer Edge.Cuts) (width 0.05) (tstamp 803D3820))
+ (gr_line (start 145.0975 105.6666) (end 142.1765 105.6666) (layer Edge.Cuts) (width 0.05) (tstamp 803D3C70))
+ (gr_arc (start 142.1765 104.7776) (end 142.1765 105.6666) (angle 90) (layer Edge.Cuts) (width 0.05) (tstamp 63E7417A))
+ (gr_line (start 141.2875 104.7776) (end 141.2875 100.8406) (layer Edge.Cuts) (width 0.05) (tstamp 63E75941))
+ (gr_arc (start 142.1765 100.8406) (end 141.2875 100.8406) (angle 90) (layer Edge.Cuts) (width 0.05) (tstamp 63E74180))
+ (gr_line (start 142.1765 99.9516) (end 155.3337 99.9516) (layer Edge.Cuts) (width 0.05) (tstamp 7FB9B000))
+ (gr_arc (start 155.3337 100.3326) (end 155.3337 99.9516) (angle 90) (layer Edge.Cuts) (width 0.05) (tstamp 7FB9B410))
+ (gr_line (start 155.7147 100.3326) (end 155.7147 106.5556) (layer Edge.Cuts) (width 0.05) (tstamp 7FB9B950))
+ (gr_arc (start 145.0975 106.3016) (end 145.7325 106.3016) (angle -90) (layer Edge.Cuts) (width 0.05) (tstamp 7FB9BE00))
+ (gr_text "OPT3001 I2C\nSensor Board" (at 151.4475 100.2056) (layer F.SilkS) (tstamp 7FC58850)
+ (effects (font (size 0.57912 0.57912) (thickness 0.109728)) (justify top))
+ )
+ (gr_line (start 155.7147 105.2856) (end 147.5105 105.2856) (layer F.SilkS) (width 0.127) (tstamp 7FC58F50))
+ (gr_line (start 147.0025 100.7136) (end 147.0025 104.7776) (layer F.SilkS) (width 0.127) (tstamp 7FA4B140))
+ (gr_arc (start 147.5105 104.7776) (end 147.0025 104.7776) (angle -90) (layer F.SilkS) (width 0.127) (tstamp 7FA4B6B0))
+ (gr_arc (start 146.2405 100.7136) (end 147.0025 100.7136) (angle -90) (layer F.SilkS) (width 0.127) (tstamp 7FA4BCC0))
+ (gr_text "A0: Thermistor !EN\nA2: Temp. Sense\nA1/A4/SDA/SCL:\nTest Points" (at 151.4475 102.1106) (layer F.SilkS) (tstamp 7FA08AA0)
+ (effects (font (size 0.4826 0.4826) (thickness 0.06096)) (justify top))
+ )
+ (gr_text "Digital IO\nAnalog input\nPWM on TC3\nUART RX/TX\nInterrupt 1/0\nA4 ONLY: Ext. wake 0" (at 155.5115 101.2216) (layer B.SilkS) (tstamp 7FA09230)
+ (effects (font (size 0.4826 0.4826) (thickness 0.06096)) (justify left top mirror))
+ )
+ (gr_text "A1/A4 Uses" (at 155.5115 100.2056) (layer B.SilkS) (tstamp 7F9A10A0)
+ (effects (font (size 0.57912 0.57912) (thickness 0.109728)) (justify left top mirror))
+ )
+ (gr_text "" (at 142.9385 104.1172) (layer F.SilkS) (tstamp 7F9A17A0)
+ (effects (font (size 0.57912 0.57912) (thickness 0.073152)) (justify left))
+ )
+ (gr_text "" (at 142.9385 101.5518) (layer F.SilkS) (tstamp 7F9A1E50)
+ (effects (font (size 0.57912 0.57912) (thickness 0.073152)) (justify left))
+ )
+ (dimension 3 (width 0.12) (layer F.SilkS)
+ (gr_text "3.000 mm" (at 160.9575 108.5016 90) (layer F.SilkS)
+ (effects (font (size 1 1) (thickness 0.15)))
+ )
+ (feature1 (pts (xy 156.0875 107.0016) (xy 160.273921 107.0016)))
+ (feature2 (pts (xy 156.0875 110.0016) (xy 160.273921 110.0016)))
+ (crossbar (pts (xy 159.6875 110.0016) (xy 159.6875 107.0016)))
+ (arrow1a (pts (xy 159.6875 107.0016) (xy 160.273921 108.128104)))
+ (arrow1b (pts (xy 159.6875 107.0016) (xy 159.101079 108.128104)))
+ (arrow2a (pts (xy 159.6875 110.0016) (xy 160.273921 108.875096)))
+ (arrow2b (pts (xy 159.6875 110.0016) (xy 159.101079 108.875096)))
+ )
+ (gr_text "Stiffener only\nin this area" (at 159.5755 108.5876) (layer F.SilkS) (tstamp 7F95BC90)
+ (effects (font (size 0.57912 0.57912) (thickness 0.109728)))
+ )
+
+ (segment (start 148.082 100.457) (end 148.137 100.457) (width 0.25) (layer Bottom) (net 0))
+ (segment (start 142.098 101.5) (end 142.748 101.5) (width 0.25) (layer Top) (net 1))
+ (via (at 142.494 100.529) (size 0.8) (drill 0.4) (layers Top Bottom) (net 1))
+ (segment (start 142.748 100.783) (end 142.494 100.529) (width 0.25) (layer Top) (net 1))
+ (segment (start 142.748 101.5) (end 142.748 100.783) (width 0.25) (layer Top) (net 1))
+ (segment (start 142.494 100.529) (end 142.76899 100.25401) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 145.201089 100.256533) (end 145.201089 101.934789) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 142.76899 100.25401) (end 145.198566 100.25401) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 145.198566 100.25401) (end 145.201089 100.256533) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 145.201089 101.934789) (end 146.1135 102.8472) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 147.022102 101.938598) (end 146.1135 102.8472) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 154.7255 104.319944) (end 152.344154 101.938598) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 152.344154 101.938598) (end 147.022102 101.938598) (width 0.25) (layer Bottom) (net 1))
+ (segment (start 154.7255 108.8056) (end 154.7255 104.319944) (width 0.25) (layer Bottom) (net 1))
+ (via (at 146.939 101.219) (size 0.8) (drill 0.4) (layers Top Bottom) (net 2) (status 1000000))
+ (segment (start 152.129838 100.901187) (end 147.822498 100.901187) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 146.7332 101.4248) (end 146.939 101.219) (width 0.25) (layer Top) (net 2))
+ (segment (start 146.3929 101.4248) (end 146.7332 101.4248) (width 0.25) (layer Top) (net 2))
+ (segment (start 146.142049 101.675651) (end 146.3929 101.4248) (width 0.25) (layer Top) (net 2))
+ (segment (start 143.573651 101.675651) (end 146.142049 101.675651) (width 0.25) (layer Top) (net 2))
+ (segment (start 146.1161 101.219) (end 146.1135 101.2216) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 147.822498 100.901187) (end 147.504685 101.219) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 155.2255 103.996849) (end 152.129838 100.901187) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 143.398 101.5) (end 143.573651 101.675651) (width 0.25) (layer Top) (net 2))
+ (segment (start 146.939 101.219) (end 146.1161 101.219) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 155.2255 108.8056) (end 155.2255 103.996849) (width 0.25) (layer Bottom) (net 2))
+ (segment (start 147.504685 101.219) (end 146.939 101.219) (width 0.25) (layer Bottom) (net 2))
+ (via (at 147.828 105.664) (size 0.8) (drill 0.4) (layers Top Bottom) (net 3))
+ (segment (start 147.6782 105.5142) (end 147.828 105.664) (width 0.25) (layer Top) (net 3))
+ (segment (start 146.3929 105.5142) (end 147.6782 105.5142) (width 0.25) (layer Top) (net 3))
+ (segment (start 151.2255 106.512573) (end 151.2255 108.8056) (width 0.25) (layer Bottom) (net 3))
+ (segment (start 150.712117 105.99919) (end 151.2255 106.512573) (width 0.25) (layer Bottom) (net 3))
+ (segment (start 148.16319 105.99919) (end 150.712117 105.99919) (width 0.25) (layer Bottom) (net 3))
+ (segment (start 147.828 105.664) (end 148.16319 105.99919) (width 0.25) (layer Bottom) (net 3))
+ (segment (start 142.37541 105.26741) (end 145.075078 105.26741) (width 0.25) (layer Top) (net 4))
+ (via (at 145.243931 105.098557) (size 0.8) (drill 0.4) (layers Top Bottom) (net 4))
+ (segment (start 142.098 104.99) (end 142.37541 105.26741) (width 0.25) (layer Top) (net 4))
+ (segment (start 142.098 104.24) (end 142.098 104.99) (width 0.25) (layer Top) (net 4))
+ (segment (start 145.075078 105.26741) (end 145.243931 105.098557) (width 0.25) (layer Top) (net 4))
+ (segment (start 145.487743 105.098557) (end 146.1135 104.4728) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 145.243931 105.098557) (end 145.487743 105.098557) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 150.674396 105.392376) (end 151.7255 106.44348) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 150.421816 105.392376) (end 150.674396 105.392376) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 146.1135 104.4728) (end 149.50224 104.4728) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 151.7255 106.44348) (end 151.7255 108.8056) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 149.50224 104.4728) (end 150.421816 105.392376) (width 0.25) (layer Bottom) (net 4))
+ (segment (start 144.0815 104.4728) (end 144.4472 104.4728) (width 0.25) (layer Bottom) (net 5))
+ (segment (start 144.4472 104.4728) (end 145.034 103.886) (width 0.25) (layer Bottom) (net 5))
+ (via (at 145.034 103.886) (size 0.8) (drill 0.4) (layers Top Bottom) (net 5))
+ (segment (start 145.034 103.886) (end 145.007 103.913) (width 0.25) (layer Top) (net 5))
+ (segment (start 145.007 103.913) (end 145.007 104.24) (width 0.25) (layer Top) (net 5))
+ (segment (start 143.398 104.24) (end 145.007 104.24) (width 0.25) (layer Top) (net 5))
+ (segment (start 145.007 104.24) (end 145.381654 104.24) (width 0.25) (layer Top) (net 5))
+ (segment (start 152.2255 108.8056) (end 152.2255 106.193504) (width 0.25) (layer Bottom) (net 5))
+ (segment (start 145.381654 104.24) (end 145.856628 104.714974) (width 0.25) (layer Top) (net 5))
+ (segment (start 145.856628 104.714974) (end 150.74697 104.714974) (width 0.25) (layer Top) (net 5))
+ (via (at 150.74697 104.714974) (size 0.8) (drill 0.4) (layers Top Bottom) (net 5))
+ (segment (start 152.2255 106.193504) (end 150.74697 104.714974) (width 0.25) (layer Bottom) (net 5))
+ (segment (start 144.0815 102.8472) (end 144.803349 102.8472) (width 0.25) (layer Bottom) (net 6))
+ (via (at 144.899309 102.94316) (size 0.8) (drill 0.4) (layers Top Bottom) (net 6))
+ (segment (start 144.803349 102.8472) (end 144.899309 102.94316) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 144.899309 102.94316) (end 145.57107 102.271399) (width 0.25) (layer Top) (net 6))
+ (via (at 148.336 103.52461) (size 0.8) (drill 0.4) (layers Top Bottom) (net 6))
+ (segment (start 147.082789 102.271399) (end 148.336 103.52461) (width 0.25) (layer Top) (net 6))
+ (segment (start 145.57107 102.271399) (end 147.082789 102.271399) (width 0.25) (layer Top) (net 6))
+ (segment (start 152.7255 106.045058) (end 151.490164 104.809722) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 151.490164 104.35972) (end 150.655054 103.52461) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 152.7255 108.8056) (end 152.7255 106.045058) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 151.490164 104.809722) (end 151.490164 104.35972) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 150.655054 103.52461) (end 148.336 103.52461) (width 0.25) (layer Bottom) (net 6))
+ (segment (start 146.2659 103.9902) (end 146.2659 102.9488) (width 0.254) (layer Top) (net 7) (tstamp 63E759C5))
+ (segment (start 153.2255 105.415504) (end 152.231065 104.421069) (width 0.25) (layer Bottom) (net 7))
+ (segment (start 148.842094 104.02107) (end 151.831066 104.02107) (width 0.25) (layer Top) (net 7))
+ (via (at 152.231065 104.421069) (size 0.8) (drill 0.4) (layers Top Bottom) (net 7))
+ (segment (start 148.661153 104.202011) (end 148.842094 104.02107) (width 0.25) (layer Top) (net 7))
+ (segment (start 146.3929 103.9902) (end 146.604711 104.202011) (width 0.25) (layer Top) (net 7))
+ (segment (start 146.604711 104.202011) (end 148.661153 104.202011) (width 0.25) (layer Top) (net 7))
+ (segment (start 151.831066 104.02107) (end 152.231065 104.421069) (width 0.25) (layer Top) (net 7))
+ (segment (start 153.2255 108.8056) (end 153.2255 105.415504) (width 0.25) (layer Bottom) (net 7))
+ (segment (start 144.0815 101.2216) (end 144.283947 101.2216) (width 0.25) (layer Bottom) (net 9))
+ (via (at 144.523679 100.981868) (size 0.8) (drill 0.4) (layers Top Bottom) (net 9))
+ (segment (start 144.283947 101.2216) (end 144.523679 100.981868) (width 0.25) (layer Bottom) (net 9))
+ (segment (start 144.523679 100.981868) (end 145.175547 100.33) (width 0.25) (layer Top) (net 9))
+ (via (at 152.019 102.616) (size 0.8) (drill 0.4) (layers Top Bottom) (net 9))
+ (segment (start 154.2255 104.8225) (end 152.019 102.616) (width 0.25) (layer Bottom) (net 9))
+ (segment (start 154.2255 108.8056) (end 154.2255 104.8225) (width 0.25) (layer Bottom) (net 9))
+ (segment (start 149.733 100.33) (end 147.574 100.33) (width 0.25) (layer Top) (net 9))
+ (segment (start 152.019 102.616) (end 149.733 100.33) (width 0.25) (layer Top) (net 9))
+ (segment (start 147.574 100.33) (end 147.955 100.33) (width 0.25) (layer Top) (net 9))
+ (segment (start 145.175547 100.33) (end 147.574 100.33) (width 0.25) (layer Top) (net 9))
+
+)
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/B1,27.kicad_mod b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/B1,27.kicad_mod
new file mode 100644
index 00000000..448fc3c1
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/B1,27.kicad_mod
@@ -0,0 +1,16 @@
+(module B1,27 (layer F.Cu) (tedit 0)
+ (descr "TEST PAD")
+ (fp_text reference A1 (at 1.524 0 -180) (layer F.SilkS)
+ (effects (font (size 0.7239 0.7239) (thickness 0.130302)) (justify left bottom))
+ )
+ (fp_text value TPB1,27 (at -0.635 0.762 -180) (layer F.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_line (start -0.635 0) (end 0.635 0) (layer Dwgs.User) (width 0.0024))
+ (fp_line (start 0 -0.635) (end 0 0.635) (layer Dwgs.User) (width 0.0024))
+ (fp_text user >TP_SIGNAL_NAME (at -0.635 1.905) (layer Dwgs.User)
+ (effects (font (size 0.95 0.95) (thickness 0.08)) (justify left bottom))
+ )
+ (pad TP smd roundrect (at 0 0) (size 1.27 1.27) (layers F.Cu F.Mask) (roundrect_rratio 0.5)
+ (solder_mask_margin 0.0635))
+)
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/FH19C9S05SH10-FFC.kicad_mod b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/FH19C9S05SH10-FFC.kicad_mod
new file mode 100644
index 00000000..61ebf5f7
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/FH19C9S05SH10-FFC.kicad_mod
@@ -0,0 +1,33 @@
+(module FH19C9S05SH10-FFC (layer F.Cu) (tedit 0)
+ (descr "FH19C-9S-0.5SH(10)-1
\n")
+ (fp_text reference U$3 (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ )
+ (fp_text value FH19C-9S-0.5SH_10-FFC (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.27 1.27) (thickness 0.15)))
+ )
+ (fp_line (start -2.4892 0) (end -2.4892 3.35) (layer Edge.Cuts) (width 0.05))
+ (fp_line (start -2.3392 3.5) (end 2.3392 3.5) (layer Edge.Cuts) (width 0.05))
+ (fp_line (start 2.4892 0) (end 2.4892 3.35) (layer Edge.Cuts) (width 0.05))
+ (fp_arc (start -2.3392 3.35) (end -2.3392 3.5) (angle 90) (layer Edge.Cuts) (width 0.05))
+ (fp_arc (start 2.3392 3.35) (end 2.3392 3.5) (angle -90) (layer Edge.Cuts) (width 0.05))
+ (fp_poly (pts (xy -2.4902 0.9742) (xy 2.4882 0.9742) (xy 2.4892 3.4798) (xy -2.4892 3.4798)) (layer B.Mask) (width 0))
+ (pad 1 smd rect (at 2 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 2 smd rect (at 1.5 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 3 smd rect (at 1 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 4 smd rect (at 0.5 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 5 smd rect (at 0 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 6 smd rect (at -0.5 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 7 smd rect (at -1 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 8 smd rect (at -1.5 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 9 smd rect (at -2 2.25 90) (size 2.5 0.347) (layers B.Cu B.Mask)
+ (solder_mask_margin 0.0635))
+)
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/_0603MP.kicad_mod b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/_0603MP.kicad_mod
new file mode 100644
index 00000000..b242ec47
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty/_0603MP.kicad_mod
@@ -0,0 +1,19 @@
+(module _0603MP (layer F.Cu) (tedit 0)
+ (descr "0603 MicroPitch")
+ (fp_text reference R1 (at -0.0635 1.7145 -180) (layer F.SilkS)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_text value "1% 10K" (at -0.9525 0.9525 -180) (layer F.Fab)
+ (effects (font (size 0.77216 0.77216) (thickness 0.138988)) (justify left bottom))
+ )
+ (fp_line (start -0.432 0.306) (end 0.432 0.306) (layer F.Fab) (width 0.1016))
+ (fp_line (start 0.432 -0.306) (end -0.432 -0.306) (layer F.Fab) (width 0.1016))
+ (fp_line (start 0 -0.254) (end 0 0.254) (layer F.SilkS) (width 0.2032))
+ (fp_poly (pts (xy 0.4318 0.4) (xy 0.8 0.4) (xy 0.8 -0.4) (xy 0.4318 -0.4)) (layer F.Fab) (width 0))
+ (fp_poly (pts (xy -0.8 0.4) (xy -0.4318 0.4) (xy -0.4318 -0.4) (xy -0.8 -0.4)) (layer F.Fab) (width 0))
+ (fp_poly (pts (xy -0.1999 0.25) (xy 0.1999 0.25) (xy 0.1999 -0.25) (xy -0.1999 -0.25)) (layer F.Adhes) (width 0))
+ (pad 1 smd rect (at -0.762 0) (size 0.8 0.8) (layers F.Cu F.Paste F.Mask)
+ (solder_mask_margin 0.0635))
+ (pad 2 smd rect (at 0.762 0) (size 0.8 0.8) (layers F.Cu F.Paste F.Mask)
+ (solder_mask_margin 0.0635))
+)
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.sch b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.sch
new file mode 100644
index 00000000..7f0a6a3d
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.sch
@@ -0,0 +1,279 @@
+EESchema Schematic File Version 4
+EELAYER 30 0
+EELAYER END
+$Descr User 11980 8268
+encoding utf-8
+Sheet 1 1
+Title ""
+Date ""
+Rev ""
+Comp ""
+Comment1 ""
+Comment2 ""
+Comment3 ""
+Comment4 ""
+$EndDescr
+Wire Wire Line
+ 3300 3100 2200 3100
+Text Label 2300 3100 0 70 ~ 0
+SCL
+Wire Wire Line
+ 3300 3000 2200 3000
+Text Label 2300 3000 0 70 ~ 0
+SDA
+Wire Wire Line
+ 4200 2500 2200 2500
+Wire Wire Line
+ 4200 2500 4200 2600
+Wire Wire Line
+ 4300 2500 4200 2500
+Text Label 2300 2500 0 70 ~ 0
+GND
+Connection ~ 4200 2500
+Wire Wire Line
+ 2200 3200 3300 3200
+Text Label 2300 3200 0 70 ~ 0
+A0_TEMP_ENABLE
+Wire Wire Line
+ 4800 3200 5900 3200
+Text Label 4900 3200 0 70 ~ 0
+A0_TEMP_ENABLE
+Wire Wire Line
+ 3300 2600 2200 2600
+Text Label 2300 2600 0 70 ~ 0
+A4_SERCOM3.2_TC3.0
+Wire Wire Line
+ 3300 2700 2200 2700
+Text Label 2300 2700 0 70 ~ 0
+NC
+Wire Wire Line
+ 3300 2800 2200 2800
+Text Label 2300 2800 0 70 ~ 0
+A2_TEMP_SENSE
+Wire Wire Line
+ 5900 2800 4800 2800
+Connection ~ 5900 2800
+Text Label 4900 2800 0 70 ~ 0
+A2_TEMP_SENSE
+Wire Wire Line
+ 4300 2400 4200 2400
+Wire Wire Line
+ 4200 2400 2200 2400
+Wire Wire Line
+ 4200 2300 4200 2400
+Text Label 2300 2400 0 70 ~ 0
+VCC
+Connection ~ 4200 2400
+Wire Wire Line
+ 3300 2900 2200 2900
+Text Label 2300 2900 0 70 ~ 0
+A1_SERCOM3.3_TC3.1
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:A4L-LOC #FRAME1
+U 1 1 73DA64CA
+P 900 6200
+F 0 "#FRAME1" H 900 6200 50 0001 C CNN
+F 1 "A4L-LOC" H 900 6200 50 0001 C CNN
+F 2 "" H 900 6200 50 0001 C CNN
+F 3 "" H 900 6200 50 0001 C CNN
+ 1 900 6200
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:FH19C-9S-0.5SH_10-FFC U$3
+U 1 1 54307519
+P 1700 2800
+F 0 "U$3" H 2050 3500 59 0000 L CNN
+F 1 "FH19C-9S-0.5SH_10-FFC" H 2050 3400 59 0000 L CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:FH19C9S05SH10-FFC" H 1700 2800 50 0001 C CNN
+F 3 "" H 1700 2800 50 0001 C CNN
+ 1 1700 2800
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:RESISTOR_0603MP RT1
+U 1 1 6C7771A6
+P 5900 2600
+F 0 "RT1" H 5900 2700 50 0000 C CNN
+F 1 "NTC 10K" H 5900 2500 40 0000 C CNB
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:_0603MP" H 5900 2600 50 0001 C CNN
+F 3 "" H 5900 2600 50 0001 C CNN
+ 1 5900 2600
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:RESISTOR_0603MP R1
+U 1 1 05CBC8DB
+P 5900 3000
+F 0 "R1" H 5900 3100 50 0000 C CNN
+F 1 "1% 10K" H 5900 2900 40 0000 C CNB
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:_0603MP" H 5900 3000 50 0001 C CNN
+F 3 "" H 5900 3000 50 0001 C CNN
+ 1 5900 3000
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:GND #GND01
+U 1 1 DB8695AC
+P 4200 2700
+F 0 "#GND01" H 4200 2700 50 0001 C CNN
+F 1 "GND" H 4100 2600 59 0000 L BNN
+F 2 "" H 4200 2700 50 0001 C CNN
+F 3 "" H 4200 2700 50 0001 C CNN
+ 1 4200 2700
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:VCC #P+01
+U 1 1 BCDF72C5
+P 4200 2200
+F 0 "#P+01" H 4200 2200 50 0001 C CNN
+F 1 "VCC" V 4100 2200 59 0000 L BNN
+F 2 "" H 4200 2200 50 0001 C CNN
+F 3 "" H 4200 2200 50 0001 C CNN
+ 1 4200 2200
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:VCC #P+02
+U 1 1 339DE9D3
+P 5900 2300
+F 0 "#P+02" H 5900 2300 50 0001 C CNN
+F 1 "VCC" V 5800 2200 59 0000 L BNN
+F 2 "" H 5900 2300 50 0001 C CNN
+F 3 "" H 5900 2300 50 0001 C CNN
+ 1 5900 2300
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 GND1
+U 1 1 8E097FD0
+P 4400 2500
+F 0 "GND1" V 4450 2550 59 0000 L BNN
+F 1 "TPB1,27" H 4400 2500 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 4400 2500 50 0001 C CNN
+F 3 "" H 4400 2500 50 0001 C CNN
+ 1 4400 2500
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 A4
+U 1 1 E65641C5
+P 3400 2600
+F 0 "A4" V 3450 2650 59 0000 L BNN
+F 1 "TPB1,27" H 3400 2600 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 3400 2600 50 0001 C CNN
+F 3 "" H 3400 2600 50 0001 C CNN
+ 1 3400 2600
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 VCC1
+U 1 1 D0DD1D8E
+P 4400 2400
+F 0 "VCC1" V 4350 2450 59 0000 L BNN
+F 1 "TPB1,27" H 4400 2400 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 4400 2400 50 0001 C CNN
+F 3 "" H 4400 2400 50 0001 C CNN
+ 1 4400 2400
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 SCL1
+U 1 1 252148C1
+P 3400 3100
+F 0 "SCL1" V 3450 3150 59 0000 L BNN
+F 1 "TPB1,27" H 3400 3100 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 3400 3100 50 0001 C CNN
+F 3 "" H 3400 3100 50 0001 C CNN
+ 1 3400 3100
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 SDA1
+U 1 1 943CED1C
+P 3400 3000
+F 0 "SDA1" V 3450 3050 59 0000 L BNN
+F 1 "TPB1,27" H 3400 3000 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 3400 3000 50 0001 C CNN
+F 3 "" H 3400 3000 50 0001 C CNN
+ 1 3400 3000
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 A1
+U 1 1 E1B0505C
+P 3400 2900
+F 0 "A1" V 3450 2950 59 0000 L BNN
+F 1 "TPB1,27" H 3400 2900 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 3400 2900 50 0001 C CNN
+F 3 "" H 3400 2900 50 0001 C CNN
+ 1 3400 2900
+ 0 1 1 0
+$EndComp
+$Comp
+L OPT3001DNPT:OPT3001DNPT U1
+U 1 1 63E70E51
+P 7050 3100
+F 0 "U1" H 7050 3767 50 0000 C CNN
+F 1 "OPT3001DNPT" H 7050 3676 50 0000 C CNN
+F 2 "SON65P200X200X65-7N" H 7050 3100 50 0001 L BNN
+F 3 "" H 7050 3100 50 0001 L BNN
+F 4 "" H 7050 3100 50 0001 L BNN "EMAX"
+F 5 "" H 7050 3100 50 0001 L BNN "BALL_COLUMNS"
+F 6 "" H 7050 3100 50 0001 L BNN "BODY_DIAMETER"
+F 7 "0.65" H 7050 3100 50 0001 L BNN "ENOM"
+F 8 "" H 7050 3100 50 0001 L BNN "D1_MIN"
+F 9 "1.9" H 7050 3100 50 0001 L BNN "E_MIN"
+F 10 "0.25" H 7050 3100 50 0001 L BNN "L_MIN"
+F 11 "2.0" H 7050 3100 50 0001 L BNN "E_NOM"
+F 12 "0.65" H 7050 3100 50 0001 L BNN "A_MAX"
+F 13 "0.65" H 7050 3100 50 0001 L BNN "E2_NOM"
+F 14 "IPC 7351B" H 7050 3100 50 0001 L BNN "STANDARD"
+F 15 "01/2018" H 7050 3100 50 0001 L BNN "PARTREV"
+F 16 "0.25" H 7050 3100 50 0001 L BNN "B_NOM"
+F 17 "" H 7050 3100 50 0001 L BNN "THERMAL_PAD"
+F 18 "0.3" H 7050 3100 50 0001 L BNN "B_MAX"
+F 19 "0.2" H 7050 3100 50 0001 L BNN "B_MIN"
+F 20 "" H 7050 3100 50 0001 L BNN "IPC"
+F 21 "" H 7050 3100 50 0001 L BNN "DNOM"
+F 22 "6.0" H 7050 3100 50 0001 L BNN "PIN_COUNT"
+F 23 "2.1" H 7050 3100 50 0001 L BNN "E_MAX"
+F 24 "" H 7050 3100 50 0001 L BNN "JEDEC"
+F 25 "" H 7050 3100 50 0001 L BNN "BALL_ROWS"
+F 26 "" H 7050 3100 50 0001 L BNN "L1_MIN"
+F 27 "" H 7050 3100 50 0001 L BNN "L1_NOM"
+F 28 "" H 7050 3100 50 0001 L BNN "DMAX"
+F 29 "" H 7050 3100 50 0001 L BNN "PIN_COLUMNS"
+F 30 "" H 7050 3100 50 0001 L BNN "VACANCIES"
+F 31 "" H 7050 3100 50 0001 L BNN "SNAPEDA_PACKAGE_ID"
+F 32 "" H 7050 3100 50 0001 L BNN "DMIN"
+F 33 "" H 7050 3100 50 0001 L BNN "L1_MAX"
+F 34 "0.35" H 7050 3100 50 0001 L BNN "L_MAX"
+F 35 "" H 7050 3100 50 0001 L BNN "D1_NOM"
+F 36 "Texas Instruments" H 7050 3100 50 0001 L BNN "MANUFACTURER"
+F 37 "1.35" H 7050 3100 50 0001 L BNN "D2_NOM"
+F 38 "" H 7050 3100 50 0001 L BNN "PACKAGE_TYPE"
+F 39 "" H 7050 3100 50 0001 L BNN "D1_MAX"
+F 40 "0.3" H 7050 3100 50 0001 L BNN "L_NOM"
+F 41 "2.1" H 7050 3100 50 0001 L BNN "D_MAX"
+F 42 "2.0" H 7050 3100 50 0001 L BNN "D_NOM"
+F 43 "" H 7050 3100 50 0001 L BNN "PINS"
+F 44 "1.9" H 7050 3100 50 0001 L BNN "D_MIN"
+F 45 "" H 7050 3100 50 0001 L BNN "EMIN"
+ 1 7050 3100
+ 1 0 0 -1
+$EndComp
+NoConn ~ 7650 3200
+NoConn ~ 7650 2900
+Text Label 7650 2700 0 50 ~ 0
+VCC
+Text Label 7650 3400 0 50 ~ 0
+GND
+Text Label 6450 2900 2 50 ~ 0
+GND
+Text Label 6450 3100 2 50 ~ 0
+SCL
+Text Label 6450 3200 2 50 ~ 0
+SDA
+$EndSCHEMATC
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.sch-bak b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.sch-bak
new file mode 100644
index 00000000..7f0a6a3d
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.sch-bak
@@ -0,0 +1,279 @@
+EESchema Schematic File Version 4
+EELAYER 30 0
+EELAYER END
+$Descr User 11980 8268
+encoding utf-8
+Sheet 1 1
+Title ""
+Date ""
+Rev ""
+Comp ""
+Comment1 ""
+Comment2 ""
+Comment3 ""
+Comment4 ""
+$EndDescr
+Wire Wire Line
+ 3300 3100 2200 3100
+Text Label 2300 3100 0 70 ~ 0
+SCL
+Wire Wire Line
+ 3300 3000 2200 3000
+Text Label 2300 3000 0 70 ~ 0
+SDA
+Wire Wire Line
+ 4200 2500 2200 2500
+Wire Wire Line
+ 4200 2500 4200 2600
+Wire Wire Line
+ 4300 2500 4200 2500
+Text Label 2300 2500 0 70 ~ 0
+GND
+Connection ~ 4200 2500
+Wire Wire Line
+ 2200 3200 3300 3200
+Text Label 2300 3200 0 70 ~ 0
+A0_TEMP_ENABLE
+Wire Wire Line
+ 4800 3200 5900 3200
+Text Label 4900 3200 0 70 ~ 0
+A0_TEMP_ENABLE
+Wire Wire Line
+ 3300 2600 2200 2600
+Text Label 2300 2600 0 70 ~ 0
+A4_SERCOM3.2_TC3.0
+Wire Wire Line
+ 3300 2700 2200 2700
+Text Label 2300 2700 0 70 ~ 0
+NC
+Wire Wire Line
+ 3300 2800 2200 2800
+Text Label 2300 2800 0 70 ~ 0
+A2_TEMP_SENSE
+Wire Wire Line
+ 5900 2800 4800 2800
+Connection ~ 5900 2800
+Text Label 4900 2800 0 70 ~ 0
+A2_TEMP_SENSE
+Wire Wire Line
+ 4300 2400 4200 2400
+Wire Wire Line
+ 4200 2400 2200 2400
+Wire Wire Line
+ 4200 2300 4200 2400
+Text Label 2300 2400 0 70 ~ 0
+VCC
+Connection ~ 4200 2400
+Wire Wire Line
+ 3300 2900 2200 2900
+Text Label 2300 2900 0 70 ~ 0
+A1_SERCOM3.3_TC3.1
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:A4L-LOC #FRAME1
+U 1 1 73DA64CA
+P 900 6200
+F 0 "#FRAME1" H 900 6200 50 0001 C CNN
+F 1 "A4L-LOC" H 900 6200 50 0001 C CNN
+F 2 "" H 900 6200 50 0001 C CNN
+F 3 "" H 900 6200 50 0001 C CNN
+ 1 900 6200
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:FH19C-9S-0.5SH_10-FFC U$3
+U 1 1 54307519
+P 1700 2800
+F 0 "U$3" H 2050 3500 59 0000 L CNN
+F 1 "FH19C-9S-0.5SH_10-FFC" H 2050 3400 59 0000 L CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:FH19C9S05SH10-FFC" H 1700 2800 50 0001 C CNN
+F 3 "" H 1700 2800 50 0001 C CNN
+ 1 1700 2800
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:RESISTOR_0603MP RT1
+U 1 1 6C7771A6
+P 5900 2600
+F 0 "RT1" H 5900 2700 50 0000 C CNN
+F 1 "NTC 10K" H 5900 2500 40 0000 C CNB
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:_0603MP" H 5900 2600 50 0001 C CNN
+F 3 "" H 5900 2600 50 0001 C CNN
+ 1 5900 2600
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:RESISTOR_0603MP R1
+U 1 1 05CBC8DB
+P 5900 3000
+F 0 "R1" H 5900 3100 50 0000 C CNN
+F 1 "1% 10K" H 5900 2900 40 0000 C CNB
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:_0603MP" H 5900 3000 50 0001 C CNN
+F 3 "" H 5900 3000 50 0001 C CNN
+ 1 5900 3000
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:GND #GND01
+U 1 1 DB8695AC
+P 4200 2700
+F 0 "#GND01" H 4200 2700 50 0001 C CNN
+F 1 "GND" H 4100 2600 59 0000 L BNN
+F 2 "" H 4200 2700 50 0001 C CNN
+F 3 "" H 4200 2700 50 0001 C CNN
+ 1 4200 2700
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:VCC #P+01
+U 1 1 BCDF72C5
+P 4200 2200
+F 0 "#P+01" H 4200 2200 50 0001 C CNN
+F 1 "VCC" V 4100 2200 59 0000 L BNN
+F 2 "" H 4200 2200 50 0001 C CNN
+F 3 "" H 4200 2200 50 0001 C CNN
+ 1 4200 2200
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:VCC #P+02
+U 1 1 339DE9D3
+P 5900 2300
+F 0 "#P+02" H 5900 2300 50 0001 C CNN
+F 1 "VCC" V 5800 2200 59 0000 L BNN
+F 2 "" H 5900 2300 50 0001 C CNN
+F 3 "" H 5900 2300 50 0001 C CNN
+ 1 5900 2300
+ 1 0 0 -1
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 GND1
+U 1 1 8E097FD0
+P 4400 2500
+F 0 "GND1" V 4450 2550 59 0000 L BNN
+F 1 "TPB1,27" H 4400 2500 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 4400 2500 50 0001 C CNN
+F 3 "" H 4400 2500 50 0001 C CNN
+ 1 4400 2500
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 A4
+U 1 1 E65641C5
+P 3400 2600
+F 0 "A4" V 3450 2650 59 0000 L BNN
+F 1 "TPB1,27" H 3400 2600 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 3400 2600 50 0001 C CNN
+F 3 "" H 3400 2600 50 0001 C CNN
+ 1 3400 2600
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 VCC1
+U 1 1 D0DD1D8E
+P 4400 2400
+F 0 "VCC1" V 4350 2450 59 0000 L BNN
+F 1 "TPB1,27" H 4400 2400 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 4400 2400 50 0001 C CNN
+F 3 "" H 4400 2400 50 0001 C CNN
+ 1 4400 2400
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 SCL1
+U 1 1 252148C1
+P 3400 3100
+F 0 "SCL1" V 3450 3150 59 0000 L BNN
+F 1 "TPB1,27" H 3400 3100 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 3400 3100 50 0001 C CNN
+F 3 "" H 3400 3100 50 0001 C CNN
+ 1 3400 3100
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 SDA1
+U 1 1 943CED1C
+P 3400 3000
+F 0 "SDA1" V 3450 3050 59 0000 L BNN
+F 1 "TPB1,27" H 3400 3000 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 3400 3000 50 0001 C CNN
+F 3 "" H 3400 3000 50 0001 C CNN
+ 1 3400 3000
+ 0 1 1 0
+$EndComp
+$Comp
+L Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import:TPB1,27 A1
+U 1 1 E1B0505C
+P 3400 2900
+F 0 "A1" V 3450 2950 59 0000 L BNN
+F 1 "TPB1,27" H 3400 2900 50 0001 C CNN
+F 2 "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001:B1,27" H 3400 2900 50 0001 C CNN
+F 3 "" H 3400 2900 50 0001 C CNN
+ 1 3400 2900
+ 0 1 1 0
+$EndComp
+$Comp
+L OPT3001DNPT:OPT3001DNPT U1
+U 1 1 63E70E51
+P 7050 3100
+F 0 "U1" H 7050 3767 50 0000 C CNN
+F 1 "OPT3001DNPT" H 7050 3676 50 0000 C CNN
+F 2 "SON65P200X200X65-7N" H 7050 3100 50 0001 L BNN
+F 3 "" H 7050 3100 50 0001 L BNN
+F 4 "" H 7050 3100 50 0001 L BNN "EMAX"
+F 5 "" H 7050 3100 50 0001 L BNN "BALL_COLUMNS"
+F 6 "" H 7050 3100 50 0001 L BNN "BODY_DIAMETER"
+F 7 "0.65" H 7050 3100 50 0001 L BNN "ENOM"
+F 8 "" H 7050 3100 50 0001 L BNN "D1_MIN"
+F 9 "1.9" H 7050 3100 50 0001 L BNN "E_MIN"
+F 10 "0.25" H 7050 3100 50 0001 L BNN "L_MIN"
+F 11 "2.0" H 7050 3100 50 0001 L BNN "E_NOM"
+F 12 "0.65" H 7050 3100 50 0001 L BNN "A_MAX"
+F 13 "0.65" H 7050 3100 50 0001 L BNN "E2_NOM"
+F 14 "IPC 7351B" H 7050 3100 50 0001 L BNN "STANDARD"
+F 15 "01/2018" H 7050 3100 50 0001 L BNN "PARTREV"
+F 16 "0.25" H 7050 3100 50 0001 L BNN "B_NOM"
+F 17 "" H 7050 3100 50 0001 L BNN "THERMAL_PAD"
+F 18 "0.3" H 7050 3100 50 0001 L BNN "B_MAX"
+F 19 "0.2" H 7050 3100 50 0001 L BNN "B_MIN"
+F 20 "" H 7050 3100 50 0001 L BNN "IPC"
+F 21 "" H 7050 3100 50 0001 L BNN "DNOM"
+F 22 "6.0" H 7050 3100 50 0001 L BNN "PIN_COUNT"
+F 23 "2.1" H 7050 3100 50 0001 L BNN "E_MAX"
+F 24 "" H 7050 3100 50 0001 L BNN "JEDEC"
+F 25 "" H 7050 3100 50 0001 L BNN "BALL_ROWS"
+F 26 "" H 7050 3100 50 0001 L BNN "L1_MIN"
+F 27 "" H 7050 3100 50 0001 L BNN "L1_NOM"
+F 28 "" H 7050 3100 50 0001 L BNN "DMAX"
+F 29 "" H 7050 3100 50 0001 L BNN "PIN_COLUMNS"
+F 30 "" H 7050 3100 50 0001 L BNN "VACANCIES"
+F 31 "" H 7050 3100 50 0001 L BNN "SNAPEDA_PACKAGE_ID"
+F 32 "" H 7050 3100 50 0001 L BNN "DMIN"
+F 33 "" H 7050 3100 50 0001 L BNN "L1_MAX"
+F 34 "0.35" H 7050 3100 50 0001 L BNN "L_MAX"
+F 35 "" H 7050 3100 50 0001 L BNN "D1_NOM"
+F 36 "Texas Instruments" H 7050 3100 50 0001 L BNN "MANUFACTURER"
+F 37 "1.35" H 7050 3100 50 0001 L BNN "D2_NOM"
+F 38 "" H 7050 3100 50 0001 L BNN "PACKAGE_TYPE"
+F 39 "" H 7050 3100 50 0001 L BNN "D1_MAX"
+F 40 "0.3" H 7050 3100 50 0001 L BNN "L_NOM"
+F 41 "2.1" H 7050 3100 50 0001 L BNN "D_MAX"
+F 42 "2.0" H 7050 3100 50 0001 L BNN "D_NOM"
+F 43 "" H 7050 3100 50 0001 L BNN "PINS"
+F 44 "1.9" H 7050 3100 50 0001 L BNN "D_MIN"
+F 45 "" H 7050 3100 50 0001 L BNN "EMIN"
+ 1 7050 3100
+ 1 0 0 -1
+$EndComp
+NoConn ~ 7650 3200
+NoConn ~ 7650 2900
+Text Label 7650 2700 0 50 ~ 0
+VCC
+Text Label 7650 3400 0 50 ~ 0
+GND
+Text Label 6450 2900 2 50 ~ 0
+GND
+Text Label 6450 3100 2 50 ~ 0
+SCL
+Text Label 6450 3200 2 50 ~ 0
+SDA
+$EndSCHEMATC
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import.dcm b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import.dcm
new file mode 100644
index 00000000..5f3ed79b
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import.dcm
@@ -0,0 +1,3 @@
+EESchema-DOCLIB Version 2.0
+#
+#End Doc Library
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import.lib b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import.lib
new file mode 100644
index 00000000..b3b54896
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import.lib
@@ -0,0 +1,990 @@
+EESchema-LIBRARY Version 2.4
+#encoding utf-8
+#
+# A4L-LOC
+#
+DEF A4L-LOC #FRAME 0 40 Y Y 1 L N
+F0 "#FRAME" 0 0 50 H I C CNN
+F1 "A4L-LOC" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 8550 600 85 0 1 0 >DRAWING_NAME Normal 0 L B
+T 0 8550 400 76 0 1 0 >LAST_DATE_TIME Normal 0 L B
+T 0 8995 200 85 0 1 0 >SHEET Normal 0 L B
+T 0 8550 800 85 0 1 0 "by joey castillo" Normal 0 L B
+T 0 9300 200 85 0 1 0 "cc-by-sa 4.0" Normal 0 L B
+T 0 7300 200 180 0 1 0 oddly_specific_objects Normal 0 L B
+T 0 8540 195 85 0 1 0 Sheet: Normal 0 L B
+P 2 1 0 0 7250 150 7250 950 N
+P 2 1 0 0 7250 950 8475 950 N
+P 2 1 0 0 8475 350 8475 150 N
+P 2 1 0 0 8475 350 8475 550 N
+P 2 1 0 0 8475 550 8475 750 N
+P 2 1 0 0 8475 550 10100 550 N
+P 2 1 0 0 8475 750 8475 950 N
+P 2 1 0 0 8475 750 10100 750 N
+P 2 1 0 0 8475 950 10100 950 N
+P 2 1 0 0 9250 150 9250 350 N
+P 2 1 0 0 9250 350 8475 350 N
+P 2 1 0 0 9250 350 10100 350 N
+P 2 1 0 0 10100 150 10100 350 N
+P 2 1 0 0 10100 350 10100 550 N
+P 2 1 0 0 10100 550 10100 750 N
+P 2 1 0 0 10100 750 10100 950 N
+ENDDRAW
+ENDDEF
+#
+# FH19C-9S-0.5SH_10-FFC
+#
+DEF FH19C-9S-0.5SH_10-FFC ~ 0 40 Y Y 1 L N
+F0 "" 350 700 59 H V L CNN
+F1 "FH19C-9S-0.5SH_10-FFC" 350 600 59 H V L CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -200 500 -200 -500 N
+P 2 1 0 0 -200 500 300 500 N
+P 2 1 0 0 300 -500 -200 -500 N
+P 2 1 0 0 300 -500 300 500 N
+X 1 1 500 400 200 L 50 50 1 0 B
+X 2 2 500 300 200 L 50 50 1 0 B
+X 3 3 500 200 200 L 50 50 1 0 B
+X 4 4 500 100 200 L 50 50 1 0 B
+X 5 5 500 0 200 L 50 50 1 0 B
+X 6 6 500 -100 200 L 50 50 1 0 B
+X 7 7 500 -200 200 L 50 50 1 0 B
+X 8 8 500 -300 200 L 50 50 1 0 B
+X 9 9 500 -400 200 L 50 50 1 0 B
+ENDDRAW
+ENDDEF
+#
+# GND
+#
+DEF GND #GND 0 40 Y Y 1 L P
+F0 "#GND" 0 0 50 H I C CNN
+F1 "GND" -100 -100 59 H V L BNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -75 0 75 0 N
+X GND 1 0 100 100 D 0 0 1 0 W
+ENDDRAW
+ENDDEF
+#
+# RESISTOR0603MINI
+#
+DEF RESISTOR0603MINI R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR0603MINI" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR0805_10MGAP
+#
+DEF RESISTOR0805_10MGAP R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR0805_10MGAP" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR0805_NOOUTLINE
+#
+DEF RESISTOR0805_NOOUTLINE R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR0805_NOOUTLINE" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR0805_NOTHERMALS
+#
+DEF RESISTOR0805_NOTHERMALS R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR0805_NOTHERMALS" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR2012
+#
+DEF RESISTOR2012 R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR2012" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR2512
+#
+DEF RESISTOR2512 R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR2512" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_0402
+#
+DEF RESISTOR_0402 R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_0402" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_0402MP
+#
+DEF RESISTOR_0402MP R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_0402MP" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_0603
+#
+DEF RESISTOR_0603 R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_0603" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_0603MP
+#
+DEF RESISTOR_0603MP R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_0603MP" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_0603_NOOUT
+#
+DEF RESISTOR_0603_NOOUT R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_0603_NOOUT" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_0805
+#
+DEF RESISTOR_0805 R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_0805" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_0805MP
+#
+DEF RESISTOR_0805MP R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_0805MP" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_1206
+#
+DEF RESISTOR_1206 R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_1206" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# RESISTOR_1206MP
+#
+DEF RESISTOR_1206MP R 0 40 Y Y 1 L N
+F0 "R" 0 100 50 H V C CNN
+F1 "RESISTOR_1206MP" 0 0 40 H V C CNB
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 -100 -50 -100 50 N
+P 2 1 0 0 -100 50 100 50 N
+P 2 1 0 0 100 -50 -100 -50 N
+P 2 1 0 0 100 50 100 -50 N
+X 1 1 -200 0 100 R 0 0 1 0 P
+X 2 2 200 0 100 L 0 0 1 0 P
+ENDDRAW
+ENDDEF
+#
+# TPB1,27
+#
+DEF TPB1,27 TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPB1,27" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPB2,54
+#
+DEF TPB2,54 TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPB2,54" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPPAD1-13
+#
+DEF TPPAD1-13 TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPPAD1-13" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPPAD1-13Y
+#
+DEF TPPAD1-13Y TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPPAD1-13Y" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPPAD1-17
+#
+DEF TPPAD1-17 TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPPAD1-17" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPPAD1-17Y
+#
+DEF TPPAD1-17Y TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPPAD1-17Y" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPPAD1-20
+#
+DEF TPPAD1-20 TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPPAD1-20" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPPAD1-20Y
+#
+DEF TPPAD1-20Y TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPPAD1-20Y" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP06R
+#
+DEF TPTP06R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP06R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP06SQ
+#
+DEF TPTP06SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP06SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP07R
+#
+DEF TPTP07R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP07R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP07SQ
+#
+DEF TPTP07SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP07SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP08R
+#
+DEF TPTP08R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP08R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP08SQ
+#
+DEF TPTP08SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP08SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP09R
+#
+DEF TPTP09R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP09R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP09SQ
+#
+DEF TPTP09SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP09SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP10R
+#
+DEF TPTP10R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP10R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP10SQ
+#
+DEF TPTP10SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP10SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP11R
+#
+DEF TPTP11R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP11R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP11SQ
+#
+DEF TPTP11SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP11SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP12R
+#
+DEF TPTP12R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP12R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP12SQ
+#
+DEF TPTP12SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP12SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP13R
+#
+DEF TPTP13R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP13R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP13SQ
+#
+DEF TPTP13SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP13SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP14R
+#
+DEF TPTP14R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP14R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP14SQ
+#
+DEF TPTP14SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP14SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP15R
+#
+DEF TPTP15R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP15R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP15SQ
+#
+DEF TPTP15SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP15SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP16R
+#
+DEF TPTP16R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP16R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP16SQ
+#
+DEF TPTP16SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP16SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP17R
+#
+DEF TPTP17R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP17R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP17SQ
+#
+DEF TPTP17SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP17SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP18R
+#
+DEF TPTP18R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP18R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP18SQ
+#
+DEF TPTP18SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP18SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP19R
+#
+DEF TPTP19R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP19R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP19SQ
+#
+DEF TPTP19SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP19SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP20R
+#
+DEF TPTP20R TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP20R" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# TPTP20SQ
+#
+DEF TPTP20SQ TP 0 40 Y Y 1 L N
+F0 "TP" -50 50 59 H V L BNN
+F1 "TPTP20SQ" 0 0 50 H I C CNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+T 0 50 -50 59 0 1 0 >TP_SIGNAL_NAME Normal 0 L B
+P 2 1 0 0 -30 -30 0 0 N
+P 2 1 0 0 0 -60 -30 -30 N
+P 2 1 0 0 0 0 30 -30 N
+P 2 1 0 0 30 -30 0 -60 N
+X TP TP 0 -100 100 U 0 0 1 0 I
+ENDDRAW
+ENDDEF
+#
+# VCC
+#
+DEF VCC #P+ 0 40 Y Y 1 L P
+F0 "#P+" 0 0 50 H I C CNN
+F1 "VCC" -100 -100 59 V V L BNN
+F2 "" 0 0 50 H I C CNN
+F3 "" 0 0 50 H I C CNN
+DRAW
+P 2 1 0 0 0 0 -50 -75 N
+P 2 1 0 0 50 -75 0 0 N
+X VCC 1 0 -100 100 U 0 0 1 0 W
+ENDDRAW
+ENDDEF
+#
+#End Library
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/empty.kicad_wks b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/empty.kicad_wks
new file mode 100644
index 00000000..f50032b1
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/empty.kicad_wks
@@ -0,0 +1,5 @@
+(page_layout
+(setup (textsize 1.5 1.5)(linewidth 0.15)(textlinewidth 0.15)
+(left_margin 10)(right_margin 10)(top_margin 10)(bottom_margin 10))
+(line (name segm1:Line) (start 0 0) (end 0 0))
+)
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/fp-info-cache b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/fp-info-cache
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/fp-info-cache
@@ -0,0 +1 @@
+0
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/fp-lib-table b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/fp-lib-table
new file mode 100644
index 00000000..9ebe18e1
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/fp-lib-table
@@ -0,0 +1,4 @@
+(fp_lib_table
+ (lib (name "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001")(type KiCad)(uri "$(KIPRJMOD)/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001.pretty")(options "")(descr ""))
+ (lib (name OPT3001DNPT)(type KiCad)(uri ${KIPRJMOD}/OPT3001DNPT)(options "")(descr ""))
+)
diff --git a/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/sym-lib-table b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/sym-lib-table
new file mode 100644
index 00000000..8b9d2689
--- /dev/null
+++ b/PCB/Sensor Boards/Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001/sym-lib-table
@@ -0,0 +1,4 @@
+(sym_lib_table
+ (lib (name Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import)(type Legacy)(uri ${KIPRJMOD}/Q3Q-SWAB-A1-00_Temperature_+_Test_Points_+_OPT3001-eagle-import.lib)(options "")(descr ""))
+ (lib (name OPT3001DNPT)(type Legacy)(uri ${KIPRJMOD}/OPT3001DNPT/OPT3001DNPT.lib)(options "")(descr ""))
+)
diff --git a/make.mk b/make.mk
index 737e1f51..35af3422 100644
--- a/make.mk
+++ b/make.mk
@@ -150,6 +150,7 @@ SRCS += \
$(TOP)/watch-library/hardware/hpl/systick/hpl_systick.c \
$(TOP)/watch-library/shared/driver/thermistor_driver.c \
$(TOP)/watch-library/shared/driver/lis2dw.c \
+ $(TOP)/watch-library/shared/driver/opt3001.c \
$(TOP)/watch-library/shared/driver/spiflash.c \
$(TOP)/watch-library/shared/watch/watch_private_buzzer.c \
$(TOP)/watch-library/shared/watch/watch_private_display.c \
@@ -195,6 +196,7 @@ SRCS += \
$(TOP)/watch-library/simulator/watch/watch_private.c \
$(TOP)/watch-library/simulator/watch/watch.c \
$(TOP)/watch-library/shared/driver/thermistor_driver.c \
+ $(TOP)/watch-library/shared/driver/opt3001.c \
$(TOP)/watch-library/shared/watch/watch_private_buzzer.c \
$(TOP)/watch-library/shared/watch/watch_private_display.c \
$(TOP)/watch-library/shared/watch/watch_utility.c \
diff --git a/movement/filesystem.c b/movement/filesystem.c
index 2b345eda..97e35455 100644
--- a/movement/filesystem.c
+++ b/movement/filesystem.c
@@ -196,6 +196,7 @@ static void filesystem_cat(char *filename) {
if (info.size > 0) {
char *buf = malloc(info.size + 1);
filesystem_read_file(filename, buf, info.size);
+ buf[info.size] = '\0';
printf("%s\n", buf);
free(buf);
} else {
diff --git a/movement/lib/chirpy_tx/chirpy_tx.c b/movement/lib/chirpy_tx/chirpy_tx.c
new file mode 100644
index 00000000..41a42a58
--- /dev/null
+++ b/movement/lib/chirpy_tx/chirpy_tx.c
@@ -0,0 +1,179 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include
+#include
+#include "chirpy_tx.h"
+
+static const uint32_t chirpy_min_freq = 2500;
+static const uint32_t cirpy_freq_step = 250;
+
+// This many bytes are followed by a CRC and block separator
+// It's a multiple of 3 so no bits are wasted (a tone encodes 3 bits)
+// Last block can be shorter
+static const uint8_t chirpy_default_block_size = 15;
+
+// The dedicated control tone. This is the highest tone index.
+static const uint8_t chirpy_control_tone = 8;
+
+// Pre-computed tone periods. This is allocated and populated on-demand.
+static uint32_t *chirpy_tone_periods = NULL;
+
+uint8_t chirpy_crc8(const uint8_t *addr, uint16_t len) {
+ uint8_t crc = 0;
+ for (uint16_t i = 0; i < len; i++)
+ crc = chirpy_update_crc8(addr[i], crc);
+ return crc;
+}
+
+uint8_t chirpy_update_crc8(uint8_t next_byte, uint8_t crc) {
+ for (uint8_t j = 0; j < 8; j++) {
+ uint8_t mix = (crc ^ next_byte) & 0x01;
+ crc >>= 1;
+ if (mix)
+ crc ^= 0x8C;
+ next_byte >>= 1;
+ }
+ return crc;
+}
+
+static void _chirpy_append_tone(chirpy_encoder_state_t *ces, uint8_t tone) {
+ // This is BAD and should never happen. But if it does, we'd rather
+ // create a corrupt transmission than corrupt memory #$^@
+ if (ces->tone_count == CHIRPY_TONE_BUF_SIZE)
+ return;
+ ces->tone_buf[ces->tone_count] = tone;
+ ++ces->tone_count;
+}
+
+void chirpy_init_encoder(chirpy_encoder_state_t *ces, chirpy_get_next_byte_t get_next_byte) {
+ memset(ces, 0, sizeof(chirpy_encoder_state_t));
+ ces->block_size = chirpy_default_block_size;
+ ces->get_next_byte = get_next_byte;
+ _chirpy_append_tone(ces, 8);
+ _chirpy_append_tone(ces, 0);
+ _chirpy_append_tone(ces, 8);
+ _chirpy_append_tone(ces, 0);
+}
+
+static uint8_t _chirpy_retrieve_next_tone(chirpy_encoder_state_t *ces) {
+ if (ces->tone_pos == ces->tone_count)
+ return 255;
+
+ uint8_t res = ces->tone_buf[ces->tone_pos];
+ ++ces->tone_pos;
+ if (ces->tone_pos == ces->tone_count) {
+ // End of buffer: reset buffer
+ ces->tone_pos = 0;
+ ces->tone_count = 0;
+ }
+ return res;
+}
+
+static void _chirpy_encode_bits(chirpy_encoder_state_t *ces, uint8_t force_partial) {
+ while (ces->bit_count > 0) {
+ if (ces->bit_count < 3 && !force_partial) break;
+ uint8_t tone = (uint8_t)(ces->bits >> 13);
+ _chirpy_append_tone(ces, tone);
+ if (ces->bit_count >= 3) {
+ ces->bits <<= 3;
+ ces->bit_count -= 3;
+ } else {
+ ces->bits = 0;
+ ces->bit_count = 0;
+ }
+ }
+}
+
+static void _chirpy_finish_block(chirpy_encoder_state_t *ces) {
+ _chirpy_append_tone(ces, chirpy_control_tone);
+ ces->bits = ces->crc;
+ ces->bits <<= 8;
+ ces->bit_count = 8;
+ _chirpy_encode_bits(ces, 1);
+ ces->bit_count = 0;
+ ces->crc = 0;
+ ces->block_len = 0;
+ _chirpy_append_tone(ces, chirpy_control_tone);
+}
+
+static void _chirpy_finish_transmission(chirpy_encoder_state_t *ces) {
+ _chirpy_append_tone(ces, chirpy_control_tone);
+ _chirpy_append_tone(ces, chirpy_control_tone);
+}
+
+uint8_t chirpy_get_next_tone(chirpy_encoder_state_t *ces) {
+ // If there are tones left in the buffer, keep sending those
+ if (ces->tone_pos < ces->tone_count)
+ return _chirpy_retrieve_next_tone(ces);
+
+ // We know data is over: that means we've wrapped up transmission
+ // Just drain tone buffer, and then keep sendig EOB
+ if (ces->get_next_byte == 0)
+ return _chirpy_retrieve_next_tone(ces);
+
+ // Fetch next byte
+ uint8_t next_byte;
+ uint8_t got_more = ces->get_next_byte(&next_byte);
+
+ // Data over: write CRC if we sent a partial buffer; send end signal
+ if (got_more == 0) {
+ ces->get_next_byte = 0;
+ if (ces->bit_count > 0) _chirpy_encode_bits(ces, 1);
+ if (ces->block_len > 0) _chirpy_finish_block(ces);
+ _chirpy_finish_transmission(ces);
+ return _chirpy_retrieve_next_tone(ces);
+ }
+
+ // Got more data: add to bits; convert
+ uint16_t msk = next_byte;
+ msk <<= (8 - ces->bit_count);
+ ces->bits |= msk;
+ ces->bit_count += 8;
+ _chirpy_encode_bits(ces, 0);
+ ++ces->block_len;
+ ces->crc = chirpy_update_crc8(next_byte, ces->crc);
+ if (ces->block_len == ces->block_size)
+ _chirpy_finish_block(ces);
+
+ return _chirpy_retrieve_next_tone(ces);
+}
+
+uint16_t chirpy_get_tone_period(uint8_t tone) {
+ // Create pre-computed tone periods array on first use
+ if (chirpy_tone_periods == NULL) {
+ chirpy_tone_periods = malloc((chirpy_control_tone + 1) * sizeof(uint32_t));
+ for (uint8_t i = 0; i < chirpy_control_tone + 1; ++i) {
+ uint32_t freq = chirpy_min_freq + i * cirpy_freq_step;
+ uint16_t period = 1000000 / freq;
+ chirpy_tone_periods[i] = period;
+ }
+ }
+ // Return pre-computed value, but be paranoid about indexing into array
+ if (tone > chirpy_control_tone)
+ tone = chirpy_control_tone;
+ return chirpy_tone_periods[tone];
+}
diff --git a/movement/lib/chirpy_tx/chirpy_tx.h b/movement/lib/chirpy_tx/chirpy_tx.h
new file mode 100644
index 00000000..092d05c7
--- /dev/null
+++ b/movement/lib/chirpy_tx/chirpy_tx.h
@@ -0,0 +1,98 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef CHIRPY_TX_H
+#define CHIRPY_TX_H
+
+/** @brief Calculates the CRC of a byte sequence.
+ */
+uint8_t chirpy_crc8(const uint8_t *addr, uint16_t len);
+
+/** @brief Updates a CRC value with the next byte of input.
+ */
+uint8_t chirpy_update_crc8(uint8_t next_byte, uint8_t crc);
+
+/** @brief Function to return next byte to be encoded.
+ * @param next_byte Pointer where implementor must write next byte (if available).
+ * @return 1 if there is a next byte, or 0 if no more data to encode.
+ */
+typedef uint8_t (*chirpy_get_next_byte_t)(uint8_t *next_byte);
+
+#define CHIRPY_TONE_BUF_SIZE 16
+
+// Holds state used by the encoder. Do not manipulate directly.
+typedef struct {
+ uint8_t tone_buf[CHIRPY_TONE_BUF_SIZE];
+ uint8_t tone_pos;
+ uint8_t tone_count;
+ uint8_t block_len;
+ uint8_t block_size;
+ uint8_t crc;
+ uint16_t bits;
+ uint8_t bit_count;
+ chirpy_get_next_byte_t get_next_byte;
+} chirpy_encoder_state_t;
+
+/** @brief Iniitializes the encoder state to be used during the transmission.
+ * @param ces Pointer to encoder state object to be initialized.
+ * @param get_next_byte Pointer to function that the encoder will call to fetch data byte by byte.
+ */
+void chirpy_init_encoder(chirpy_encoder_state_t *ces, chirpy_get_next_byte_t get_next_byte);
+
+/** @brief Returns the next tone to be transmitted.
+ * @details This function will call the get_next_byte function stored in the encoder state to
+ * retrieve the next byte to be transmitted as needed. As a single byte is encoded as several tones,
+ * and because the transmission also includes periodic CRC values, not every call to this function
+ * will result in a callback for the next data byte.
+ * @param ced Pointer to the encoder state object.
+ * @return A tone index from 0 to N (where N is the largest tone index), or 255 if the transmission is over.
+ */
+uint8_t chirpy_get_next_tone(chirpy_encoder_state_t *ces);
+
+/** @brief Returns the period value for buzzing out a tone.
+ * @param tone The tone index, 0 thru 8.
+ * @return The period for the tone's frequency, i.e., 1_000_000 / freq.
+ */
+uint16_t chirpy_get_tone_period(uint8_t tone);
+
+/** @brief Typedef for a tick handler function.
+ */
+typedef void (*chirpy_tick_fun_t)(void *context);
+
+/** @brief Creature-comfort struct for use in your chirping code.
+ * @details The idea is to handle a tick that happens 64 times per second at the outermost level.
+ * To get to the desired ~20 tones per second, increment a counter and call the actual
+ * transmission ticker when tick_counter reaches tick_compare, with a compare value of 3.
+ * seq_pos is for use by the transmission function to keep track of where it is in the data.
+ * The current transmission function is stored in tick_fun. You can have multiple phases
+ * by switching to a different function. E.g., intro countdown first, followed by data chirping.
+ */
+typedef struct {
+ uint8_t tick_count;
+ uint8_t tick_compare;
+ uint16_t seq_pos;
+ chirpy_tick_fun_t tick_fun;
+} chirpy_tick_state_t;
+
+#endif
diff --git a/movement/lib/chirpy_tx/test/test_main.c b/movement/lib/chirpy_tx/test/test_main.c
new file mode 100644
index 00000000..c76a3f3c
--- /dev/null
+++ b/movement/lib/chirpy_tx/test/test_main.c
@@ -0,0 +1,160 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include
+#include "../src/lib/chirpy_tx.h"
+#include "unity.h"
+
+
+void setUp(void) {
+}
+
+void tearDown(void) {
+}
+
+const char *crc_test_str0 = "";
+const char *crc_test_str1 = "h";
+const char *crc_test_str2 = "he";
+const char *crc_test_str3 = "hen";
+const char *crc_test_str4 = "henl";
+const char *crc_test_str5 = "henlO";
+
+#define CRC_VAL_COUNT 5
+const uint8_t crc_vals[] = {167, 118, 95, 92, 127};
+
+void test_crc8() {
+ TEST_ASSERT_EQUAL_UINT8(0, chirpy_crc8((const uint8_t *)crc_test_str0, 0));
+ TEST_ASSERT_EQUAL_UINT8(167, chirpy_crc8((const uint8_t *)crc_test_str1, strlen(crc_test_str1)));
+ TEST_ASSERT_EQUAL_UINT8(118, chirpy_crc8((const uint8_t *)crc_test_str2, strlen(crc_test_str2)));
+ TEST_ASSERT_EQUAL_UINT8(95, chirpy_crc8((const uint8_t *)crc_test_str3, strlen(crc_test_str3)));
+ TEST_ASSERT_EQUAL_UINT8(92, chirpy_crc8((const uint8_t *)crc_test_str4, strlen(crc_test_str4)));
+ TEST_ASSERT_EQUAL_UINT8(127, chirpy_crc8((const uint8_t *)crc_test_str5, strlen(crc_test_str5)));
+
+ uint8_t crc = 0;
+ for (uint16_t i = 0; i < CRC_VAL_COUNT; ++i) {
+ uint8_t next_byte = crc_test_str5[i];
+ crc = chirpy_update_crc8(next_byte, crc);
+ TEST_ASSERT_EQUAL_UINT8(crc_vals[i], crc);
+ }
+
+ crc = chirpy_update_crc8(0, 0);
+ TEST_ASSERT_EQUAL(0, crc);
+
+ crc = chirpy_update_crc8(0x4f, 0);
+ TEST_ASSERT_EQUAL(7, crc);
+}
+
+const uint16_t data_len_01 = 0;
+const uint8_t data_01[] = {};
+const uint16_t tones_len_01 = 6;
+const uint8_t tones_01[] = {8, 0, 8, 0, 8, 8};
+
+const uint16_t data_len_02 = 1;
+const uint8_t data_02[] = {0};
+const uint16_t tones_len_02 = 14;
+const uint8_t tones_02[] = {8, 0, 8, 0, 0, 0, 0, 8, 0, 0, 0, 8, 8, 8};
+
+const uint16_t data_len_03 = 1;
+const uint8_t data_03[] = {0x68};
+const uint16_t tones_len_03 = 14;
+const uint8_t tones_03[] = {8, 0, 8, 0, 3, 2, 0, 8, 5, 1, 6, 8, 8, 8};
+
+const uint16_t data_len_04 = 3;
+const uint8_t data_04[] = {0x68, 0x65, 0x6e};
+const uint16_t tones_len_04 = 19;
+const uint8_t tones_04[] = {8, 0, 8, 0, 3, 2, 0, 6, 2, 5, 5, 6, 8, 2, 7, 6, 8, 8, 8};
+
+const uint16_t data_len_05 = 4;
+const uint8_t data_05[] = {0x68, 0x65, 0x6e, 0x4f};
+const uint16_t tones_len_05 = 27;
+const uint8_t tones_05[] = {
+ 8, 0, 8, 0, 3, 2, 0, 6, 2, 5, 5, 6, 8, 2, 7, 6, 8,
+ 2, 3, 6, 8, 0, 1, 6, 8, 8, 8};
+
+uint8_t curr_data_pos;
+uint8_t curr_data_len;
+const uint8_t *curr_data;
+
+uint8_t get_next_byte(uint8_t *next_byte) {
+ if (curr_data_pos < curr_data_len) {
+ *next_byte = curr_data[curr_data_pos];
+ ++curr_data_pos;
+ return 1;
+ }
+ return 0;
+}
+
+void test_encoder_one(const uint8_t *data, uint16_t data_len, const uint8_t *tones, uint16_t tones_len) {
+ curr_data = data;
+ curr_data_len = data_len;
+ curr_data_pos = 0;
+ chirpy_encoder_state_t ces;
+ chirpy_init_encoder(&ces, get_next_byte);
+ ces.block_size = 3;
+
+ uint8_t got_tones[2048] = {0};
+ uint16_t got_tone_pos = 0;
+ while (got_tone_pos < 2048) {
+ uint8_t tone = chirpy_get_next_tone(&ces);
+ got_tones[got_tone_pos] = tone;
+ if (tone == 255) break;
+ ++got_tone_pos;
+ }
+ char buf1[65536];
+ char bufx[256];
+ memset(buf1, 0, 65536);
+ for (uint16_t i = 0; i < got_tone_pos; ++i) {
+ if (i == 0)
+ sprintf(bufx, "%d", got_tones[i]);
+ else
+ sprintf(bufx, ", %d", got_tones[i]);
+ strcat(buf1, bufx);
+ }
+ TEST_MESSAGE(buf1);
+ TEST_ASSERT_EQUAL(tones_len, got_tone_pos);
+ uint16_t smaller_len = tones_len < got_tone_pos ? tones_len : got_tone_pos;
+ TEST_ASSERT_EQUAL_UINT8_ARRAY(tones, got_tones, smaller_len);
+}
+
+void test_encoder() {
+ TEST_MESSAGE("Testing encoder with dataset 01");
+ test_encoder_one(data_01, data_len_01, tones_01, tones_len_01);
+ TEST_MESSAGE("Testing encoder with dataset 02");
+ test_encoder_one(data_02, data_len_02, tones_02, tones_len_02);
+ TEST_MESSAGE("Testing encoder with dataset 03");
+ test_encoder_one(data_03, data_len_03, tones_03, tones_len_03);
+ TEST_MESSAGE("Testing encoder with dataset 04");
+ test_encoder_one(data_04, data_len_04, tones_04, tones_len_04);
+ TEST_MESSAGE("Testing encoder with dataset 05");
+ test_encoder_one(data_05, data_len_05, tones_05, tones_len_05);
+}
+
+int main(void) {
+ UNITY_BEGIN();
+ RUN_TEST(test_crc8);
+ RUN_TEST(test_encoder);
+ return UNITY_END();
+}
diff --git a/movement/lib/chirpy_tx/test/unity.c b/movement/lib/chirpy_tx/test/unity.c
new file mode 100644
index 00000000..3e4bc04d
--- /dev/null
+++ b/movement/lib/chirpy_tx/test/unity.c
@@ -0,0 +1,2466 @@
+/* =========================================================================
+ Unity Project - A Test Framework for C
+ Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
+ [Released under MIT License. Please refer to license.txt for details]
+============================================================================ */
+
+#include "unity.h"
+
+#ifndef UNITY_PROGMEM
+#define UNITY_PROGMEM
+#endif
+
+/* If omitted from header, declare overrideable prototypes here so they're ready for use */
+#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION
+void UNITY_OUTPUT_CHAR(int);
+#endif
+
+/* Helpful macros for us to use here in Assert functions */
+#define UNITY_FAIL_AND_BAIL do { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0)
+#define UNITY_IGNORE_AND_BAIL do { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0)
+#define RETURN_IF_FAIL_OR_IGNORE do { if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) { TEST_ABORT(); } } while (0)
+
+struct UNITY_STORAGE_T Unity;
+
+#ifdef UNITY_OUTPUT_COLOR
+const char UNITY_PROGMEM UnityStrOk[] = "\033[42mOK\033[0m";
+const char UNITY_PROGMEM UnityStrPass[] = "\033[42mPASS\033[0m";
+const char UNITY_PROGMEM UnityStrFail[] = "\033[41mFAIL\033[0m";
+const char UNITY_PROGMEM UnityStrIgnore[] = "\033[43mIGNORE\033[0m";
+#else
+const char UNITY_PROGMEM UnityStrOk[] = "OK";
+const char UNITY_PROGMEM UnityStrPass[] = "PASS";
+const char UNITY_PROGMEM UnityStrFail[] = "FAIL";
+const char UNITY_PROGMEM UnityStrIgnore[] = "IGNORE";
+#endif
+static const char UNITY_PROGMEM UnityStrNull[] = "NULL";
+static const char UNITY_PROGMEM UnityStrSpacer[] = ". ";
+static const char UNITY_PROGMEM UnityStrExpected[] = " Expected ";
+static const char UNITY_PROGMEM UnityStrWas[] = " Was ";
+static const char UNITY_PROGMEM UnityStrGt[] = " to be greater than ";
+static const char UNITY_PROGMEM UnityStrLt[] = " to be less than ";
+static const char UNITY_PROGMEM UnityStrOrEqual[] = "or equal to ";
+static const char UNITY_PROGMEM UnityStrNotEqual[] = " to be not equal to ";
+static const char UNITY_PROGMEM UnityStrElement[] = " Element ";
+static const char UNITY_PROGMEM UnityStrByte[] = " Byte ";
+static const char UNITY_PROGMEM UnityStrMemory[] = " Memory Mismatch.";
+static const char UNITY_PROGMEM UnityStrDelta[] = " Values Not Within Delta ";
+static const char UNITY_PROGMEM UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless.";
+static const char UNITY_PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL";
+static const char UNITY_PROGMEM UnityStrNullPointerForActual[] = " Actual pointer was NULL";
+#ifndef UNITY_EXCLUDE_FLOAT
+static const char UNITY_PROGMEM UnityStrNot[] = "Not ";
+static const char UNITY_PROGMEM UnityStrInf[] = "Infinity";
+static const char UNITY_PROGMEM UnityStrNegInf[] = "Negative Infinity";
+static const char UNITY_PROGMEM UnityStrNaN[] = "NaN";
+static const char UNITY_PROGMEM UnityStrDet[] = "Determinate";
+static const char UNITY_PROGMEM UnityStrInvalidFloatTrait[] = "Invalid Float Trait";
+#endif
+const char UNITY_PROGMEM UnityStrErrShorthand[] = "Unity Shorthand Support Disabled";
+const char UNITY_PROGMEM UnityStrErrFloat[] = "Unity Floating Point Disabled";
+const char UNITY_PROGMEM UnityStrErrDouble[] = "Unity Double Precision Disabled";
+const char UNITY_PROGMEM UnityStrErr64[] = "Unity 64-bit Support Disabled";
+static const char UNITY_PROGMEM UnityStrBreaker[] = "-----------------------";
+static const char UNITY_PROGMEM UnityStrResultsTests[] = " Tests ";
+static const char UNITY_PROGMEM UnityStrResultsFailures[] = " Failures ";
+static const char UNITY_PROGMEM UnityStrResultsIgnored[] = " Ignored ";
+#ifndef UNITY_EXCLUDE_DETAILS
+static const char UNITY_PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " ";
+static const char UNITY_PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " ";
+#endif
+/*-----------------------------------------------
+ * Pretty Printers & Test Result Output Handlers
+ *-----------------------------------------------*/
+
+/*-----------------------------------------------*/
+/* Local helper function to print characters. */
+static void UnityPrintChar(const char* pch)
+{
+ /* printable characters plus CR & LF are printed */
+ if ((*pch <= 126) && (*pch >= 32))
+ {
+ UNITY_OUTPUT_CHAR(*pch);
+ }
+ /* write escaped carriage returns */
+ else if (*pch == 13)
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('r');
+ }
+ /* write escaped line feeds */
+ else if (*pch == 10)
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('n');
+ }
+ /* unprintable characters are shown as codes */
+ else
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('x');
+ UnityPrintNumberHex((UNITY_UINT)*pch, 2);
+ }
+}
+
+/*-----------------------------------------------*/
+/* Local helper function to print ANSI escape strings e.g. "\033[42m". */
+#ifdef UNITY_OUTPUT_COLOR
+static UNITY_UINT UnityPrintAnsiEscapeString(const char* string)
+{
+ const char* pch = string;
+ UNITY_UINT count = 0;
+
+ while (*pch && (*pch != 'm'))
+ {
+ UNITY_OUTPUT_CHAR(*pch);
+ pch++;
+ count++;
+ }
+ UNITY_OUTPUT_CHAR('m');
+ count++;
+
+ return count;
+}
+#endif
+
+/*-----------------------------------------------*/
+void UnityPrint(const char* string)
+{
+ const char* pch = string;
+
+ if (pch != NULL)
+ {
+ while (*pch)
+ {
+#ifdef UNITY_OUTPUT_COLOR
+ /* print ANSI escape code */
+ if ((*pch == 27) && (*(pch + 1) == '['))
+ {
+ pch += UnityPrintAnsiEscapeString(pch);
+ continue;
+ }
+#endif
+ UnityPrintChar(pch);
+ pch++;
+ }
+ }
+}
+/*-----------------------------------------------*/
+void UnityPrintLen(const char* string, const UNITY_UINT32 length)
+{
+ const char* pch = string;
+
+ if (pch != NULL)
+ {
+ while (*pch && ((UNITY_UINT32)(pch - string) < length))
+ {
+ /* printable characters plus CR & LF are printed */
+ if ((*pch <= 126) && (*pch >= 32))
+ {
+ UNITY_OUTPUT_CHAR(*pch);
+ }
+ /* write escaped carriage returns */
+ else if (*pch == 13)
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('r');
+ }
+ /* write escaped line feeds */
+ else if (*pch == 10)
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('n');
+ }
+ /* unprintable characters are shown as codes */
+ else
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('x');
+ UnityPrintNumberHex((UNITY_UINT)*pch, 2);
+ }
+ pch++;
+ }
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style)
+{
+ if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+ {
+ if (style == UNITY_DISPLAY_STYLE_CHAR)
+ {
+ /* printable characters plus CR & LF are printed */
+ UNITY_OUTPUT_CHAR('\'');
+ if ((number <= 126) && (number >= 32))
+ {
+ UNITY_OUTPUT_CHAR((int)number);
+ }
+ /* write escaped carriage returns */
+ else if (number == 13)
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('r');
+ }
+ /* write escaped line feeds */
+ else if (number == 10)
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('n');
+ }
+ /* unprintable characters are shown as codes */
+ else
+ {
+ UNITY_OUTPUT_CHAR('\\');
+ UNITY_OUTPUT_CHAR('x');
+ UnityPrintNumberHex((UNITY_UINT)number, 2);
+ }
+ UNITY_OUTPUT_CHAR('\'');
+ }
+ else
+ {
+ UnityPrintNumber(number);
+ }
+ }
+ else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT)
+ {
+ UnityPrintNumberUnsigned((UNITY_UINT)number);
+ }
+ else
+ {
+ UNITY_OUTPUT_CHAR('0');
+ UNITY_OUTPUT_CHAR('x');
+ UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2));
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityPrintNumber(const UNITY_INT number_to_print)
+{
+ UNITY_UINT number = (UNITY_UINT)number_to_print;
+
+ if (number_to_print < 0)
+ {
+ /* A negative number, including MIN negative */
+ UNITY_OUTPUT_CHAR('-');
+ number = (~number) + 1;
+ }
+ UnityPrintNumberUnsigned(number);
+}
+
+/*-----------------------------------------------
+ * basically do an itoa using as little ram as possible */
+void UnityPrintNumberUnsigned(const UNITY_UINT number)
+{
+ UNITY_UINT divisor = 1;
+
+ /* figure out initial divisor */
+ while (number / divisor > 9)
+ {
+ divisor *= 10;
+ }
+
+ /* now mod and print, then divide divisor */
+ do
+ {
+ UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10)));
+ divisor /= 10;
+ } while (divisor > 0);
+}
+
+/*-----------------------------------------------*/
+void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print)
+{
+ int nibble;
+ char nibbles = nibbles_to_print;
+
+ if ((unsigned)nibbles > UNITY_MAX_NIBBLES)
+ {
+ nibbles = UNITY_MAX_NIBBLES;
+ }
+
+ while (nibbles > 0)
+ {
+ nibbles--;
+ nibble = (int)(number >> (nibbles * 4)) & 0x0F;
+ if (nibble <= 9)
+ {
+ UNITY_OUTPUT_CHAR((char)('0' + nibble));
+ }
+ else
+ {
+ UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble));
+ }
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number)
+{
+ UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1);
+ UNITY_INT32 i;
+
+ for (i = 0; i < UNITY_INT_WIDTH; i++)
+ {
+ if (current_bit & mask)
+ {
+ if (current_bit & number)
+ {
+ UNITY_OUTPUT_CHAR('1');
+ }
+ else
+ {
+ UNITY_OUTPUT_CHAR('0');
+ }
+ }
+ else
+ {
+ UNITY_OUTPUT_CHAR('X');
+ }
+ current_bit = current_bit >> 1;
+ }
+}
+
+/*-----------------------------------------------*/
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+/*
+ * This function prints a floating-point value in a format similar to
+ * printf("%.7g") on a single-precision machine or printf("%.9g") on a
+ * double-precision machine. The 7th digit won't always be totally correct
+ * in single-precision operation (for that level of accuracy, a more
+ * complicated algorithm would be needed).
+ */
+void UnityPrintFloat(const UNITY_DOUBLE input_number)
+{
+#ifdef UNITY_INCLUDE_DOUBLE
+ static const int sig_digits = 9;
+ static const UNITY_INT32 min_scaled = 100000000;
+ static const UNITY_INT32 max_scaled = 1000000000;
+#else
+ static const int sig_digits = 7;
+ static const UNITY_INT32 min_scaled = 1000000;
+ static const UNITY_INT32 max_scaled = 10000000;
+#endif
+
+ UNITY_DOUBLE number = input_number;
+
+ /* print minus sign (does not handle negative zero) */
+ if (number < 0.0f)
+ {
+ UNITY_OUTPUT_CHAR('-');
+ number = -number;
+ }
+
+ /* handle zero, NaN, and +/- infinity */
+ if (number == 0.0f)
+ {
+ UnityPrint("0");
+ }
+ else if (isnan(number))
+ {
+ UnityPrint("nan");
+ }
+ else if (isinf(number))
+ {
+ UnityPrint("inf");
+ }
+ else
+ {
+ UNITY_INT32 n_int = 0;
+ UNITY_INT32 n;
+ int exponent = 0;
+ int decimals;
+ int digits;
+ char buf[16] = {0};
+
+ /*
+ * Scale up or down by powers of 10. To minimize rounding error,
+ * start with a factor/divisor of 10^10, which is the largest
+ * power of 10 that can be represented exactly. Finally, compute
+ * (exactly) the remaining power of 10 and perform one more
+ * multiplication or division.
+ */
+ if (number < 1.0f)
+ {
+ UNITY_DOUBLE factor = 1.0f;
+
+ while (number < (UNITY_DOUBLE)max_scaled / 1e10f) { number *= 1e10f; exponent -= 10; }
+ while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; }
+
+ number *= factor;
+ }
+ else if (number > (UNITY_DOUBLE)max_scaled)
+ {
+ UNITY_DOUBLE divisor = 1.0f;
+
+ while (number > (UNITY_DOUBLE)min_scaled * 1e10f) { number /= 1e10f; exponent += 10; }
+ while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; }
+
+ number /= divisor;
+ }
+ else
+ {
+ /*
+ * In this range, we can split off the integer part before
+ * doing any multiplications. This reduces rounding error by
+ * freeing up significant bits in the fractional part.
+ */
+ UNITY_DOUBLE factor = 1.0f;
+ n_int = (UNITY_INT32)number;
+ number -= (UNITY_DOUBLE)n_int;
+
+ while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; }
+
+ number *= factor;
+ }
+
+ /* round to nearest integer */
+ n = ((UNITY_INT32)(number + number) + 1) / 2;
+
+#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO
+ /* round to even if exactly between two integers */
+ if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f))
+ n--;
+#endif
+
+ n += n_int;
+
+ if (n >= max_scaled)
+ {
+ n = min_scaled;
+ exponent++;
+ }
+
+ /* determine where to place decimal point */
+ decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1);
+ exponent += decimals;
+
+ /* truncate trailing zeroes after decimal point */
+ while ((decimals > 0) && ((n % 10) == 0))
+ {
+ n /= 10;
+ decimals--;
+ }
+
+ /* build up buffer in reverse order */
+ digits = 0;
+ while ((n != 0) || (digits <= decimals))
+ {
+ buf[digits++] = (char)('0' + n % 10);
+ n /= 10;
+ }
+
+ /* print out buffer (backwards) */
+ while (digits > 0)
+ {
+ if (digits == decimals)
+ {
+ UNITY_OUTPUT_CHAR('.');
+ }
+ UNITY_OUTPUT_CHAR(buf[--digits]);
+ }
+
+ /* print exponent if needed */
+ if (exponent != 0)
+ {
+ UNITY_OUTPUT_CHAR('e');
+
+ if (exponent < 0)
+ {
+ UNITY_OUTPUT_CHAR('-');
+ exponent = -exponent;
+ }
+ else
+ {
+ UNITY_OUTPUT_CHAR('+');
+ }
+
+ digits = 0;
+ while ((exponent != 0) || (digits < 2))
+ {
+ buf[digits++] = (char)('0' + exponent % 10);
+ exponent /= 10;
+ }
+ while (digits > 0)
+ {
+ UNITY_OUTPUT_CHAR(buf[--digits]);
+ }
+ }
+ }
+}
+#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */
+
+/*-----------------------------------------------*/
+static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line)
+{
+#ifdef UNITY_OUTPUT_FOR_ECLIPSE
+ UNITY_OUTPUT_CHAR('(');
+ UnityPrint(file);
+ UNITY_OUTPUT_CHAR(':');
+ UnityPrintNumber((UNITY_INT)line);
+ UNITY_OUTPUT_CHAR(')');
+ UNITY_OUTPUT_CHAR(' ');
+ UnityPrint(Unity.CurrentTestName);
+ UNITY_OUTPUT_CHAR(':');
+#else
+#ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH
+ UnityPrint("');
+ UnityPrint(Unity.CurrentTestName);
+ UnityPrint(" ");
+#else
+#ifdef UNITY_OUTPUT_FOR_QT_CREATOR
+ UnityPrint("file://");
+ UnityPrint(file);
+ UNITY_OUTPUT_CHAR(':');
+ UnityPrintNumber((UNITY_INT)line);
+ UNITY_OUTPUT_CHAR(' ');
+ UnityPrint(Unity.CurrentTestName);
+ UNITY_OUTPUT_CHAR(':');
+#else
+ UnityPrint(file);
+ UNITY_OUTPUT_CHAR(':');
+ UnityPrintNumber((UNITY_INT)line);
+ UNITY_OUTPUT_CHAR(':');
+ UnityPrint(Unity.CurrentTestName);
+ UNITY_OUTPUT_CHAR(':');
+#endif
+#endif
+#endif
+}
+
+/*-----------------------------------------------*/
+static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line)
+{
+ UnityTestResultsBegin(Unity.TestFile, line);
+ UnityPrint(UnityStrFail);
+ UNITY_OUTPUT_CHAR(':');
+}
+
+/*-----------------------------------------------*/
+void UnityConcludeTest(void)
+{
+ if (Unity.CurrentTestIgnored)
+ {
+ Unity.TestIgnores++;
+ }
+ else if (!Unity.CurrentTestFailed)
+ {
+ UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber);
+ UnityPrint(UnityStrPass);
+ }
+ else
+ {
+ Unity.TestFailures++;
+ }
+
+ Unity.CurrentTestFailed = 0;
+ Unity.CurrentTestIgnored = 0;
+ UNITY_PRINT_EXEC_TIME();
+ UNITY_PRINT_EOL();
+ UNITY_FLUSH_CALL();
+}
+
+/*-----------------------------------------------*/
+static void UnityAddMsgIfSpecified(const char* msg)
+{
+#ifdef UNITY_PRINT_TEST_CONTEXT
+ UnityPrint(UnityStrSpacer);
+ UNITY_PRINT_TEST_CONTEXT();
+#endif
+#ifndef UNITY_EXCLUDE_DETAILS
+ if (Unity.CurrentDetail1)
+ {
+ UnityPrint(UnityStrSpacer);
+ UnityPrint(UnityStrDetail1Name);
+ UnityPrint(Unity.CurrentDetail1);
+ if (Unity.CurrentDetail2)
+ {
+ UnityPrint(UnityStrDetail2Name);
+ UnityPrint(Unity.CurrentDetail2);
+ }
+ }
+#endif
+ if (msg)
+ {
+ UnityPrint(UnityStrSpacer);
+ UnityPrint(msg);
+ }
+}
+
+/*-----------------------------------------------*/
+static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual)
+{
+ UnityPrint(UnityStrExpected);
+ if (expected != NULL)
+ {
+ UNITY_OUTPUT_CHAR('\'');
+ UnityPrint(expected);
+ UNITY_OUTPUT_CHAR('\'');
+ }
+ else
+ {
+ UnityPrint(UnityStrNull);
+ }
+ UnityPrint(UnityStrWas);
+ if (actual != NULL)
+ {
+ UNITY_OUTPUT_CHAR('\'');
+ UnityPrint(actual);
+ UNITY_OUTPUT_CHAR('\'');
+ }
+ else
+ {
+ UnityPrint(UnityStrNull);
+ }
+}
+
+/*-----------------------------------------------*/
+static void UnityPrintExpectedAndActualStringsLen(const char* expected,
+ const char* actual,
+ const UNITY_UINT32 length)
+{
+ UnityPrint(UnityStrExpected);
+ if (expected != NULL)
+ {
+ UNITY_OUTPUT_CHAR('\'');
+ UnityPrintLen(expected, length);
+ UNITY_OUTPUT_CHAR('\'');
+ }
+ else
+ {
+ UnityPrint(UnityStrNull);
+ }
+ UnityPrint(UnityStrWas);
+ if (actual != NULL)
+ {
+ UNITY_OUTPUT_CHAR('\'');
+ UnityPrintLen(actual, length);
+ UNITY_OUTPUT_CHAR('\'');
+ }
+ else
+ {
+ UnityPrint(UnityStrNull);
+ }
+}
+
+/*-----------------------------------------------
+ * Assertion & Control Helpers
+ *-----------------------------------------------*/
+
+/*-----------------------------------------------*/
+static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected,
+ UNITY_INTERNAL_PTR actual,
+ const UNITY_LINE_TYPE lineNumber,
+ const char* msg)
+{
+ /* Both are NULL or same pointer */
+ if (expected == actual) { return 0; }
+
+ /* print and return true if just expected is NULL */
+ if (expected == NULL)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrNullPointerForExpected);
+ UnityAddMsgIfSpecified(msg);
+ return 1;
+ }
+
+ /* print and return true if just actual is NULL */
+ if (actual == NULL)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrNullPointerForActual);
+ UnityAddMsgIfSpecified(msg);
+ return 1;
+ }
+
+ return 0; /* return false if neither is NULL */
+}
+
+/*-----------------------------------------------
+ * Assertion Functions
+ *-----------------------------------------------*/
+
+/*-----------------------------------------------*/
+void UnityAssertBits(const UNITY_INT mask,
+ const UNITY_INT expected,
+ const UNITY_INT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if ((mask & expected) != (mask & actual))
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected);
+ UnityPrint(UnityStrWas);
+ UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualNumber(const UNITY_INT expected,
+ const UNITY_INT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (expected != actual)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ UnityPrintNumberByStyle(expected, style);
+ UnityPrint(UnityStrWas);
+ UnityPrintNumberByStyle(actual, style);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold,
+ const UNITY_INT actual,
+ const UNITY_COMPARISON_T compare,
+ const char *msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style)
+{
+ int failed = 0;
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if ((threshold == actual) && (compare & UNITY_EQUAL_TO)) { return; }
+ if ((threshold == actual)) { failed = 1; }
+
+ if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+ {
+ if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
+ if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
+ }
+ else /* UINT or HEX */
+ {
+ if (((UNITY_UINT)actual > (UNITY_UINT)threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
+ if (((UNITY_UINT)actual < (UNITY_UINT)threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
+ }
+
+ if (failed)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ UnityPrintNumberByStyle(actual, style);
+ if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); }
+ if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); }
+ if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); }
+ if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); }
+ UnityPrintNumberByStyle(threshold, style);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+#define UnityPrintPointlessAndBail() \
+do { \
+ UnityTestResultsFailBegin(lineNumber); \
+ UnityPrint(UnityStrPointless); \
+ UnityAddMsgIfSpecified(msg); \
+ UNITY_FAIL_AND_BAIL; \
+} while (0)
+
+/*-----------------------------------------------*/
+void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
+ UNITY_INTERNAL_PTR actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style,
+ const UNITY_FLAGS_T flags)
+{
+ UNITY_UINT32 elements = num_elements;
+ unsigned int length = style & 0xF;
+ unsigned int increment = 0;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (num_elements == 0)
+ {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+ UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+ UnityPrintPointlessAndBail();
+#endif
+ }
+
+ if (expected == actual)
+ {
+ return; /* Both are NULL or same pointer */
+ }
+
+ if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
+ {
+ UNITY_FAIL_AND_BAIL;
+ }
+
+ while ((elements > 0) && (elements--))
+ {
+ UNITY_INT expect_val;
+ UNITY_INT actual_val;
+
+ switch (length)
+ {
+ case 1:
+ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected;
+ actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual;
+ if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
+ {
+ expect_val &= 0x000000FF;
+ actual_val &= 0x000000FF;
+ }
+ increment = sizeof(UNITY_INT8);
+ break;
+
+ case 2:
+ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected;
+ actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual;
+ if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
+ {
+ expect_val &= 0x0000FFFF;
+ actual_val &= 0x0000FFFF;
+ }
+ increment = sizeof(UNITY_INT16);
+ break;
+
+#ifdef UNITY_SUPPORT_64
+ case 8:
+ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected;
+ actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual;
+ increment = sizeof(UNITY_INT64);
+ break;
+#endif
+
+ default: /* default is length 4 bytes */
+ case 4:
+ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected;
+ actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual;
+#ifdef UNITY_SUPPORT_64
+ if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX))
+ {
+ expect_val &= 0x00000000FFFFFFFF;
+ actual_val &= 0x00000000FFFFFFFF;
+ }
+#endif
+ increment = sizeof(UNITY_INT32);
+ length = 4;
+ break;
+ }
+
+ if (expect_val != actual_val)
+ {
+ if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8)))
+ { /* For UINT, remove sign extension (padding 1's) from signed type casts above */
+ UNITY_INT mask = 1;
+ mask = (mask << 8 * length) - 1;
+ expect_val &= mask;
+ actual_val &= mask;
+ }
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrElement);
+ UnityPrintNumberUnsigned(num_elements - elements - 1);
+ UnityPrint(UnityStrExpected);
+ UnityPrintNumberByStyle(expect_val, style);
+ UnityPrint(UnityStrWas);
+ UnityPrintNumberByStyle(actual_val, style);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+ /* Walk through array by incrementing the pointers */
+ if (flags == UNITY_ARRAY_TO_ARRAY)
+ {
+ expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment);
+ }
+ actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment);
+ }
+}
+
+/*-----------------------------------------------*/
+#ifndef UNITY_EXCLUDE_FLOAT
+/* Wrap this define in a function with variable types as float or double */
+#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \
+ if (isinf(expected) && isinf(actual) && (((expected) < 0) == ((actual) < 0))) return 1; \
+ if (UNITY_NAN_CHECK) return 1; \
+ (diff) = (actual) - (expected); \
+ if ((diff) < 0) (diff) = -(diff); \
+ if ((delta) < 0) (delta) = -(delta); \
+ return !(isnan(diff) || isinf(diff) || ((diff) > (delta)))
+ /* This first part of this condition will catch any NaN or Infinite values */
+#ifndef UNITY_NAN_NOT_EQUAL_NAN
+ #define UNITY_NAN_CHECK isnan(expected) && isnan(actual)
+#else
+ #define UNITY_NAN_CHECK 0
+#endif
+
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+ #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \
+ do { \
+ UnityPrint(UnityStrExpected); \
+ UnityPrintFloat(expected); \
+ UnityPrint(UnityStrWas); \
+ UnityPrintFloat(actual); \
+ } while (0)
+#else
+ #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \
+ UnityPrint(UnityStrDelta)
+#endif /* UNITY_EXCLUDE_FLOAT_PRINT */
+
+/*-----------------------------------------------*/
+static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual)
+{
+ UNITY_FLOAT diff;
+ UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff);
+}
+
+/*-----------------------------------------------*/
+void UnityAssertWithinFloatArray(const UNITY_FLOAT delta,
+ UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected,
+ UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLAGS_T flags)
+{
+ UNITY_UINT32 elements = num_elements;
+ UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected;
+ UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual;
+ UNITY_FLOAT in_delta = delta;
+ UNITY_FLOAT current_element_delta = delta;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (elements == 0)
+ {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+ UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+ UnityPrintPointlessAndBail();
+#endif
+ }
+
+ if (isinf(in_delta))
+ {
+ return; /* Arrays will be force equal with infinite delta */
+ }
+
+ if (isnan(in_delta))
+ {
+ /* Delta must be correct number */
+ UnityPrintPointlessAndBail();
+ }
+
+ if (expected == actual)
+ {
+ return; /* Both are NULL or same pointer */
+ }
+
+ if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
+ {
+ UNITY_FAIL_AND_BAIL;
+ }
+
+ /* fix delta sign if need */
+ if (in_delta < 0)
+ {
+ in_delta = -in_delta;
+ }
+
+ while (elements--)
+ {
+ current_element_delta = *ptr_expected * UNITY_FLOAT_PRECISION;
+
+ if (current_element_delta < 0)
+ {
+ /* fix delta sign for correct calculations */
+ current_element_delta = -current_element_delta;
+ }
+
+ if (!UnityFloatsWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual))
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrElement);
+ UnityPrintNumberUnsigned(num_elements - elements - 1);
+ UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+ if (flags == UNITY_ARRAY_TO_ARRAY)
+ {
+ ptr_expected++;
+ }
+ ptr_actual++;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertFloatsWithin(const UNITY_FLOAT delta,
+ const UNITY_FLOAT expected,
+ const UNITY_FLOAT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+
+ if (!UnityFloatsWithin(delta, expected, actual))
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta,
+ const UNITY_FLOAT expected,
+ const UNITY_FLOAT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (UnityFloatsWithin(delta, expected, actual))
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ UnityPrintFloat((UNITY_DOUBLE)expected);
+ UnityPrint(UnityStrNotEqual);
+ UnityPrintFloat((UNITY_DOUBLE)actual);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold,
+ const UNITY_FLOAT actual,
+ const UNITY_COMPARISON_T compare,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ int failed;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ failed = 0;
+
+ /* Checking for "not success" rather than failure to get the right result for NaN */
+ if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
+ if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
+
+ if ((compare & UNITY_EQUAL_TO) && UnityFloatsWithin(threshold * UNITY_FLOAT_PRECISION, threshold, actual)) { failed = 0; }
+
+ if (failed)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ UnityPrintFloat(actual);
+ if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); }
+ if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); }
+ if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); }
+ UnityPrintFloat(threshold);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLOAT_TRAIT_T style)
+{
+ const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet};
+ UNITY_INT should_be_trait = ((UNITY_INT)style & 1);
+ UNITY_INT is_trait = !should_be_trait;
+ UNITY_INT trait_index = (UNITY_INT)(style >> 1);
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ switch (style)
+ {
+ case UNITY_FLOAT_IS_INF:
+ case UNITY_FLOAT_IS_NOT_INF:
+ is_trait = isinf(actual) && (actual > 0);
+ break;
+ case UNITY_FLOAT_IS_NEG_INF:
+ case UNITY_FLOAT_IS_NOT_NEG_INF:
+ is_trait = isinf(actual) && (actual < 0);
+ break;
+
+ case UNITY_FLOAT_IS_NAN:
+ case UNITY_FLOAT_IS_NOT_NAN:
+ is_trait = isnan(actual) ? 1 : 0;
+ break;
+
+ case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */
+ case UNITY_FLOAT_IS_NOT_DET:
+ is_trait = !isinf(actual) && !isnan(actual);
+ break;
+
+ default: /* including UNITY_FLOAT_INVALID_TRAIT */
+ trait_index = 0;
+ trait_names[0] = UnityStrInvalidFloatTrait;
+ break;
+ }
+
+ if (is_trait != should_be_trait)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ if (!should_be_trait)
+ {
+ UnityPrint(UnityStrNot);
+ }
+ UnityPrint(trait_names[trait_index]);
+ UnityPrint(UnityStrWas);
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+ UnityPrintFloat((UNITY_DOUBLE)actual);
+#else
+ if (should_be_trait)
+ {
+ UnityPrint(UnityStrNot);
+ }
+ UnityPrint(trait_names[trait_index]);
+#endif
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+#endif /* not UNITY_EXCLUDE_FLOAT */
+
+/*-----------------------------------------------*/
+#ifndef UNITY_EXCLUDE_DOUBLE
+static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual)
+{
+ UNITY_DOUBLE diff;
+ UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff);
+}
+
+/*-----------------------------------------------*/
+void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta,
+ UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected,
+ UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLAGS_T flags)
+{
+ UNITY_UINT32 elements = num_elements;
+ UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected;
+ UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual;
+ UNITY_DOUBLE in_delta = delta;
+ UNITY_DOUBLE current_element_delta = delta;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (elements == 0)
+ {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+ UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+ UnityPrintPointlessAndBail();
+#endif
+ }
+
+ if (isinf(in_delta))
+ {
+ return; /* Arrays will be force equal with infinite delta */
+ }
+
+ if (isnan(in_delta))
+ {
+ /* Delta must be correct number */
+ UnityPrintPointlessAndBail();
+ }
+
+ if (expected == actual)
+ {
+ return; /* Both are NULL or same pointer */
+ }
+
+ if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
+ {
+ UNITY_FAIL_AND_BAIL;
+ }
+
+ /* fix delta sign if need */
+ if (in_delta < 0)
+ {
+ in_delta = -in_delta;
+ }
+
+ while (elements--)
+ {
+ current_element_delta = *ptr_expected * UNITY_DOUBLE_PRECISION;
+
+ if (current_element_delta < 0)
+ {
+ /* fix delta sign for correct calculations */
+ current_element_delta = -current_element_delta;
+ }
+
+ if (!UnityDoublesWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual))
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrElement);
+ UnityPrintNumberUnsigned(num_elements - elements - 1);
+ UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+ if (flags == UNITY_ARRAY_TO_ARRAY)
+ {
+ ptr_expected++;
+ }
+ ptr_actual++;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertDoublesWithin(const UNITY_DOUBLE delta,
+ const UNITY_DOUBLE expected,
+ const UNITY_DOUBLE actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (!UnityDoublesWithin(delta, expected, actual))
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta,
+ const UNITY_DOUBLE expected,
+ const UNITY_DOUBLE actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (UnityDoublesWithin(delta, expected, actual))
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ UnityPrintFloat((UNITY_DOUBLE)expected);
+ UnityPrint(UnityStrNotEqual);
+ UnityPrintFloat((UNITY_DOUBLE)actual);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold,
+ const UNITY_DOUBLE actual,
+ const UNITY_COMPARISON_T compare,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ int failed;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ failed = 0;
+
+ /* Checking for "not success" rather than failure to get the right result for NaN */
+ if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; }
+ if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; }
+
+ if ((compare & UNITY_EQUAL_TO) && UnityDoublesWithin(threshold * UNITY_DOUBLE_PRECISION, threshold, actual)) { failed = 0; }
+
+ if (failed)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ UnityPrintFloat(actual);
+ if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); }
+ if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); }
+ if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); }
+ UnityPrintFloat(threshold);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLOAT_TRAIT_T style)
+{
+ const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet};
+ UNITY_INT should_be_trait = ((UNITY_INT)style & 1);
+ UNITY_INT is_trait = !should_be_trait;
+ UNITY_INT trait_index = (UNITY_INT)(style >> 1);
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ switch (style)
+ {
+ case UNITY_FLOAT_IS_INF:
+ case UNITY_FLOAT_IS_NOT_INF:
+ is_trait = isinf(actual) && (actual > 0);
+ break;
+ case UNITY_FLOAT_IS_NEG_INF:
+ case UNITY_FLOAT_IS_NOT_NEG_INF:
+ is_trait = isinf(actual) && (actual < 0);
+ break;
+
+ case UNITY_FLOAT_IS_NAN:
+ case UNITY_FLOAT_IS_NOT_NAN:
+ is_trait = isnan(actual) ? 1 : 0;
+ break;
+
+ case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */
+ case UNITY_FLOAT_IS_NOT_DET:
+ is_trait = !isinf(actual) && !isnan(actual);
+ break;
+
+ default: /* including UNITY_FLOAT_INVALID_TRAIT */
+ trait_index = 0;
+ trait_names[0] = UnityStrInvalidFloatTrait;
+ break;
+ }
+
+ if (is_trait != should_be_trait)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrExpected);
+ if (!should_be_trait)
+ {
+ UnityPrint(UnityStrNot);
+ }
+ UnityPrint(trait_names[trait_index]);
+ UnityPrint(UnityStrWas);
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+ UnityPrintFloat(actual);
+#else
+ if (should_be_trait)
+ {
+ UnityPrint(UnityStrNot);
+ }
+ UnityPrint(trait_names[trait_index]);
+#endif
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+#endif /* not UNITY_EXCLUDE_DOUBLE */
+
+/*-----------------------------------------------*/
+void UnityAssertNumbersWithin(const UNITY_UINT delta,
+ const UNITY_INT expected,
+ const UNITY_INT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+ {
+ if (actual > expected)
+ {
+ Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta);
+ }
+ else
+ {
+ Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta);
+ }
+ }
+ else
+ {
+ if ((UNITY_UINT)actual > (UNITY_UINT)expected)
+ {
+ Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta);
+ }
+ else
+ {
+ Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta);
+ }
+ }
+
+ if (Unity.CurrentTestFailed)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrDelta);
+ UnityPrintNumberByStyle((UNITY_INT)delta, style);
+ UnityPrint(UnityStrExpected);
+ UnityPrintNumberByStyle(expected, style);
+ UnityPrint(UnityStrWas);
+ UnityPrintNumberByStyle(actual, style);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertNumbersArrayWithin(const UNITY_UINT delta,
+ UNITY_INTERNAL_PTR expected,
+ UNITY_INTERNAL_PTR actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style,
+ const UNITY_FLAGS_T flags)
+{
+ UNITY_UINT32 elements = num_elements;
+ unsigned int length = style & 0xF;
+ unsigned int increment = 0;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (num_elements == 0)
+ {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+ UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+ UnityPrintPointlessAndBail();
+#endif
+ }
+
+ if (expected == actual)
+ {
+ return; /* Both are NULL or same pointer */
+ }
+
+ if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
+ {
+ UNITY_FAIL_AND_BAIL;
+ }
+
+ while ((elements > 0) && (elements--))
+ {
+ UNITY_INT expect_val;
+ UNITY_INT actual_val;
+
+ switch (length)
+ {
+ case 1:
+ /* fixing problems with signed overflow on unsigned numbers */
+ if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+ {
+ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected;
+ actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual;
+ increment = sizeof(UNITY_INT8);
+ }
+ else
+ {
+ expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT8*)expected;
+ actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT8*)actual;
+ increment = sizeof(UNITY_UINT8);
+ }
+ break;
+
+ case 2:
+ /* fixing problems with signed overflow on unsigned numbers */
+ if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+ {
+ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected;
+ actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual;
+ increment = sizeof(UNITY_INT16);
+ }
+ else
+ {
+ expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT16*)expected;
+ actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT16*)actual;
+ increment = sizeof(UNITY_UINT16);
+ }
+ break;
+
+#ifdef UNITY_SUPPORT_64
+ case 8:
+ /* fixing problems with signed overflow on unsigned numbers */
+ if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+ {
+ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected;
+ actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual;
+ increment = sizeof(UNITY_INT64);
+ }
+ else
+ {
+ expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT64*)expected;
+ actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT64*)actual;
+ increment = sizeof(UNITY_UINT64);
+ }
+ break;
+#endif
+
+ default: /* default is length 4 bytes */
+ case 4:
+ /* fixing problems with signed overflow on unsigned numbers */
+ if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+ {
+ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected;
+ actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual;
+ increment = sizeof(UNITY_INT32);
+ }
+ else
+ {
+ expect_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT32*)expected;
+ actual_val = (UNITY_INT)*(UNITY_PTR_ATTRIBUTE const UNITY_UINT32*)actual;
+ increment = sizeof(UNITY_UINT32);
+ }
+ length = 4;
+ break;
+ }
+
+ if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT)
+ {
+ if (actual_val > expect_val)
+ {
+ Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta);
+ }
+ else
+ {
+ Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta);
+ }
+ }
+ else
+ {
+ if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val)
+ {
+ Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta);
+ }
+ else
+ {
+ Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta);
+ }
+ }
+
+ if (Unity.CurrentTestFailed)
+ {
+ if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8)))
+ { /* For UINT, remove sign extension (padding 1's) from signed type casts above */
+ UNITY_INT mask = 1;
+ mask = (mask << 8 * length) - 1;
+ expect_val &= mask;
+ actual_val &= mask;
+ }
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrDelta);
+ UnityPrintNumberByStyle((UNITY_INT)delta, style);
+ UnityPrint(UnityStrElement);
+ UnityPrintNumberUnsigned(num_elements - elements - 1);
+ UnityPrint(UnityStrExpected);
+ UnityPrintNumberByStyle(expect_val, style);
+ UnityPrint(UnityStrWas);
+ UnityPrintNumberByStyle(actual_val, style);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+ /* Walk through array by incrementing the pointers */
+ if (flags == UNITY_ARRAY_TO_ARRAY)
+ {
+ expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment);
+ }
+ actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment);
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualString(const char* expected,
+ const char* actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ UNITY_UINT32 i;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ /* if both pointers not null compare the strings */
+ if (expected && actual)
+ {
+ for (i = 0; expected[i] || actual[i]; i++)
+ {
+ if (expected[i] != actual[i])
+ {
+ Unity.CurrentTestFailed = 1;
+ break;
+ }
+ }
+ }
+ else
+ { /* handle case of one pointers being null (if both null, test should pass) */
+ if (expected != actual)
+ {
+ Unity.CurrentTestFailed = 1;
+ }
+ }
+
+ if (Unity.CurrentTestFailed)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrintExpectedAndActualStrings(expected, actual);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualStringLen(const char* expected,
+ const char* actual,
+ const UNITY_UINT32 length,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber)
+{
+ UNITY_UINT32 i;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ /* if both pointers not null compare the strings */
+ if (expected && actual)
+ {
+ for (i = 0; (i < length) && (expected[i] || actual[i]); i++)
+ {
+ if (expected[i] != actual[i])
+ {
+ Unity.CurrentTestFailed = 1;
+ break;
+ }
+ }
+ }
+ else
+ { /* handle case of one pointers being null (if both null, test should pass) */
+ if (expected != actual)
+ {
+ Unity.CurrentTestFailed = 1;
+ }
+ }
+
+ if (Unity.CurrentTestFailed)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrintExpectedAndActualStringsLen(expected, actual, length);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected,
+ const char** actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLAGS_T flags)
+{
+ UNITY_UINT32 i = 0;
+ UNITY_UINT32 j = 0;
+ const char* expd = NULL;
+ const char* act = NULL;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ /* if no elements, it's an error */
+ if (num_elements == 0)
+ {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+ UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+ UnityPrintPointlessAndBail();
+#endif
+ }
+
+ if ((const void*)expected == (const void*)actual)
+ {
+ return; /* Both are NULL or same pointer */
+ }
+
+ if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg))
+ {
+ UNITY_FAIL_AND_BAIL;
+ }
+
+ if (flags != UNITY_ARRAY_TO_ARRAY)
+ {
+ expd = (const char*)expected;
+ }
+
+ do
+ {
+ act = actual[j];
+ if (flags == UNITY_ARRAY_TO_ARRAY)
+ {
+ expd = ((const char* const*)expected)[j];
+ }
+
+ /* if both pointers not null compare the strings */
+ if (expd && act)
+ {
+ for (i = 0; expd[i] || act[i]; i++)
+ {
+ if (expd[i] != act[i])
+ {
+ Unity.CurrentTestFailed = 1;
+ break;
+ }
+ }
+ }
+ else
+ { /* handle case of one pointers being null (if both null, test should pass) */
+ if (expd != act)
+ {
+ Unity.CurrentTestFailed = 1;
+ }
+ }
+
+ if (Unity.CurrentTestFailed)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ if (num_elements > 1)
+ {
+ UnityPrint(UnityStrElement);
+ UnityPrintNumberUnsigned(j);
+ }
+ UnityPrintExpectedAndActualStrings(expd, act);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+ } while (++j < num_elements);
+}
+
+/*-----------------------------------------------*/
+void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected,
+ UNITY_INTERNAL_PTR actual,
+ const UNITY_UINT32 length,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLAGS_T flags)
+{
+ UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected;
+ UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual;
+ UNITY_UINT32 elements = num_elements;
+ UNITY_UINT32 bytes;
+
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ if (elements == 0)
+ {
+#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY
+ UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg);
+#else
+ UnityPrintPointlessAndBail();
+#endif
+ }
+ if (length == 0)
+ {
+ UnityPrintPointlessAndBail();
+ }
+
+ if (expected == actual)
+ {
+ return; /* Both are NULL or same pointer */
+ }
+
+ if (UnityIsOneArrayNull(expected, actual, lineNumber, msg))
+ {
+ UNITY_FAIL_AND_BAIL;
+ }
+
+ while (elements--)
+ {
+ bytes = length;
+ while (bytes--)
+ {
+ if (*ptr_exp != *ptr_act)
+ {
+ UnityTestResultsFailBegin(lineNumber);
+ UnityPrint(UnityStrMemory);
+ if (num_elements > 1)
+ {
+ UnityPrint(UnityStrElement);
+ UnityPrintNumberUnsigned(num_elements - elements - 1);
+ }
+ UnityPrint(UnityStrByte);
+ UnityPrintNumberUnsigned(length - bytes - 1);
+ UnityPrint(UnityStrExpected);
+ UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8);
+ UnityPrint(UnityStrWas);
+ UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8);
+ UnityAddMsgIfSpecified(msg);
+ UNITY_FAIL_AND_BAIL;
+ }
+ ptr_exp++;
+ ptr_act++;
+ }
+ if (flags == UNITY_ARRAY_TO_VAL)
+ {
+ ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected;
+ }
+ }
+}
+
+/*-----------------------------------------------*/
+
+static union
+{
+ UNITY_INT8 i8;
+ UNITY_INT16 i16;
+ UNITY_INT32 i32;
+#ifdef UNITY_SUPPORT_64
+ UNITY_INT64 i64;
+#endif
+#ifndef UNITY_EXCLUDE_FLOAT
+ float f;
+#endif
+#ifndef UNITY_EXCLUDE_DOUBLE
+ double d;
+#endif
+} UnityQuickCompare;
+
+UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size)
+{
+ switch(size)
+ {
+ case 1:
+ UnityQuickCompare.i8 = (UNITY_INT8)num;
+ return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8);
+
+ case 2:
+ UnityQuickCompare.i16 = (UNITY_INT16)num;
+ return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16);
+
+#ifdef UNITY_SUPPORT_64
+ case 8:
+ UnityQuickCompare.i64 = (UNITY_INT64)num;
+ return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64);
+#endif
+
+ default: /* 4 bytes */
+ UnityQuickCompare.i32 = (UNITY_INT32)num;
+ return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32);
+ }
+}
+
+#ifndef UNITY_EXCLUDE_FLOAT
+/*-----------------------------------------------*/
+UNITY_INTERNAL_PTR UnityFloatToPtr(const float num)
+{
+ UnityQuickCompare.f = num;
+ return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f);
+}
+#endif
+
+#ifndef UNITY_EXCLUDE_DOUBLE
+/*-----------------------------------------------*/
+UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num)
+{
+ UnityQuickCompare.d = num;
+ return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d);
+}
+#endif
+
+#ifdef UNITY_INCLUDE_PRINT_FORMATTED
+
+/*-----------------------------------------------
+ * printf length modifier helpers
+ *-----------------------------------------------*/
+
+enum UnityLengthModifier {
+ UNITY_LENGTH_MODIFIER_NONE,
+ UNITY_LENGTH_MODIFIER_LONG_LONG,
+ UNITY_LENGTH_MODIFIER_LONG,
+};
+
+#define UNITY_EXTRACT_ARG(NUMBER_T, NUMBER, LENGTH_MOD, VA, ARG_T) \
+do { \
+ switch (LENGTH_MOD) \
+ { \
+ case UNITY_LENGTH_MODIFIER_LONG_LONG: \
+ { \
+ NUMBER = (NUMBER_T)va_arg(VA, long long ARG_T); \
+ break; \
+ } \
+ case UNITY_LENGTH_MODIFIER_LONG: \
+ { \
+ NUMBER = (NUMBER_T)va_arg(VA, long ARG_T); \
+ break; \
+ } \
+ case UNITY_LENGTH_MODIFIER_NONE: \
+ default: \
+ { \
+ NUMBER = (NUMBER_T)va_arg(VA, ARG_T); \
+ break; \
+ } \
+ } \
+} while (0)
+
+static enum UnityLengthModifier UnityLengthModifierGet(const char *pch, int *length)
+{
+ enum UnityLengthModifier length_mod;
+ switch (pch[0])
+ {
+ case 'l':
+ {
+ if (pch[1] == 'l')
+ {
+ *length = 2;
+ length_mod = UNITY_LENGTH_MODIFIER_LONG_LONG;
+ }
+ else
+ {
+ *length = 1;
+ length_mod = UNITY_LENGTH_MODIFIER_LONG;
+ }
+ break;
+ }
+ case 'h':
+ {
+ // short and char are converted to int
+ length_mod = UNITY_LENGTH_MODIFIER_NONE;
+ if (pch[1] == 'h')
+ {
+ *length = 2;
+ }
+ else
+ {
+ *length = 1;
+ }
+ break;
+ }
+ case 'j':
+ case 'z':
+ case 't':
+ case 'L':
+ {
+ // Not supported, but should gobble up the length specifier anyway
+ length_mod = UNITY_LENGTH_MODIFIER_NONE;
+ *length = 1;
+ break;
+ }
+ default:
+ {
+ length_mod = UNITY_LENGTH_MODIFIER_NONE;
+ *length = 0;
+ }
+ }
+ return length_mod;
+}
+
+/*-----------------------------------------------
+ * printf helper function
+ *-----------------------------------------------*/
+static void UnityPrintFVA(const char* format, va_list va)
+{
+ const char* pch = format;
+ if (pch != NULL)
+ {
+ while (*pch)
+ {
+ /* format identification character */
+ if (*pch == '%')
+ {
+ pch++;
+
+ if (pch != NULL)
+ {
+ int length_mod_size;
+ enum UnityLengthModifier length_mod = UnityLengthModifierGet(pch, &length_mod_size);
+ pch += length_mod_size;
+
+ switch (*pch)
+ {
+ case 'd':
+ case 'i':
+ {
+ UNITY_INT number;
+ UNITY_EXTRACT_ARG(UNITY_INT, number, length_mod, va, int);
+ UnityPrintNumber((UNITY_INT)number);
+ break;
+ }
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+ case 'f':
+ case 'g':
+ {
+ const double number = va_arg(va, double);
+ UnityPrintFloat((UNITY_DOUBLE)number);
+ break;
+ }
+#endif
+ case 'u':
+ {
+ UNITY_UINT number;
+ UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
+ UnityPrintNumberUnsigned(number);
+ break;
+ }
+ case 'b':
+ {
+ UNITY_UINT number;
+ UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
+ const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1;
+ UNITY_OUTPUT_CHAR('0');
+ UNITY_OUTPUT_CHAR('b');
+ UnityPrintMask(mask, number);
+ break;
+ }
+ case 'x':
+ case 'X':
+ {
+ UNITY_UINT number;
+ UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int);
+ UNITY_OUTPUT_CHAR('0');
+ UNITY_OUTPUT_CHAR('x');
+ UnityPrintNumberHex(number, 8);
+ break;
+ }
+ case 'p':
+ {
+ const unsigned int number = va_arg(va, unsigned int);
+ UNITY_OUTPUT_CHAR('0');
+ UNITY_OUTPUT_CHAR('x');
+ UnityPrintNumberHex((UNITY_UINT)number, 8);
+ break;
+ }
+ case 'c':
+ {
+ const int ch = va_arg(va, int);
+ UnityPrintChar((const char *)&ch);
+ break;
+ }
+ case 's':
+ {
+ const char * string = va_arg(va, const char *);
+ UnityPrint(string);
+ break;
+ }
+ case '%':
+ {
+ UnityPrintChar(pch);
+ break;
+ }
+ default:
+ {
+ /* print the unknown format character */
+ UNITY_OUTPUT_CHAR('%');
+ UnityPrintChar(pch);
+ break;
+ }
+ }
+ }
+ }
+#ifdef UNITY_OUTPUT_COLOR
+ /* print ANSI escape code */
+ else if ((*pch == 27) && (*(pch + 1) == '['))
+ {
+ pch += UnityPrintAnsiEscapeString(pch);
+ continue;
+ }
+#endif
+ else if (*pch == '\n')
+ {
+ UNITY_PRINT_EOL();
+ }
+ else
+ {
+ UnityPrintChar(pch);
+ }
+
+ pch++;
+ }
+ }
+}
+
+void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...)
+{
+ UnityTestResultsBegin(Unity.TestFile, line);
+ UnityPrint("INFO");
+ if(format != NULL)
+ {
+ UnityPrint(": ");
+ va_list va;
+ va_start(va, format);
+ UnityPrintFVA(format, va);
+ va_end(va);
+ }
+ UNITY_PRINT_EOL();
+}
+#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */
+
+
+/*-----------------------------------------------
+ * Control Functions
+ *-----------------------------------------------*/
+
+/*-----------------------------------------------*/
+void UnityFail(const char* msg, const UNITY_LINE_TYPE line)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ UnityTestResultsBegin(Unity.TestFile, line);
+ UnityPrint(UnityStrFail);
+ if (msg != NULL)
+ {
+ UNITY_OUTPUT_CHAR(':');
+
+#ifdef UNITY_PRINT_TEST_CONTEXT
+ UNITY_PRINT_TEST_CONTEXT();
+#endif
+#ifndef UNITY_EXCLUDE_DETAILS
+ if (Unity.CurrentDetail1)
+ {
+ UnityPrint(UnityStrDetail1Name);
+ UnityPrint(Unity.CurrentDetail1);
+ if (Unity.CurrentDetail2)
+ {
+ UnityPrint(UnityStrDetail2Name);
+ UnityPrint(Unity.CurrentDetail2);
+ }
+ UnityPrint(UnityStrSpacer);
+ }
+#endif
+ if (msg[0] != ' ')
+ {
+ UNITY_OUTPUT_CHAR(' ');
+ }
+ UnityPrint(msg);
+ }
+
+ UNITY_FAIL_AND_BAIL;
+}
+
+/*-----------------------------------------------*/
+void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line)
+{
+ RETURN_IF_FAIL_OR_IGNORE;
+
+ UnityTestResultsBegin(Unity.TestFile, line);
+ UnityPrint(UnityStrIgnore);
+ if (msg != NULL)
+ {
+ UNITY_OUTPUT_CHAR(':');
+ UNITY_OUTPUT_CHAR(' ');
+ UnityPrint(msg);
+ }
+ UNITY_IGNORE_AND_BAIL;
+}
+
+/*-----------------------------------------------*/
+void UnityMessage(const char* msg, const UNITY_LINE_TYPE line)
+{
+ UnityTestResultsBegin(Unity.TestFile, line);
+ UnityPrint("INFO");
+ if (msg != NULL)
+ {
+ UNITY_OUTPUT_CHAR(':');
+ UNITY_OUTPUT_CHAR(' ');
+ UnityPrint(msg);
+ }
+ UNITY_PRINT_EOL();
+}
+
+/*-----------------------------------------------*/
+/* If we have not defined our own test runner, then include our default test runner to make life easier */
+#ifndef UNITY_SKIP_DEFAULT_RUNNER
+void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum)
+{
+ Unity.CurrentTestName = FuncName;
+ Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum;
+ Unity.NumberOfTests++;
+ UNITY_CLR_DETAILS();
+ UNITY_EXEC_TIME_START();
+ if (TEST_PROTECT())
+ {
+ setUp();
+ Func();
+ }
+ if (TEST_PROTECT())
+ {
+ tearDown();
+ }
+ UNITY_EXEC_TIME_STOP();
+ UnityConcludeTest();
+}
+#endif
+
+/*-----------------------------------------------*/
+void UnitySetTestFile(const char* filename)
+{
+ Unity.TestFile = filename;
+}
+
+/*-----------------------------------------------*/
+void UnityBegin(const char* filename)
+{
+ Unity.TestFile = filename;
+ Unity.CurrentTestName = NULL;
+ Unity.CurrentTestLineNumber = 0;
+ Unity.NumberOfTests = 0;
+ Unity.TestFailures = 0;
+ Unity.TestIgnores = 0;
+ Unity.CurrentTestFailed = 0;
+ Unity.CurrentTestIgnored = 0;
+
+ UNITY_CLR_DETAILS();
+ UNITY_OUTPUT_START();
+}
+
+/*-----------------------------------------------*/
+int UnityEnd(void)
+{
+ UNITY_PRINT_EOL();
+ UnityPrint(UnityStrBreaker);
+ UNITY_PRINT_EOL();
+ UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests));
+ UnityPrint(UnityStrResultsTests);
+ UnityPrintNumber((UNITY_INT)(Unity.TestFailures));
+ UnityPrint(UnityStrResultsFailures);
+ UnityPrintNumber((UNITY_INT)(Unity.TestIgnores));
+ UnityPrint(UnityStrResultsIgnored);
+ UNITY_PRINT_EOL();
+ if (Unity.TestFailures == 0U)
+ {
+ UnityPrint(UnityStrOk);
+ }
+ else
+ {
+ UnityPrint(UnityStrFail);
+#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL
+ UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D');
+#endif
+ }
+ UNITY_PRINT_EOL();
+ UNITY_FLUSH_CALL();
+ UNITY_OUTPUT_COMPLETE();
+ return (int)(Unity.TestFailures);
+}
+
+/*-----------------------------------------------
+ * Command Line Argument Support
+ *-----------------------------------------------*/
+#ifdef UNITY_USE_COMMAND_LINE_ARGS
+
+char* UnityOptionIncludeNamed = NULL;
+char* UnityOptionExcludeNamed = NULL;
+int UnityVerbosity = 1;
+
+/*-----------------------------------------------*/
+int UnityParseOptions(int argc, char** argv)
+{
+ int i;
+ UnityOptionIncludeNamed = NULL;
+ UnityOptionExcludeNamed = NULL;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'l': /* list tests */
+ return -1;
+ case 'n': /* include tests with name including this string */
+ case 'f': /* an alias for -n */
+ if (argv[i][2] == '=')
+ {
+ UnityOptionIncludeNamed = &argv[i][3];
+ }
+ else if (++i < argc)
+ {
+ UnityOptionIncludeNamed = argv[i];
+ }
+ else
+ {
+ UnityPrint("ERROR: No Test String to Include Matches For");
+ UNITY_PRINT_EOL();
+ return 1;
+ }
+ break;
+ case 'q': /* quiet */
+ UnityVerbosity = 0;
+ break;
+ case 'v': /* verbose */
+ UnityVerbosity = 2;
+ break;
+ case 'x': /* exclude tests with name including this string */
+ if (argv[i][2] == '=')
+ {
+ UnityOptionExcludeNamed = &argv[i][3];
+ }
+ else if (++i < argc)
+ {
+ UnityOptionExcludeNamed = argv[i];
+ }
+ else
+ {
+ UnityPrint("ERROR: No Test String to Exclude Matches For");
+ UNITY_PRINT_EOL();
+ return 1;
+ }
+ break;
+ default:
+ UnityPrint("ERROR: Unknown Option ");
+ UNITY_OUTPUT_CHAR(argv[i][1]);
+ UNITY_PRINT_EOL();
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------*/
+int IsStringInBiggerString(const char* longstring, const char* shortstring)
+{
+ const char* lptr = longstring;
+ const char* sptr = shortstring;
+ const char* lnext = lptr;
+
+ if (*sptr == '*')
+ {
+ return 1;
+ }
+
+ while (*lptr)
+ {
+ lnext = lptr + 1;
+
+ /* If they current bytes match, go on to the next bytes */
+ while (*lptr && *sptr && (*lptr == *sptr))
+ {
+ lptr++;
+ sptr++;
+
+ /* We're done if we match the entire string or up to a wildcard */
+ if (*sptr == '*')
+ return 1;
+ if (*sptr == ',')
+ return 1;
+ if (*sptr == '"')
+ return 1;
+ if (*sptr == '\'')
+ return 1;
+ if (*sptr == ':')
+ return 2;
+ if (*sptr == 0)
+ return 1;
+ }
+
+ /* Otherwise we start in the long pointer 1 character further and try again */
+ lptr = lnext;
+ sptr = shortstring;
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------*/
+int UnityStringArgumentMatches(const char* str)
+{
+ int retval;
+ const char* ptr1;
+ const char* ptr2;
+ const char* ptrf;
+
+ /* Go through the options and get the substrings for matching one at a time */
+ ptr1 = str;
+ while (ptr1[0] != 0)
+ {
+ if ((ptr1[0] == '"') || (ptr1[0] == '\''))
+ {
+ ptr1++;
+ }
+
+ /* look for the start of the next partial */
+ ptr2 = ptr1;
+ ptrf = 0;
+ do
+ {
+ ptr2++;
+ if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ','))
+ {
+ ptrf = &ptr2[1];
+ }
+ } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ','));
+
+ while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ',')))
+ {
+ ptr2++;
+ }
+
+ /* done if complete filename match */
+ retval = IsStringInBiggerString(Unity.TestFile, ptr1);
+ if (retval == 1)
+ {
+ return retval;
+ }
+
+ /* done if testname match after filename partial match */
+ if ((retval == 2) && (ptrf != 0))
+ {
+ if (IsStringInBiggerString(Unity.CurrentTestName, ptrf))
+ {
+ return 1;
+ }
+ }
+
+ /* done if complete testname match */
+ if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1)
+ {
+ return 1;
+ }
+
+ ptr1 = ptr2;
+ }
+
+ /* we couldn't find a match for any substrings */
+ return 0;
+}
+
+/*-----------------------------------------------*/
+int UnityTestMatches(void)
+{
+ /* Check if this test name matches the included test pattern */
+ int retval;
+ if (UnityOptionIncludeNamed)
+ {
+ retval = UnityStringArgumentMatches(UnityOptionIncludeNamed);
+ }
+ else
+ {
+ retval = 1;
+ }
+
+ /* Check if this test name matches the excluded test pattern */
+ if (UnityOptionExcludeNamed)
+ {
+ if (UnityStringArgumentMatches(UnityOptionExcludeNamed))
+ {
+ retval = 0;
+ }
+ }
+
+ return retval;
+}
+
+#endif /* UNITY_USE_COMMAND_LINE_ARGS */
+/*-----------------------------------------------*/
diff --git a/movement/lib/chirpy_tx/test/unity.h b/movement/lib/chirpy_tx/test/unity.h
new file mode 100644
index 00000000..e321a1da
--- /dev/null
+++ b/movement/lib/chirpy_tx/test/unity.h
@@ -0,0 +1,687 @@
+/* ==========================================
+ Unity Project - A Test Framework for C
+ Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
+ [Released under MIT License. Please refer to license.txt for details]
+========================================== */
+
+#ifndef UNITY_FRAMEWORK_H
+#define UNITY_FRAMEWORK_H
+#define UNITY
+
+#define UNITY_VERSION_MAJOR 2
+#define UNITY_VERSION_MINOR 5
+#define UNITY_VERSION_BUILD 4
+#define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "unity_internals.h"
+
+/*-------------------------------------------------------
+ * Test Setup / Teardown
+ *-------------------------------------------------------*/
+
+/* These functions are intended to be called before and after each test.
+ * If using unity directly, these will need to be provided for each test
+ * executable built. If you are using the test runner generator and/or
+ * Ceedling, these are optional. */
+void setUp(void);
+void tearDown(void);
+
+/* These functions are intended to be called at the beginning and end of an
+ * entire test suite. suiteTearDown() is passed the number of tests that
+ * failed, and its return value becomes the exit code of main(). If using
+ * Unity directly, you're in charge of calling these if they are desired.
+ * If using Ceedling or the test runner generator, these will be called
+ * automatically if they exist. */
+void suiteSetUp(void);
+int suiteTearDown(int num_failures);
+
+/*-------------------------------------------------------
+ * Test Reset and Verify
+ *-------------------------------------------------------*/
+
+/* These functions are intended to be called before during tests in order
+ * to support complex test loops, etc. Both are NOT built into Unity. Instead
+ * the test runner generator will create them. resetTest will run teardown and
+ * setup again, verifying any end-of-test needs between. verifyTest will only
+ * run the verification. */
+void resetTest(void);
+void verifyTest(void);
+
+/*-------------------------------------------------------
+ * Configuration Options
+ *-------------------------------------------------------
+ * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above.
+
+ * Integers/longs/pointers
+ * - Unity attempts to automatically discover your integer sizes
+ * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in
+ * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in
+ * - If you cannot use the automatic methods above, you can force Unity by using these options:
+ * - define UNITY_SUPPORT_64
+ * - set UNITY_INT_WIDTH
+ * - set UNITY_LONG_WIDTH
+ * - set UNITY_POINTER_WIDTH
+
+ * Floats
+ * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons
+ * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT
+ * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats
+ * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons
+ * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default)
+ * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE
+ * - define UNITY_DOUBLE_TYPE to specify something other than double
+ * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors
+
+ * Output
+ * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired
+ * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure
+
+ * Optimization
+ * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge
+ * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests.
+
+ * Test Cases
+ * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script
+
+ * Parameterized Tests
+ * - you'll want to create a define of TEST_CASE(...) and/or TEST_RANGE(...) which basically evaluates to nothing
+
+ * Tests with Arguments
+ * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity
+
+ *-------------------------------------------------------
+ * Basic Fail and Ignore
+ *-------------------------------------------------------*/
+
+#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message))
+#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL)
+#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message))
+#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL)
+#define TEST_MESSAGE(message) UnityMessage((message), __LINE__)
+#define TEST_ONLY()
+#ifdef UNITY_INCLUDE_PRINT_FORMATTED
+#define TEST_PRINTF(message, ...) UnityPrintF(__LINE__, (message), __VA_ARGS__)
+#endif
+
+/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails.
+ * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */
+#define TEST_PASS() TEST_ABORT()
+#define TEST_PASS_MESSAGE(message) do { UnityMessage((message), __LINE__); TEST_ABORT(); } while (0)
+
+/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out
+ * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */
+#define TEST_FILE(a)
+
+/*-------------------------------------------------------
+ * Test Asserts (simple)
+ *-------------------------------------------------------*/
+
+/* Boolean */
+#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE")
+#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE")
+#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE")
+#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE")
+#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL")
+#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL")
+#define TEST_ASSERT_EMPTY(pointer) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, " Expected Empty")
+#define TEST_ASSERT_NOT_EMPTY(pointer) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty")
+
+/* Integers (of all sizes) */
+#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_size_t(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL)
+#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL)
+
+/* Integer Not Equal To (of all sizes) */
+#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
+
+/* Integer Greater Than/ Less Than (of all sizes) */
+#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, NULL)
+
+#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, NULL)
+
+#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
+
+#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL)
+
+/* Integer Ranges (of all sizes) */
+#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_size_t_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_CHAR_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+
+/* Integer Array Ranges (of all sizes) */
+#define TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_size_t_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+#define TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL)
+
+
+/* Structs and Strings */
+#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL)
+
+/* Arrays */
+#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_size_t_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+
+/* Arrays Compared To Single Value */
+#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_size_t(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, NULL)
+
+/* Floating Point (If Enabled) */
+#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual) UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual) UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL)
+#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL)
+
+/* Double (If Enabled) */
+#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL)
+#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL)
+
+/* Shorthand */
+#ifdef UNITY_SHORTHAND_AS_OLD
+#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal")
+#endif
+#ifdef UNITY_SHORTHAND_AS_INT
+#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+#ifdef UNITY_SHORTHAND_AS_MEM
+#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, NULL)
+#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+#ifdef UNITY_SHORTHAND_AS_RAW
+#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, " Expected Equal")
+#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal")
+#endif
+#ifdef UNITY_SHORTHAND_AS_NONE
+#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+
+/*-------------------------------------------------------
+ * Test Asserts (with additional messages)
+ *-------------------------------------------------------*/
+
+/* Boolean */
+#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message))
+#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message))
+#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message))
+#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message))
+#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message))
+#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message))
+#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, (message))
+#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message))
+
+/* Integers (of all sizes) */
+#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_size_t_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message))
+#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message))
+#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message))
+#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message))
+
+/* Integer Not Equal To (of all sizes) */
+#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
+
+
+/* Integer Greater Than/ Less Than (of all sizes) */
+#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, (message))
+
+#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, (message))
+
+#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
+
+#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message))
+
+/* Integer Ranges (of all sizes) */
+#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_size_t_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_CHAR_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, (message))
+
+/* Integer Array Ranges (of all sizes) */
+#define TEST_ASSERT_INT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_INT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_INT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_INT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_INT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_UINT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_size_t_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_HEX64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+#define TEST_ASSERT_CHAR_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message))
+
+
+/* Structs and Strings */
+#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message))
+
+/* Arrays */
+#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_size_t_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_CHAR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+
+/* Arrays Compared To Single Value*/
+#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_size_t_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_CHAR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, (message))
+
+/* Floating Point (If Enabled) */
+#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message))
+#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message))
+
+/* Double (If Enabled) */
+#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message))
+#define TEST_ASSERT_GREATER_THAN_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_THAN_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message))
+#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message))
+
+/* Shorthand */
+#ifdef UNITY_SHORTHAND_AS_OLD
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message))
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message))
+#endif
+#ifdef UNITY_SHORTHAND_AS_INT
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, message)
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+#ifdef UNITY_SHORTHAND_AS_MEM
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, message)
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+#ifdef UNITY_SHORTHAND_AS_RAW
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, message)
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, message)
+#endif
+#ifdef UNITY_SHORTHAND_AS_NONE
+#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand)
+#endif
+
+/* end of UNITY_FRAMEWORK_H */
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/movement/lib/chirpy_tx/test/unity_internals.h b/movement/lib/chirpy_tx/test/unity_internals.h
new file mode 100644
index 00000000..47bd370d
--- /dev/null
+++ b/movement/lib/chirpy_tx/test/unity_internals.h
@@ -0,0 +1,1145 @@
+/* ==========================================
+ Unity Project - A Test Framework for C
+ Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams
+ [Released under MIT License. Please refer to license.txt for details]
+========================================== */
+
+#ifndef UNITY_INTERNALS_H
+#define UNITY_INTERNALS_H
+
+#ifdef UNITY_INCLUDE_CONFIG_H
+#include "unity_config.h"
+#endif
+
+#ifndef UNITY_EXCLUDE_SETJMP_H
+#include
+#endif
+
+#ifndef UNITY_EXCLUDE_MATH_H
+#include
+#endif
+
+#ifndef UNITY_EXCLUDE_STDDEF_H
+#include
+#endif
+
+#ifdef UNITY_INCLUDE_PRINT_FORMATTED
+#include
+#endif
+
+/* Unity Attempts to Auto-Detect Integer Types
+ * Attempt 1: UINT_MAX, ULONG_MAX in , or default to 32 bits
+ * Attempt 2: UINTPTR_MAX in , or default to same size as long
+ * The user may override any of these derived constants:
+ * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */
+#ifndef UNITY_EXCLUDE_STDINT_H
+#include
+#endif
+
+#ifndef UNITY_EXCLUDE_LIMITS_H
+#include
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+ #define UNITY_FUNCTION_ATTR(a) __attribute__((a))
+#else
+ #define UNITY_FUNCTION_ATTR(a) /* ignore */
+#endif
+
+#ifndef UNITY_NORETURN
+ #if defined(__cplusplus)
+ #if __cplusplus >= 201103L
+ #define UNITY_NORETURN [[ noreturn ]]
+ #endif
+ #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+ #if defined(_WIN32) && defined(_MSC_VER)
+ /* We are using MSVC compiler on Windows platform. */
+ /* Not all Windows SDKs supports , but compiler can support C11: */
+ /* https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/ */
+ /* Not sure, that Mingw compilers has Windows SDK headers at all. */
+ #include
+ #endif
+
+ /* Using Windows SDK predefined macro for detecting supported SDK with MSVC compiler. */
+ /* Mingw GCC should work without that fixes. */
+ /* Based on: */
+ /* https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170 */
+ /* NTDDI_WIN10_FE is equal to Windows 10 SDK 2104 */
+ #if defined(_MSC_VER) && ((!defined(NTDDI_WIN10_FE)) || WDK_NTDDI_VERSION < NTDDI_WIN10_FE)
+ /* Based on tests and: */
+ /* https://docs.microsoft.com/en-us/cpp/c-language/noreturn?view=msvc-170 */
+ /* https://en.cppreference.com/w/c/language/_Noreturn */
+ #define UNITY_NORETURN _Noreturn
+ #else /* Using newer Windows SDK or not MSVC compiler */
+ #include
+ #define UNITY_NORETURN noreturn
+ #endif
+ #endif
+#endif
+#ifndef UNITY_NORETURN
+ #define UNITY_NORETURN UNITY_FUNCTION_ATTR(noreturn)
+#endif
+
+/*-------------------------------------------------------
+ * Guess Widths If Not Specified
+ *-------------------------------------------------------*/
+
+/* Determine the size of an int, if not already specified.
+ * We cannot use sizeof(int), because it is not yet defined
+ * at this stage in the translation of the C program.
+ * Also sizeof(int) does return the size in addressable units on all platforms,
+ * which may not necessarily be the size in bytes.
+ * Therefore, infer it from UINT_MAX if possible. */
+#ifndef UNITY_INT_WIDTH
+ #ifdef UINT_MAX
+ #if (UINT_MAX == 0xFFFF)
+ #define UNITY_INT_WIDTH (16)
+ #elif (UINT_MAX == 0xFFFFFFFF)
+ #define UNITY_INT_WIDTH (32)
+ #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF)
+ #define UNITY_INT_WIDTH (64)
+ #endif
+ #else /* Set to default */
+ #define UNITY_INT_WIDTH (32)
+ #endif /* UINT_MAX */
+#endif
+
+/* Determine the size of a long, if not already specified. */
+#ifndef UNITY_LONG_WIDTH
+ #ifdef ULONG_MAX
+ #if (ULONG_MAX == 0xFFFF)
+ #define UNITY_LONG_WIDTH (16)
+ #elif (ULONG_MAX == 0xFFFFFFFF)
+ #define UNITY_LONG_WIDTH (32)
+ #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF)
+ #define UNITY_LONG_WIDTH (64)
+ #endif
+ #else /* Set to default */
+ #define UNITY_LONG_WIDTH (32)
+ #endif /* ULONG_MAX */
+#endif
+
+/* Determine the size of a pointer, if not already specified. */
+#ifndef UNITY_POINTER_WIDTH
+ #ifdef UINTPTR_MAX
+ #if (UINTPTR_MAX <= 0xFFFF)
+ #define UNITY_POINTER_WIDTH (16)
+ #elif (UINTPTR_MAX <= 0xFFFFFFFF)
+ #define UNITY_POINTER_WIDTH (32)
+ #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF)
+ #define UNITY_POINTER_WIDTH (64)
+ #endif
+ #else /* Set to default */
+ #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH
+ #endif /* UINTPTR_MAX */
+#endif
+
+/*-------------------------------------------------------
+ * Int Support (Define types based on detected sizes)
+ *-------------------------------------------------------*/
+
+#if (UNITY_INT_WIDTH == 32)
+ typedef unsigned char UNITY_UINT8;
+ typedef unsigned short UNITY_UINT16;
+ typedef unsigned int UNITY_UINT32;
+ typedef signed char UNITY_INT8;
+ typedef signed short UNITY_INT16;
+ typedef signed int UNITY_INT32;
+#elif (UNITY_INT_WIDTH == 16)
+ typedef unsigned char UNITY_UINT8;
+ typedef unsigned int UNITY_UINT16;
+ typedef unsigned long UNITY_UINT32;
+ typedef signed char UNITY_INT8;
+ typedef signed int UNITY_INT16;
+ typedef signed long UNITY_INT32;
+#else
+ #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported)
+#endif
+
+/*-------------------------------------------------------
+ * 64-bit Support
+ *-------------------------------------------------------*/
+
+/* Auto-detect 64 Bit Support */
+#ifndef UNITY_SUPPORT_64
+ #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64
+ #define UNITY_SUPPORT_64
+ #endif
+#endif
+
+/* 64-Bit Support Dependent Configuration */
+#ifndef UNITY_SUPPORT_64
+ /* No 64-bit Support */
+ typedef UNITY_UINT32 UNITY_UINT;
+ typedef UNITY_INT32 UNITY_INT;
+ #define UNITY_MAX_NIBBLES (8) /* Maximum number of nibbles in a UNITY_(U)INT */
+#else
+ /* 64-bit Support */
+ #if (UNITY_LONG_WIDTH == 32)
+ typedef unsigned long long UNITY_UINT64;
+ typedef signed long long UNITY_INT64;
+ #elif (UNITY_LONG_WIDTH == 64)
+ typedef unsigned long UNITY_UINT64;
+ typedef signed long UNITY_INT64;
+ #else
+ #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported)
+ #endif
+ typedef UNITY_UINT64 UNITY_UINT;
+ typedef UNITY_INT64 UNITY_INT;
+ #define UNITY_MAX_NIBBLES (16) /* Maximum number of nibbles in a UNITY_(U)INT */
+#endif
+
+/*-------------------------------------------------------
+ * Pointer Support
+ *-------------------------------------------------------*/
+
+#if (UNITY_POINTER_WIDTH == 32)
+ #define UNITY_PTR_TO_INT UNITY_INT32
+ #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32
+#elif (UNITY_POINTER_WIDTH == 64)
+ #define UNITY_PTR_TO_INT UNITY_INT64
+ #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64
+#elif (UNITY_POINTER_WIDTH == 16)
+ #define UNITY_PTR_TO_INT UNITY_INT16
+ #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16
+#else
+ #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported)
+#endif
+
+#ifndef UNITY_PTR_ATTRIBUTE
+ #define UNITY_PTR_ATTRIBUTE
+#endif
+
+#ifndef UNITY_INTERNAL_PTR
+ #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void*
+#endif
+
+/* optionally define UNITY_COMPARE_PTRS_ON_ZERO_ARRAY */
+
+/*-------------------------------------------------------
+ * Float Support
+ *-------------------------------------------------------*/
+
+#ifdef UNITY_EXCLUDE_FLOAT
+
+/* No Floating Point Support */
+#ifndef UNITY_EXCLUDE_DOUBLE
+#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */
+#endif
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+#define UNITY_EXCLUDE_FLOAT_PRINT
+#endif
+
+#else
+
+/* Floating Point Support */
+#ifndef UNITY_FLOAT_PRECISION
+#define UNITY_FLOAT_PRECISION (0.00001f)
+#endif
+#ifndef UNITY_FLOAT_TYPE
+#define UNITY_FLOAT_TYPE float
+#endif
+typedef UNITY_FLOAT_TYPE UNITY_FLOAT;
+
+/* isinf & isnan macros should be provided by math.h */
+#ifndef isinf
+/* The value of Inf - Inf is NaN */
+#define isinf(n) (isnan((n) - (n)) && !isnan(n))
+#endif
+
+#ifndef isnan
+/* NaN is the only floating point value that does NOT equal itself.
+ * Therefore if n != n, then it is NaN. */
+#define isnan(n) ((n != n) ? 1 : 0)
+#endif
+
+#endif
+
+/*-------------------------------------------------------
+ * Double Float Support
+ *-------------------------------------------------------*/
+
+/* unlike float, we DON'T include by default */
+#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE)
+
+ /* No Floating Point Support */
+ #ifndef UNITY_EXCLUDE_DOUBLE
+ #define UNITY_EXCLUDE_DOUBLE
+ #else
+ #undef UNITY_INCLUDE_DOUBLE
+ #endif
+
+ #ifndef UNITY_EXCLUDE_FLOAT
+ #ifndef UNITY_DOUBLE_TYPE
+ #define UNITY_DOUBLE_TYPE double
+ #endif
+ typedef UNITY_FLOAT UNITY_DOUBLE;
+ /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */
+ #endif
+
+#else
+
+ /* Double Floating Point Support */
+ #ifndef UNITY_DOUBLE_PRECISION
+ #define UNITY_DOUBLE_PRECISION (1e-12)
+ #endif
+
+ #ifndef UNITY_DOUBLE_TYPE
+ #define UNITY_DOUBLE_TYPE double
+ #endif
+ typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE;
+
+#endif
+
+/*-------------------------------------------------------
+ * Output Method: stdout (DEFAULT)
+ *-------------------------------------------------------*/
+#ifndef UNITY_OUTPUT_CHAR
+ /* Default to using putchar, which is defined in stdio.h */
+ #include
+ #define UNITY_OUTPUT_CHAR(a) (void)putchar(a)
+#else
+ /* If defined as something else, make sure we declare it here so it's ready for use */
+ #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION
+ extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION;
+ #endif
+#endif
+
+#ifndef UNITY_OUTPUT_FLUSH
+ #ifdef UNITY_USE_FLUSH_STDOUT
+ /* We want to use the stdout flush utility */
+ #include
+ #define UNITY_OUTPUT_FLUSH() (void)fflush(stdout)
+ #else
+ /* We've specified nothing, therefore flush should just be ignored */
+ #define UNITY_OUTPUT_FLUSH() (void)0
+ #endif
+#else
+ /* If defined as something else, make sure we declare it here so it's ready for use */
+ #ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION
+ extern void UNITY_OUTPUT_FLUSH_HEADER_DECLARATION;
+ #endif
+#endif
+
+#ifndef UNITY_OUTPUT_FLUSH
+#define UNITY_FLUSH_CALL()
+#else
+#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH()
+#endif
+
+#ifndef UNITY_PRINT_EOL
+#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n')
+#endif
+
+#ifndef UNITY_OUTPUT_START
+#define UNITY_OUTPUT_START()
+#endif
+
+#ifndef UNITY_OUTPUT_COMPLETE
+#define UNITY_OUTPUT_COMPLETE()
+#endif
+
+#ifdef UNITY_INCLUDE_EXEC_TIME
+ #if !defined(UNITY_EXEC_TIME_START) && \
+ !defined(UNITY_EXEC_TIME_STOP) && \
+ !defined(UNITY_PRINT_EXEC_TIME) && \
+ !defined(UNITY_TIME_TYPE)
+ /* If none any of these macros are defined then try to provide a default implementation */
+
+ #if defined(UNITY_CLOCK_MS)
+ /* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */
+ #define UNITY_TIME_TYPE UNITY_UINT
+ #define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS()
+ #define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS()
+ #define UNITY_PRINT_EXEC_TIME() { \
+ UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
+ UnityPrint(" ("); \
+ UnityPrintNumberUnsigned(execTimeMs); \
+ UnityPrint(" ms)"); \
+ }
+ #elif defined(_WIN32)
+ #include
+ #define UNITY_TIME_TYPE clock_t
+ #define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC)
+ #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
+ #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime)
+ #define UNITY_PRINT_EXEC_TIME() { \
+ UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
+ UnityPrint(" ("); \
+ UnityPrintNumberUnsigned(execTimeMs); \
+ UnityPrint(" ms)"); \
+ }
+ #elif defined(__unix__) || defined(__APPLE__)
+ #include
+ #define UNITY_TIME_TYPE struct timespec
+ #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t)
+ #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
+ #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime)
+ #define UNITY_PRINT_EXEC_TIME() { \
+ UNITY_UINT execTimeMs = ((Unity.CurrentTestStopTime.tv_sec - Unity.CurrentTestStartTime.tv_sec) * 1000L); \
+ execTimeMs += ((Unity.CurrentTestStopTime.tv_nsec - Unity.CurrentTestStartTime.tv_nsec) / 1000000L); \
+ UnityPrint(" ("); \
+ UnityPrintNumberUnsigned(execTimeMs); \
+ UnityPrint(" ms)"); \
+ }
+ #endif
+ #endif
+#endif
+
+#ifndef UNITY_EXEC_TIME_START
+#define UNITY_EXEC_TIME_START() do { /* nothing*/ } while (0)
+#endif
+
+#ifndef UNITY_EXEC_TIME_STOP
+#define UNITY_EXEC_TIME_STOP() do { /* nothing*/ } while (0)
+#endif
+
+#ifndef UNITY_TIME_TYPE
+#define UNITY_TIME_TYPE UNITY_UINT
+#endif
+
+#ifndef UNITY_PRINT_EXEC_TIME
+#define UNITY_PRINT_EXEC_TIME() do { /* nothing*/ } while (0)
+#endif
+
+/*-------------------------------------------------------
+ * Footprint
+ *-------------------------------------------------------*/
+
+#ifndef UNITY_LINE_TYPE
+#define UNITY_LINE_TYPE UNITY_UINT
+#endif
+
+#ifndef UNITY_COUNTER_TYPE
+#define UNITY_COUNTER_TYPE UNITY_UINT
+#endif
+
+/*-------------------------------------------------------
+ * Internal Structs Needed
+ *-------------------------------------------------------*/
+
+typedef void (*UnityTestFunction)(void);
+
+#define UNITY_DISPLAY_RANGE_INT (0x10)
+#define UNITY_DISPLAY_RANGE_UINT (0x20)
+#define UNITY_DISPLAY_RANGE_HEX (0x40)
+#define UNITY_DISPLAY_RANGE_CHAR (0x80)
+
+typedef enum
+{
+ UNITY_DISPLAY_STYLE_INT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_INT,
+ UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT,
+ UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT,
+ UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT,
+#ifdef UNITY_SUPPORT_64
+ UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT,
+#endif
+
+ UNITY_DISPLAY_STYLE_UINT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_UINT,
+ UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT,
+ UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT,
+ UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT,
+#ifdef UNITY_SUPPORT_64
+ UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT,
+#endif
+
+ UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX,
+ UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX,
+ UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX,
+#ifdef UNITY_SUPPORT_64
+ UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX,
+#endif
+
+ UNITY_DISPLAY_STYLE_CHAR = 1 + UNITY_DISPLAY_RANGE_CHAR + UNITY_DISPLAY_RANGE_INT,
+
+ UNITY_DISPLAY_STYLE_UNKNOWN
+} UNITY_DISPLAY_STYLE_T;
+
+typedef enum
+{
+ UNITY_WITHIN = 0x0,
+ UNITY_EQUAL_TO = 0x1,
+ UNITY_GREATER_THAN = 0x2,
+ UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO,
+ UNITY_SMALLER_THAN = 0x4,
+ UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO,
+ UNITY_NOT_EQUAL = 0x0,
+ UNITY_UNKNOWN
+} UNITY_COMPARISON_T;
+
+#ifndef UNITY_EXCLUDE_FLOAT
+typedef enum UNITY_FLOAT_TRAIT
+{
+ UNITY_FLOAT_IS_NOT_INF = 0,
+ UNITY_FLOAT_IS_INF,
+ UNITY_FLOAT_IS_NOT_NEG_INF,
+ UNITY_FLOAT_IS_NEG_INF,
+ UNITY_FLOAT_IS_NOT_NAN,
+ UNITY_FLOAT_IS_NAN,
+ UNITY_FLOAT_IS_NOT_DET,
+ UNITY_FLOAT_IS_DET,
+ UNITY_FLOAT_INVALID_TRAIT
+} UNITY_FLOAT_TRAIT_T;
+#endif
+
+typedef enum
+{
+ UNITY_ARRAY_TO_VAL = 0,
+ UNITY_ARRAY_TO_ARRAY,
+ UNITY_ARRAY_UNKNOWN
+} UNITY_FLAGS_T;
+
+struct UNITY_STORAGE_T
+{
+ const char* TestFile;
+ const char* CurrentTestName;
+#ifndef UNITY_EXCLUDE_DETAILS
+ const char* CurrentDetail1;
+ const char* CurrentDetail2;
+#endif
+ UNITY_LINE_TYPE CurrentTestLineNumber;
+ UNITY_COUNTER_TYPE NumberOfTests;
+ UNITY_COUNTER_TYPE TestFailures;
+ UNITY_COUNTER_TYPE TestIgnores;
+ UNITY_COUNTER_TYPE CurrentTestFailed;
+ UNITY_COUNTER_TYPE CurrentTestIgnored;
+#ifdef UNITY_INCLUDE_EXEC_TIME
+ UNITY_TIME_TYPE CurrentTestStartTime;
+ UNITY_TIME_TYPE CurrentTestStopTime;
+#endif
+#ifndef UNITY_EXCLUDE_SETJMP_H
+ jmp_buf AbortFrame;
+#endif
+};
+
+extern struct UNITY_STORAGE_T Unity;
+
+/*-------------------------------------------------------
+ * Test Suite Management
+ *-------------------------------------------------------*/
+
+void UnityBegin(const char* filename);
+int UnityEnd(void);
+void UnitySetTestFile(const char* filename);
+void UnityConcludeTest(void);
+
+#ifndef RUN_TEST
+void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum);
+#else
+#define UNITY_SKIP_DEFAULT_RUNNER
+#endif
+
+/*-------------------------------------------------------
+ * Details Support
+ *-------------------------------------------------------*/
+
+#ifdef UNITY_EXCLUDE_DETAILS
+#define UNITY_CLR_DETAILS()
+#define UNITY_SET_DETAIL(d1)
+#define UNITY_SET_DETAILS(d1,d2)
+#else
+#define UNITY_CLR_DETAILS() do { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } while (0)
+#define UNITY_SET_DETAIL(d1) do { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = 0; } while (0)
+#define UNITY_SET_DETAILS(d1,d2) do { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = (d2); } while (0)
+
+#ifndef UNITY_DETAIL1_NAME
+#define UNITY_DETAIL1_NAME "Function"
+#endif
+
+#ifndef UNITY_DETAIL2_NAME
+#define UNITY_DETAIL2_NAME "Argument"
+#endif
+#endif
+
+#ifdef UNITY_PRINT_TEST_CONTEXT
+void UNITY_PRINT_TEST_CONTEXT(void);
+#endif
+
+/*-------------------------------------------------------
+ * Test Output
+ *-------------------------------------------------------*/
+
+void UnityPrint(const char* string);
+
+#ifdef UNITY_INCLUDE_PRINT_FORMATTED
+void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...);
+#endif
+
+void UnityPrintLen(const char* string, const UNITY_UINT32 length);
+void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number);
+void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style);
+void UnityPrintNumber(const UNITY_INT number_to_print);
+void UnityPrintNumberUnsigned(const UNITY_UINT number);
+void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print);
+
+#ifndef UNITY_EXCLUDE_FLOAT_PRINT
+void UnityPrintFloat(const UNITY_DOUBLE input_number);
+#endif
+
+/*-------------------------------------------------------
+ * Test Assertion Functions
+ *-------------------------------------------------------
+ * Use the macros below this section instead of calling
+ * these directly. The macros have a consistent naming
+ * convention and will pull in file and line information
+ * for you. */
+
+void UnityAssertEqualNumber(const UNITY_INT expected,
+ const UNITY_INT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style);
+
+void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold,
+ const UNITY_INT actual,
+ const UNITY_COMPARISON_T compare,
+ const char *msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style);
+
+void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
+ UNITY_INTERNAL_PTR actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style,
+ const UNITY_FLAGS_T flags);
+
+void UnityAssertBits(const UNITY_INT mask,
+ const UNITY_INT expected,
+ const UNITY_INT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertEqualString(const char* expected,
+ const char* actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertEqualStringLen(const char* expected,
+ const char* actual,
+ const UNITY_UINT32 length,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected,
+ const char** actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLAGS_T flags);
+
+void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected,
+ UNITY_INTERNAL_PTR actual,
+ const UNITY_UINT32 length,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLAGS_T flags);
+
+void UnityAssertNumbersWithin(const UNITY_UINT delta,
+ const UNITY_INT expected,
+ const UNITY_INT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style);
+
+void UnityAssertNumbersArrayWithin(const UNITY_UINT delta,
+ UNITY_INTERNAL_PTR expected,
+ UNITY_INTERNAL_PTR actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_DISPLAY_STYLE_T style,
+ const UNITY_FLAGS_T flags);
+
+#ifndef UNITY_EXCLUDE_SETJMP_H
+UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line);
+UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line);
+#else
+void UnityFail(const char* message, const UNITY_LINE_TYPE line);
+void UnityIgnore(const char* message, const UNITY_LINE_TYPE line);
+#endif
+
+void UnityMessage(const char* message, const UNITY_LINE_TYPE line);
+
+#ifndef UNITY_EXCLUDE_FLOAT
+void UnityAssertFloatsWithin(const UNITY_FLOAT delta,
+ const UNITY_FLOAT expected,
+ const UNITY_FLOAT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta,
+ const UNITY_FLOAT expected,
+ const UNITY_FLOAT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold,
+ const UNITY_FLOAT actual,
+ const UNITY_COMPARISON_T compare,
+ const char* msg,
+ const UNITY_LINE_TYPE linenumber);
+
+void UnityAssertWithinFloatArray(const UNITY_FLOAT delta,
+ UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected,
+ UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLAGS_T flags);
+
+void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLOAT_TRAIT_T style);
+#endif
+
+#ifndef UNITY_EXCLUDE_DOUBLE
+void UnityAssertDoublesWithin(const UNITY_DOUBLE delta,
+ const UNITY_DOUBLE expected,
+ const UNITY_DOUBLE actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta,
+ const UNITY_DOUBLE expected,
+ const UNITY_DOUBLE actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber);
+
+void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold,
+ const UNITY_DOUBLE actual,
+ const UNITY_COMPARISON_T compare,
+ const char* msg,
+ const UNITY_LINE_TYPE linenumber);
+
+void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta,
+ UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected,
+ UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual,
+ const UNITY_UINT32 num_elements,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLAGS_T flags);
+
+void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual,
+ const char* msg,
+ const UNITY_LINE_TYPE lineNumber,
+ const UNITY_FLOAT_TRAIT_T style);
+#endif
+
+/*-------------------------------------------------------
+ * Helpers
+ *-------------------------------------------------------*/
+
+UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size);
+#ifndef UNITY_EXCLUDE_FLOAT
+UNITY_INTERNAL_PTR UnityFloatToPtr(const float num);
+#endif
+#ifndef UNITY_EXCLUDE_DOUBLE
+UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num);
+#endif
+
+/*-------------------------------------------------------
+ * Error Strings We Might Need
+ *-------------------------------------------------------*/
+
+extern const char UnityStrOk[];
+extern const char UnityStrPass[];
+extern const char UnityStrFail[];
+extern const char UnityStrIgnore[];
+
+extern const char UnityStrErrFloat[];
+extern const char UnityStrErrDouble[];
+extern const char UnityStrErr64[];
+extern const char UnityStrErrShorthand[];
+
+/*-------------------------------------------------------
+ * Test Running Macros
+ *-------------------------------------------------------*/
+
+#ifndef UNITY_EXCLUDE_SETJMP_H
+#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0)
+#define TEST_ABORT() longjmp(Unity.AbortFrame, 1)
+#else
+#define TEST_PROTECT() 1
+#define TEST_ABORT() return
+#endif
+
+/* Automatically enable variadic macros support, if it not enabled before */
+#ifndef UNITY_SUPPORT_VARIADIC_MACROS
+ #ifdef __STDC_VERSION__
+ #if __STDC_VERSION__ >= 199901L
+ #define UNITY_SUPPORT_VARIADIC_MACROS
+ #endif
+ #endif
+#endif
+
+/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */
+#ifndef RUN_TEST
+#ifdef UNITY_SUPPORT_VARIADIC_MACROS
+#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway)
+#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line)
+#endif
+#endif
+
+/* Enable default macros for masking param tests test cases */
+#ifdef UNITY_SUPPORT_TEST_CASES
+ #ifdef UNITY_SUPPORT_VARIADIC_MACROS
+ #if !defined(TEST_CASE) && !defined(UNITY_EXCLUDE_TEST_CASE)
+ #define TEST_CASE(...)
+ #endif
+ #if !defined(TEST_RANGE) && !defined(UNITY_EXCLUDE_TEST_RANGE)
+ #define TEST_RANGE(...)
+ #endif
+ #endif
+#endif
+
+/* If we can't do the tricky version, we'll just have to require them to always include the line number */
+#ifndef RUN_TEST
+#ifdef CMOCK
+#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num)
+#else
+#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__)
+#endif
+#endif
+
+#define TEST_LINE_NUM (Unity.CurrentTestLineNumber)
+#define TEST_IS_IGNORED (Unity.CurrentTestIgnored)
+#define UNITY_NEW_TEST(a) \
+ Unity.CurrentTestName = (a); \
+ Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \
+ Unity.NumberOfTests++;
+
+#ifndef UNITY_BEGIN
+#define UNITY_BEGIN() UnityBegin(__FILE__)
+#endif
+
+#ifndef UNITY_END
+#define UNITY_END() UnityEnd()
+#endif
+
+#ifndef UNITY_SHORTHAND_AS_INT
+#ifndef UNITY_SHORTHAND_AS_MEM
+#ifndef UNITY_SHORTHAND_AS_NONE
+#ifndef UNITY_SHORTHAND_AS_RAW
+#define UNITY_SHORTHAND_AS_OLD
+#endif
+#endif
+#endif
+#endif
+
+/*-----------------------------------------------
+ * Command Line Argument Support
+ *-----------------------------------------------*/
+
+#ifdef UNITY_USE_COMMAND_LINE_ARGS
+int UnityParseOptions(int argc, char** argv);
+int UnityTestMatches(void);
+#endif
+
+/*-------------------------------------------------------
+ * Basic Fail and Ignore
+ *-------------------------------------------------------*/
+
+#define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line))
+
+/*-------------------------------------------------------
+ * Test Asserts
+ *-------------------------------------------------------*/
+
+#define UNITY_TEST_ASSERT(condition, line, message) do { if (condition) { /* nothing*/ } else { UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message)); } } while (0)
+#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) == 0), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) != 0), (UNITY_LINE_TYPE)(line), (message))
+
+#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line))
+
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16) (threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32) (threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT)
+#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8)
+#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16) (expected), (UNITY_INT)(UNITY_INT16) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16)
+#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32) (expected), (UNITY_INT)(UNITY_INT32) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32)
+#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT)
+#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8)
+#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16)
+#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32)
+#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8)
+#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16)
+#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32)
+#define UNITY_TEST_ASSERT_CHAR_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR)
+
+#define UNITY_TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY)
+
+
+#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER)
+#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+
+#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY)
+
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT) (expected), (UNITY_POINTER_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_VAL)
+
+#ifdef UNITY_SUPPORT_64
+#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64)
+#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY)
+#else
+#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64)
+#endif
+
+#ifdef UNITY_EXCLUDE_FLOAT
+#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat)
+#else
+#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsNotWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)(delta), (UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)0, (UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)0, UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN)
+#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN)
+#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET)
+#endif
+
+#ifdef UNITY_EXCLUDE_DOUBLE
+#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble)
+#else
+#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesNotWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message))
+#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)(delta), (UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, (UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY)
+#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL)
+#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line))
+#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN)
+#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET)
+#endif
+
+/* End of UNITY_INTERNALS_H */
+#endif
diff --git a/movement/lib/morsecalc/calc.c b/movement/lib/morsecalc/calc.c
index 49b19a00..ed7eb25b 100644
--- a/movement/lib/morsecalc/calc.c
+++ b/movement/lib/morsecalc/calc.c
@@ -24,6 +24,7 @@
#include
#include
+#include
#include "calc.h"
#include "calc_fns.h"
@@ -41,12 +42,11 @@ int calc_init(calc_state_t *cs) {
}
/* calc_input_function
- * Try to execut the token as a calculator function
- * TODO: Maybe replace this loop with binary search for token in a sorted calc_dict
+ * Try to execute the token as a calculator function
*/
int calc_input_function(calc_state_t *cs, char *token) {
for(uint8_t idx=0; idxs--;
return 0;
}
+int calc_torad(calc_state_t *cs) {
+ STACK_CHECK_1_IN_1_OUT;
+ cs->stack[cs->s-1] = cs->stack[cs->s-1]*to_rad;
+ return 0;
+}
+int calc_todeg(calc_state_t *cs) {
+ STACK_CHECK_1_IN_1_OUT;
+ cs->stack[cs->s-1] = cs->stack[cs->s-1]*to_deg;
+ return 0;
+}
diff --git a/movement/lib/morsecalc/calc_fns.h b/movement/lib/morsecalc/calc_fns.h
index fd1d7aba..15f56b0e 100644
--- a/movement/lib/morsecalc/calc_fns.h
+++ b/movement/lib/morsecalc/calc_fns.h
@@ -67,57 +67,62 @@ int calc_asind(calc_state_t *cs);
int calc_acosd(calc_state_t *cs);
int calc_atand(calc_state_t *cs);
int calc_atan2d(calc_state_t *cs);
+int calc_torad(calc_state_t *cs);
+int calc_todeg(calc_state_t *cs);
// Dictionary definition
typedef int (*calc_fn_t)(calc_state_t *cs);
typedef struct {
- char *names[3]; // Token to use to run this function
+ uint8_t n_names; // Number of aliases
+ const char ** names; // Token to use to run this function
calc_fn_t fn; // Pointer to function
} calc_dict_entry_t;
static const calc_dict_entry_t calc_dict[] = {
// Stack and register control
- {{"x"}, &calc_delete},
- {{"xx"}, &calc_clear_stack},
- {{"xxx"}, &calc_init},
- {{"f"}, &calc_flip},
- {{"mc"}, &calc_mem_clear},
- {{"mr"}, &calc_mem_recall},
- {{"ma"}, &calc_mem_add},
- {{"ms"}, &calc_mem_subtract},
+ {1, (const char*[]){"x"}, &calc_delete},
+ {1, (const char*[]){"xx"}, &calc_clear_stack},
+ {1, (const char*[]){"xxx"}, &calc_init},
+ {1, (const char*[]){"f"}, &calc_flip},
+ {1, (const char*[]){"mc"}, &calc_mem_clear},
+ {1, (const char*[]){"mr"}, &calc_mem_recall},
+ {1, (const char*[]){"ma"}, &calc_mem_add},
+ {1, (const char*[]){"ms"}, &calc_mem_subtract},
// Basic operations
- {{"a"}, &calc_add},
- {{"s"}, &calc_subtract},
- {{"n"}, &calc_negate},
- {{"m"}, &calc_multiply},
- {{"d"}, &calc_divide},
- {{"i"}, &calc_invert},
+ {1, (const char*[]){"a"}, &calc_add},
+ {1, (const char*[]){"s"}, &calc_subtract},
+ {1, (const char*[]){"n"}, &calc_negate},
+ {1, (const char*[]){"m"}, &calc_multiply},
+ {1, (const char*[]){"d"}, &calc_divide},
+ {1, (const char*[]){"i"}, &calc_invert},
// Constants
- {{"e"}, &calc_e},
- {{"pi"}, &calc_pi},
+ {1, (const char*[]){"e"}, &calc_e},
+ {1, (const char*[]){"pi"}, &calc_pi},
// Exponential/logarithmic
- {{"exp"}, &calc_exp},
- {{"pow"}, &calc_pow},
- {{"ln"}, &calc_ln},
- {{"log"}, &calc_log},
- {{"sqrt"}, &calc_sqrt},
+ {1, (const char*[]){"exp"}, &calc_exp},
+ {1, (const char*[]){"pow"}, &calc_pow},
+ {1, (const char*[]){"ln"}, &calc_ln},
+ {1, (const char*[]){"log"}, &calc_log},
+ {1, (const char*[]){"sqrt"}, &calc_sqrt},
// Trigonometric
- {{"sin", "sn"}, &calc_sin},
- {{"cos"}, &calc_cos},
- {{"tan"}, &calc_tan},
- {{"asin"}, &calc_asin},
- {{"acos"}, &calc_acos},
- {{"atan"}, &calc_atan},
- {{"atan2"}, &calc_atan2},
- {{"sind"}, &calc_sind},
- {{"cosd"}, &calc_cosd},
- {{"tand"}, &calc_tand},
- {{"asind"}, &calc_asind},
- {{"acosd"}, &calc_acosd},
- {{"atand"}, &calc_atand},
- {{"atan2d"}, &calc_atan2d},
+ {2, (const char*[]){"sin", "sn"}, &calc_sin},
+ {1, (const char*[]){"cos"}, &calc_cos},
+ {1, (const char*[]){"tan"}, &calc_tan},
+ {1, (const char*[]){"asin"}, &calc_asin},
+ {1, (const char*[]){"acos"}, &calc_acos},
+ {1, (const char*[]){"atan"}, &calc_atan},
+ {1, (const char*[]){"atan2"}, &calc_atan2},
+ {1, (const char*[]){"sind"}, &calc_sind},
+ {1, (const char*[]){"cosd"}, &calc_cosd},
+ {1, (const char*[]){"tand"}, &calc_tand},
+ {1, (const char*[]){"asind"}, &calc_asind},
+ {1, (const char*[]){"acosd"}, &calc_acosd},
+ {1, (const char*[]){"atand"}, &calc_atand},
+ {1, (const char*[]){"atan2d"}, &calc_atan2d},
+ {1, (const char*[]){"tor"}, &calc_torad},
+ {1, (const char*[]){"tod"}, &calc_todeg},
};
diff --git a/movement/lib/morsecalc/mc.c b/movement/lib/morsecalc/mc.c
deleted file mode 100644
index 94f6511b..00000000
--- a/movement/lib/morsecalc/mc.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2023 Christian Chapman
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-
-#include
-#include "mc.h"
-
-/* mc_reset Initialize or reset an MC buffer
- * Input: mc = location of buffer to reset
- */
-void mc_reset(mc_state_t * mc) {
- memset(mc->b, '\0', BUFFLEN*sizeof(mc->b[0]));
- mc->bidx = 0;
- return;
- return;
-}
-
-/* mc_input Read an input into a morse code buffer
- * Input: mc = buffer to read into
- * c = character to read into buffer ('.' or '-', ignored otherwise).
- * If the buffer is full, reset it instead of entering the new character.
- */
-void mc_input(mc_state_t * mc, char c) {
- if(mc->bidx >= BUFFLEN) mc_reset(mc);
- else if( ('.' == c) || ('-' == c) ) {
- mc->b[mc->bidx] = c;
- mc->bidx++;
- }
- return;
-}
-
-/* mc_dec Decode a Morse code character (descend MC_DEC_KEY[])
- * Input: b = BUFFLEN-length char array with '.'s and '-'s
- * Output: c = Character b represents, or '\0' if not a Morse code.
- */
-char mc_dec(char b[BUFFLEN]) {
- uint8_t pos = 1; // Binary tree position ('.'=0; '-'=1)
- for(uint8_t idx=0; idx
+#include
+
+#include "watch_private_display.h"
+#include "morsecalc_display.h"
+
+// Display float on screen
+void morsecalc_display_float(double d) {
+ // Special cases
+ if(d == 0) {
+ watch_display_string(" 0", 4);
+ return;
+ }
+ else if(isnan(d)) {
+ watch_display_string(" nan", 4);
+ return;
+ }
+ else if(d == (1.0)/(0.0)) {
+ watch_display_string(" inf", 4);
+ return;
+ }
+ else if(d == (-1.0)/(0.0)) {
+ watch_display_character('X', 1);
+ watch_display_string(" inf", 4);
+ return;
+ }
+
+ // Record number properties
+ // Sign
+ int is_negative = d<0;
+ if(is_negative) d = -d;
+
+ // Order of magnitude
+ int om = (int) floor(log(d)/log(10));
+ int om_is_negative = (om<0);
+
+ // Get the first 4 significant figures
+ int digits;
+ digits = round(d*pow(10.0, 3-om));
+ if(digits>9999) {
+ digits = 1000;
+ om++;
+ }
+
+ // Print signs
+ if(is_negative) {
+ // Xi; see https://joeycastillo.github.io/Sensor-Watch-Documentation/segmap
+ watch_set_pixel(0,11);
+ watch_set_pixel(2,12);
+ watch_set_pixel(2,11);
+ }
+ else watch_display_character(' ', 1);
+ if(om_is_negative) watch_set_pixel(1,9);
+ else watch_display_character(' ', 2);
+
+ // Print first 4 significant figures
+ watch_display_character('0'+(digits/1000)%10, 4);
+ watch_display_character('0'+(digits/100 )%10, 5);
+ watch_display_character('0'+(digits/10 )%10, 6);
+ watch_display_character('0'+(digits/1 )%10, 7);
+
+ // Prinat exponent
+ if(om_is_negative) om = -om; // Make exponent positive for display
+ if(om<=99) {
+ watch_display_character('0'+(om/10 )%10, 8);
+ watch_display_character('0'+(om/1 )%10, 9);
+ } else { // Over/underflow
+ if(om_is_negative) watch_display_string(" uf", 4);
+ else watch_display_string(" of", 4);
+ if(om<9999) { // Use main display to show order of magnitude
+ // (Should always succeed; max double is <2e308)
+ watch_display_character('0'+(om/1000)%10, 4);
+ watch_display_character('0'+(om/100 )%10, 5);
+ watch_display_character('0'+(om/10 )%10, 6);
+ watch_display_character('0'+(om/1 )%10, 7);
+ }
+ }
+ return;
+}
+
+// Print current input token
+void morsecalc_display_token(morsecalc_state_t *mcs) {
+ watch_display_string(" ", 0); // Clear display
+
+ // Print morse code buffer
+ char c = MORSECODE_TREE[mcs->mc]; // Decode the morse code buffer's current contents
+ if('\0' == c) c = ' '; // Needed for watch_display_character
+ watch_display_character(c, 0); // Display current morse code char in mode position
+
+ unsigned int v = mcs->mc+1;
+ char bidx = 0; while (v >>= 1) bidx++;
+ watch_display_character('0'+bidx, 3); // Display buffer position in top right
+
+ // Print last 6 chars of current input line
+ uint8_t nlen = strlen(mcs->token); // number of characters in token
+ uint8_t nprint = min(nlen,6); // number of characters to print
+ watch_display_string(mcs->token+nlen-nprint, 10-nprint); // print right-aligned
+ return;
+}
+
+// Print stack or memory register contents.
+void morsecalc_display_stack(morsecalc_state_t * mcs) {
+ watch_display_string(" ", 0); // Clear display
+
+ char c = MORSECODE_TREE[mcs->mc];
+ if('m' == c) { // Display memory
+ morsecalc_display_float(mcs->cs->mem);
+ watch_display_character(c, 0);
+ }
+ else {
+ // If the morse code buffer has a numeral in it, print that stack item
+ // Otherwise print top of stack
+ uint8_t idx = 0;
+ if(c >= '0' && c <= '9') idx = c - '0';
+ if(idx >= mcs->cs->s) watch_display_string(" empty", 4); // Stack empty
+ else morsecalc_display_float(mcs->cs->stack[mcs->cs->s-1-idx]); // Print stack item
+
+ watch_display_character('0'+idx, 0); // Print which stack item this is top center
+ }
+ watch_display_character('0'+(mcs->cs->s), 3); // Print the # of stack items top right
+ return;
+}
+
diff --git a/movement/lib/morsecalc/mc.h b/movement/lib/morsecalc/morsecalc_display.h
similarity index 58%
rename from movement/lib/morsecalc/mc.h
rename to movement/lib/morsecalc/morsecalc_display.h
index 0daa470e..74f8b0cc 100644
--- a/movement/lib/morsecalc/mc.h
+++ b/movement/lib/morsecalc/morsecalc_display.h
@@ -22,30 +22,14 @@
* SOFTWARE.
*/
+#include "morsecalc_face.h"
-/* mc Morse code reading methods
-*/
-#include "stdint.h"
+// Display float on screen
+void morsecalc_display_float(double d);
-#define BUFFLEN 5
-typedef struct {
- char b[BUFFLEN];
- uint8_t bidx;
-} mc_state_t;
+// Print current input token
+void morsecalc_display_token(morsecalc_state_t *mcs);
-// MC_DEC_KEY represents a binary tree of International Morse Code.
-// where '.' = 0 and '-' = 1. Levels of the tree are concatenated.
-//
-// Capitals denote special characters:
-// C = Ch digraph
-// V = VERIFY (ITU-R "UNDERSTOOD")
-// R = REPEAT
-// W = WAIT
-// S = START TRANSMISSION
-// E = END OF WORK
-static const char MC_DEC_KEY[] = " etianmsurwdkgohvf\0l\0pjbxcyzq\0C\x35\x34V\x33\0R\0\x32W\0+\0\0\0\0\x31\x36=/\0\0S(\0\x37\0\0\0\x38\0\x39\x30\0\0\0\0\0E\0\0\0\0\0\0?_\0\0\0\0\"\0\0.\0\0\0\0@\0\0\0'\0\0-\0\0\0\0\0\0\0\0;!\0)\0\0\0\0\0,\0\0\0\0:\0\0\0\0\0\0\0";
-
-void mc_reset(mc_state_t * mcb);
-void mc_input(mc_state_t * mc, char c);
-char mc_dec(char b[BUFFLEN]);
+// Print stack or memory register contents.
+void morsecalc_display_stack(morsecalc_state_t *mcs);
diff --git a/movement/lib/morsecalc/test_morsecalc.c b/movement/lib/morsecalc/test_morsecalc.c
new file mode 100644
index 00000000..fc640bad
--- /dev/null
+++ b/movement/lib/morsecalc/test_morsecalc.c
@@ -0,0 +1,66 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Christian Chapman
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// Computer console interface to calc and morsecode for testing without involving watch stuff.
+// cc calc_strtof.c calc.c calc_fns.c test_morsecalc.c -lm
+
+#include
+#include
+#include
+#include
+
+#include "calc.h"
+#include "calc_fns.h"
+
+int main(void) {
+ calc_state_t cs;
+ calc_init(&cs);
+
+ char * word = malloc(0);
+ unsigned int nword = 0;
+ char c;
+ int retval = 0;
+ for(unsigned int ii = 0; ii < 100; ii++) {
+ c = getchar();
+ word = realloc(word, (++nword)*sizeof(char));
+ word[nword-1] = c;
+ if((nword > 0) && isspace(c)) { // Word is finished
+ word[nword-1] = '\0';
+ retval = calc_input(&cs, word); // Submit word
+ word = realloc(word, 0); nword = 0; // Clear word
+
+ switch(retval) {
+ case 0: printf("Success.\n"); break;
+ case -1: printf("Bad command.\n"); break;
+ case -2: printf("Stack over/underflow.\n"); break;
+ case -3: printf("Error.\n"); break;
+ }
+ if(cs.s > 0) printf("[%i]: %.4f\n", cs.s, cs.stack[cs.s-1]);
+ else printf("[%i]\n", cs.s);
+ }
+ }
+
+ free(word);
+ return 0;
+}
diff --git a/movement/make/Makefile b/movement/make/Makefile
index 417be4c5..952f3c70 100644
--- a/movement/make/Makefile
+++ b/movement/make/Makefile
@@ -17,6 +17,7 @@ INCLUDES += \
-I../watch_faces/sensor/ \
-I../watch_faces/demo/ \
-I../../littlefs/ \
+ -I../lib/chirpy_tx/ \
-I../lib/TOTP/ \
-I../lib/base32/ \
-I../lib/sunriset/ \
@@ -31,6 +32,7 @@ INCLUDES += \
# ../drivers/lis2dh.c \
# ../watch_faces/fitness/step_count_face.c
SRCS += \
+ ../lib/chirpy_tx/chirpy_tx.c \
../lib/TOTP/sha1.c \
../lib/TOTP/sha256.c \
../lib/TOTP/sha512.c \
@@ -42,7 +44,7 @@ SRCS += \
../lib/morsecalc/calc.c \
../lib/morsecalc/calc_fns.c \
../lib/morsecalc/calc_strtof.c \
- ../lib/morsecalc/mc.c \
+ ../lib/morsecalc/morsecalc_display.c \
../../littlefs/lfs.c \
../../littlefs/lfs_util.c \
../movement.c \
@@ -95,9 +97,23 @@ SRCS += \
../watch_faces/complication/tarot_face.c \
../watch_faces/complication/morsecalc_face.c \
../watch_faces/complication/rpn_calculator_face.c \
+ ../watch_faces/complication/activity_face.c \
+ ../watch_faces/demo/chirpy_demo_face.c \
../watch_faces/complication/ships_bell_face.c \
+ ../watch_faces/sensor/lightmeter_face.c \
+ ../watch_faces/complication/discgolf_face.c \
../watch_faces/complication/habit_face.c \
+ ../watch_faces/complication/planetary_time_face.c \
+ ../watch_faces/complication/planetary_hours_face.c \
+ ../watch_faces/complication/breathing_face.c \
+ ../watch_faces/clock/repetition_minute_face.c \
../watch_faces/complication/timer_face.c \
+ ../watch_faces/complication/invaders_face.c \
+ ../watch_faces/clock/world_clock2_face.c \
+ ../watch_faces/complication/time_left_face.c \
+ ../watch_faces/complication/randonaut_face.c \
+ ../watch_faces/complication/toss_up_face.c \
+ ../watch_faces/complication/geomancy_face.c \
# New watch faces go above this line.
# Leave this line at the bottom of the file; it has all the targets for making your project.
diff --git a/movement/movement_faces.h b/movement/movement_faces.h
index 56e722dd..0e2a45b1 100644
--- a/movement/movement_faces.h
+++ b/movement/movement_faces.h
@@ -73,9 +73,24 @@
#include "interval_face.h"
#include "morsecalc_face.h"
#include "rpn_calculator_face.h"
+#include "activity_face.h"
+#include "chirpy_demo_face.h"
#include "ships_bell_face.h"
+#include "lightmeter_face.h"
+#include "discgolf_face.h"
#include "habit_face.h"
+#include "planetary_time_face.h"
+#include "planetary_hours_face.h"
+#include "breathing_face.h"
+#include "repetition_minute_face.h"
#include "timer_face.h"
+#include "invaders_face.h"
+#include "world_clock2_face.h"
+#include "time_left_face.h"
+#include "randonaut_face.h"
+#include "toss_up_face.h"
+#include "geomancy_face.h"
+#include "dual_timer_face.h"
// New includes go above this line.
#endif // MOVEMENT_FACES_H_
diff --git a/movement/watch_faces/clock/repetition_minute_face.c b/movement/watch_faces/clock/repetition_minute_face.c
new file mode 100644
index 00000000..a0fbe077
--- /dev/null
+++ b/movement/watch_faces/clock/repetition_minute_face.c
@@ -0,0 +1,231 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jonas Termeau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include "repetition_minute_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_private_display.h"
+
+void play_hour_chime(void) {
+ watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
+ watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
+}
+
+void play_quarter_chime(void) {
+ watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
+ watch_buzzer_play_note(BUZZER_NOTE_REST, 150);
+ watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
+ watch_buzzer_play_note(BUZZER_NOTE_REST, 750);
+}
+
+void play_minute_chime(void) {
+ watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
+ watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
+}
+
+static void _update_alarm_indicator(bool settings_alarm_enabled, repetition_minute_state_t *state) {
+ state->alarm_enabled = settings_alarm_enabled;
+ if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+ else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+}
+
+void repetition_minute_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) settings;
+ (void) watch_face_index;
+
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(repetition_minute_state_t));
+ repetition_minute_state_t *state = (repetition_minute_state_t *)*context_ptr;
+ state->signal_enabled = false;
+ state->watch_face_index = watch_face_index;
+ }
+}
+
+void repetition_minute_face_activate(movement_settings_t *settings, void *context) {
+ repetition_minute_state_t *state = (repetition_minute_state_t *)context;
+
+ if (watch_tick_animation_is_running()) watch_stop_tick_animation();
+
+ if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
+
+ // handle chime indicator
+ if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+ else watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+ // show alarm indicator if there is an active alarm
+ _update_alarm_indicator(settings->bit.alarm_enabled, state);
+
+ watch_set_colon();
+
+ // this ensures that none of the timestamp fields will match, so we can re-render them all.
+ state->previous_date_time = 0xFFFFFFFF;
+}
+
+bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ repetition_minute_state_t *state = (repetition_minute_state_t *)context;
+ char buf[11];
+ uint8_t pos;
+
+ watch_date_time date_time;
+ uint32_t previous_date_time;
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ case EVENT_TICK:
+ case EVENT_LOW_ENERGY_UPDATE:
+ date_time = watch_rtc_get_date_time();
+ previous_date_time = state->previous_date_time;
+ state->previous_date_time = date_time.reg;
+
+ // check the battery voltage once a day...
+ if (date_time.unit.day != state->last_battery_check) {
+ state->last_battery_check = date_time.unit.day;
+ watch_enable_adc();
+ uint16_t voltage = watch_get_vcc_voltage();
+ watch_disable_adc();
+ // 2.2 volts will happen when the battery has maybe 5-10% remaining?
+ // we can refine this later.
+ state->battery_low = (voltage < 2200);
+ }
+
+ // ...and set the LAP indicator if low.
+ if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
+
+ if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+ // everything before seconds is the same, don't waste cycles setting those segments.
+ watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
+ watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
+ break;
+ } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+ // everything before minutes is the same.
+ pos = 6;
+ sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
+ } else {
+ // other stuff changed; let's do it all.
+ if (!settings->bit.clock_mode_24h) {
+ // if we are in 12 hour mode, do some cleanup.
+ if (date_time.unit.hour < 12) {
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ } else {
+ watch_set_indicator(WATCH_INDICATOR_PM);
+ }
+ date_time.unit.hour %= 12;
+ if (date_time.unit.hour == 0) date_time.unit.hour = 12;
+ }
+ pos = 0;
+ if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+ if (!watch_tick_animation_is_running()) watch_start_tick_animation(500);
+ sprintf(buf, "%s%2d%2d%02d ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
+ } else {
+ sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
+ }
+ }
+ watch_display_string(buf, pos);
+ // handle alarm indicator
+ if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state);
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ state->signal_enabled = !state->signal_enabled;
+ if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
+ else watch_clear_indicator(WATCH_INDICATOR_BELL);
+ break;
+ case EVENT_BACKGROUND_TASK:
+ // uncomment this line to snap back to the clock face when the hour signal sounds:
+ // movement_move_to_face(state->watch_face_index);
+ if (watch_is_buzzer_or_led_enabled()) {
+ // if we are in the foreground, we can just beep.
+ movement_play_signal();
+ } else {
+ // if we were in the background, we need to enable the buzzer peripheral first,
+ watch_enable_buzzer();
+ // beep quickly (this call blocks for 275 ms),
+ movement_play_signal();
+ // and then turn the buzzer peripheral off again.
+ watch_disable_buzzer();
+ }
+ break;
+ case EVENT_LIGHT_LONG_UP:
+ /*
+ * Howdy neighbors, this is the actual complication. Like an actual
+ * (very expensive) watch with a repetition minute complication it's
+ * boring at 00:00 or 1:00 and very quite musical at 23:59 or 12:59.
+ */
+
+ date_time = watch_rtc_get_date_time();
+
+
+ int hours = date_time.unit.hour;
+ int quarters = date_time.unit.minute / 15;
+ int minutes = date_time.unit.minute % 15;
+
+ // chiming hours
+ if (!settings->bit.clock_mode_24h) {
+ hours = date_time.unit.hour % 12;
+ if (hours == 0) hours = 12;
+ }
+ if (hours > 0) {
+ int count = 0;
+ for(count = hours; count > 0; --count) {
+ play_hour_chime();
+ }
+ }
+
+ // chiming quarters (if needed)
+ if (quarters > 0) {
+ int count = 0;
+ for(count = quarters; count > 0; --count) {
+ play_quarter_chime();
+ }
+ }
+
+ // chiming minutes (if needed)
+ if (minutes > 0) {
+ int count = 0;
+ for(count = minutes; count > 0; --count) {
+ play_minute_chime();
+ }
+ }
+
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+
+ return true;
+}
+
+void repetition_minute_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+}
+
+bool repetition_minute_face_wants_background_task(movement_settings_t *settings, void *context) {
+ (void) settings;
+ repetition_minute_state_t *state = (repetition_minute_state_t *)context;
+ if (!state->signal_enabled) return false;
+
+ watch_date_time date_time = watch_rtc_get_date_time();
+
+ return date_time.unit.minute == 0;
+}
diff --git a/movement/watch_faces/clock/repetition_minute_face.h b/movement/watch_faces/clock/repetition_minute_face.h
new file mode 100644
index 00000000..5a897bc1
--- /dev/null
+++ b/movement/watch_faces/clock/repetition_minute_face.h
@@ -0,0 +1,83 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Jonas Termeau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef REPETITION_MINUTE_FACE_H_
+#define REPETITION_MINUTE_FACE_H_
+
+#include "movement.h"
+
+/*
+ * A hopefully useful complication for friendly neighbors in the dark
+ *
+ * Originating from 1676 from reverend and mechanician Edward Barlow, and
+ * perfected in 1820 by neighbor Abraham Breguet, a minute repeater or
+ * "repetition minute" is a complication in a mechanical watch or clock that
+ * chimes the hours and often minutes at the press of a button. There are many
+ * types of repeater, from the simple repeater which merely strikes the number
+ * of hours, to the minute repeater which chimes the time down to the minute,
+ * using separate tones for hours, quarter hours, and minutes. They originated
+ * before widespread artificial illumination, to allow the time to be determined
+ * in the dark, and were also used by the visually impaired.
+ *
+ *
+ * How to use it :
+ *
+ * Long press the light button to get an auditive reading of the time like so :
+ * 0..23 (1..12 if 24-hours format isn't enabled) low beep(s) for the hours
+ * 0..3 low-high couple pitched beeps for the quarters
+ * 0..14 high pitched beep(s) for the remaining minutes
+ *
+ * Prerequisite : a watch with a working buzzer
+ *
+ * ~ Only in the darkness can you see the stars. - Martin Luther King ~
+ *
+ */
+
+typedef struct {
+ uint32_t previous_date_time;
+ uint8_t last_battery_check;
+ uint8_t watch_face_index;
+ bool signal_enabled;
+ bool battery_low;
+ bool alarm_enabled;
+} repetition_minute_state_t;
+
+void play_hour_chime(void);
+void play_quarter_chime(void);
+void play_minute_chime(void);
+void repetition_minute_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void repetition_minute_face_activate(movement_settings_t *settings, void *context);
+bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void repetition_minute_face_resign(movement_settings_t *settings, void *context);
+bool repetition_minute_face_wants_background_task(movement_settings_t *settings, void *context);
+
+#define repetition_minute_face ((const watch_face_t){ \
+ repetition_minute_face_setup, \
+ repetition_minute_face_activate, \
+ repetition_minute_face_loop, \
+ repetition_minute_face_resign, \
+ repetition_minute_face_wants_background_task, \
+})
+
+#endif // REPETITION_MINUTE_FACE_H_
diff --git a/movement/watch_faces/clock/world_clock2_face.c b/movement/watch_faces/clock/world_clock2_face.c
new file mode 100644
index 00000000..0077f639
--- /dev/null
+++ b/movement/watch_faces/clock/world_clock2_face.c
@@ -0,0 +1,450 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Konrad Rieck
+ * Copyright (c) 2022 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * World Clock 2
+ * =============
+ *
+ * This is an alternative world clock face that allows the user to cycle
+ * through a list of selected time zones. It extends the original
+ * implementation by Joey Castillo. The face has two modes *display mode*
+ * and *settings mode*.
+ *
+ * ### Settings mode
+ *
+ * When the clock face is activated for the first time, it enters
+ * *settings mode*. Here, the user can select the time zones they want to
+ * display. The face shows a summary of the current time zone:
+ *
+ * - The top of the face displays the first two letters of the time zone
+ * abbreviation, such as "PS" for Pacific Standard Time or CE for
+ * "Central European Time".
+ *
+ * - The upper-right corner shows the index number of the time zone. This
+ * helps avoid confusion when multiple time zones have the same
+ * two-letter abbreviation.
+ *
+ * - The main display shows the offset from UTC, with a "+" indicating a
+ * positive offset and a "-" indicating a negative offset. For example,
+ * the offset for Japanese Standard Time is displayed as "+9:00".
+ *
+ * The user can navigate through the time zones and select them using the
+ * following buttons:
+ *
+ * - The *alarm button* moves forward to the next time zone, while the
+ * *light button* moves backward to the previous zone. This way, the
+ * user can cycle through all 41 supported time zones.
+ *
+ * - A *long press* on the *light button* selects the current time zone,
+ * and the signal indicator appears at the top left. Another *long
+ * press* of the *light button* deselects the time zone.
+ *
+ * - A *long press* on the *alarm button* exits settings mode and returns
+ * to display mode.
+ *
+ * ### Display mode
+ *
+ * In the display mode, the face shows the time of the currently selected
+ * time zone. The face includes the following components:
+ *
+ * - The top of the face displays the first two letters of the time zone
+ * abbreviation, such as "PS" for Pacific Standard Time or "CE" for
+ * Central European Time.
+ *
+ * - The upper-right corner shows the current day of the month, which
+ * helps indicate time zones that cross the international date line
+ * with respect to the local time.
+ *
+ * - The main display shows the time in the selected time zone in either
+ * 12-hour or 24-hour form. There is no timeout, allowing users to keep
+ * the chosen time zone displayed for as long as they wish.
+ *
+ * The user can navigate through the selected time zones using the
+ * following buttons:
+ *
+ * - The *alarm button* moves to the next selected time zone, while the
+ * light button moves to the *previous zone*. If no time zone is
+ * selected, the face simply shows UTC.
+ *
+ * - A *long press* on the *alarm button* enters settings mode and
+ * enables the user to re-configure the selected time zones.
+ *
+ * - A *long press* on the *light button* activates the LED illumination
+ * of the watch.
+ *
+ */
+
+#include
+#include
+#include "world_clock2_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_utility.h"
+
+static bool refresh_face;
+
+/* Simple macros for navigation */
+#define FORWARD +1
+#define BACKWARD -1
+
+/* Activate refresh of time */
+#define REFRESH_TIME 0xffffffff
+
+/* List of all time zone names */
+const char *zone_names[] = {
+ "UTC", // 0 : 0:00:00 (UTC)
+ "CET", // 1 : 1:00:00 (Central European Time)
+ "SAST", // 2 : 2:00:00 (South African Standard Time)
+ "ARST", // 3 : 3:00:00 (Arabia Standard Time)
+ "IRST", // 4 : 3:30:00 (Iran Standard Time)
+ "GET", // 5 : 4:00:00 (Georgia Standard Time)
+ "AFT", // 6 : 4:30:00 (Afghanistan Time)
+ "PKT", // 7 : 5:00:00 (Pakistan Standard Time)
+ "IST", // 8 : 5:30:00 (Indian Standard Time)
+ "NPT", // 9 : 5:45:00 (Nepal Time)
+ "KGT", // 10 : 6:00:00 (Kyrgyzstan time)
+ "MYST", // 11 : 6:30:00 (Myanmar Time)
+ "THA", // 12 : 7:00:00 (Thailand Standard Time)
+ "CST", // 13 : 8:00:00 (China Standard Time, Australian Western Standard Time)
+ "ACWS", // 14 : 8:45:00 (Australian Central Western Standard Time)
+ "JST", // 15 : 9:00:00 (Japan Standard Time, Korea Standard Time)
+ "ACST", // 16 : 9:30:00 (Australian Central Standard Time)
+ "AEST", // 17 : 10:00:00 (Australian Eastern Standard Time)
+ "LHST", // 18 : 10:30:00 (Lord Howe Standard Time)
+ "SBT", // 19 : 11:00:00 (Solomon Islands Time)
+ "NZST", // 20 : 12:00:00 (New Zealand Standard Time)
+ "CHAS", // 21 : 12:45:00 (Chatham Standard Time)
+ "TOT", // 22 : 13:00:00 (Tonga Time)
+ "CHAD", // 23 : 13:45:00 (Chatham Daylight Time)
+ "LINT", // 24 : 14:00:00 (Line Islands Time)
+ "BIT", // 25 : -12:00:00 (Baker Island Time)
+ "NUT", // 26 : -11:00:00 (Niue Time)
+ "HST", // 27 : -10:00:00 (Hawaii-Aleutian Standard Time)
+ "MART", // 28 : -9:30:00 (Marquesas Islands Time)
+ "AKST", // 29 : -9:00:00 (Alaska Standard Time)
+ "PST", // 30 : -8:00:00 (Pacific Standard Time)
+ "MST", // 31 : -7:00:00 (Mountain Standard Time)
+ "CST", // 32 : -6:00:00 (Central Standard Time)
+ "EST", // 33 : -5:00:00 (Eastern Standard Time)
+ "VET", // 34 : -4:30:00 (Venezuelan Standard Time)
+ "AST", // 35 : -4:00:00 (Atlantic Standard Time)
+ "NST", // 36 : -3:30:00 (Newfoundland Standard Time)
+ "BRT", // 37 : -3:00:00 (Brasilia Time)
+ "NDT", // 38 : -2:30:00 (Newfoundland Daylight Time)
+ "FNT", // 39 : -2:00:00 (Fernando de Noronha Time)
+ "AZOT", // 40 : -1:00:00 (Azores Standard Time)
+};
+
+/* Modulo function */
+static inline unsigned int mod(int a, int b)
+{
+ int r = a % b;
+ return r < 0 ? r + b : r;
+}
+
+/* Find the next selected time zone */
+static inline uint8_t find_selected_zone(world_clock2_state_t *state, int direction)
+{
+ uint8_t i = state->current_zone;
+
+ do {
+ i = mod(i + direction, NUM_TIME_ZONES);
+ /* Could not find a selected zone. Return UTC */
+ if (i == state->current_zone) {
+ return 0;
+ }
+ } while (!state->zones[i].selected);
+
+ return i;
+}
+
+/* Beep when zone is enabled. An octave up */
+static void beep_enable() {
+ watch_buzzer_play_note(BUZZER_NOTE_G7, 50);
+ watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
+ watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
+}
+
+/* Beep when zone id disable. An octave down */
+static void beep_disable() {
+ watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+ watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
+ watch_buzzer_play_note(BUZZER_NOTE_G7, 75);
+}
+
+void world_clock2_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr)
+{
+ (void) settings;
+ (void) watch_face_index;
+
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(world_clock2_state_t));
+ memset(*context_ptr, 0, sizeof(world_clock2_state_t));
+
+ /* Start in settings mode */
+ world_clock2_state_t *state = (world_clock2_state_t *) * context_ptr;
+ state->current_mode = WORLD_CLOCK2_MODE_SETTINGS;
+ }
+}
+
+void world_clock2_face_activate(movement_settings_t *settings, void *context)
+{
+ (void) settings;
+ world_clock2_state_t *state = (world_clock2_state_t *) context;
+
+ if (watch_tick_animation_is_running())
+ watch_stop_tick_animation();
+
+ switch (state->current_mode) {
+ case WORLD_CLOCK2_MODE_DISPLAY:
+ /* Normal tick frequency */
+ movement_request_tick_frequency(1);
+ break;
+ case WORLD_CLOCK2_MODE_SETTINGS:
+ /* Faster frequency for blinking effect */
+ movement_request_tick_frequency(4);
+ break;
+ }
+ refresh_face = true;
+}
+
+static bool mode_display(movement_event_t event, movement_settings_t *settings, world_clock2_state_t *state)
+{
+ char buf[11];
+ uint8_t pos;
+
+ uint32_t timestamp;
+ uint32_t previous_date_time;
+ watch_date_time date_time;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ case EVENT_TICK:
+ case EVENT_LOW_ENERGY_UPDATE:
+ /* Update indicators and colon on refresh */
+ if (refresh_face) {
+ watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+ watch_set_colon();
+ if (settings->bit.clock_mode_24h)
+ watch_set_indicator(WATCH_INDICATOR_24H);
+
+ state->previous_date_time = REFRESH_TIME;
+ refresh_face = false;
+ }
+
+ /* Determine current time at time zone and store date/time */
+ date_time = watch_rtc_get_date_time();
+ timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60);
+ date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->current_zone] * 60);
+ previous_date_time = state->previous_date_time;
+ state->previous_date_time = date_time.reg;
+
+ if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+ /* Everything before seconds is the same, don't waste cycles setting those segments. */
+ pos = 8;
+ sprintf(buf, "%02d", date_time.unit.second);
+ } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
+ /* Everything before minutes is the same. */
+ pos = 6;
+ sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
+ } else {
+ /* Other stuff changed; Let's do it all. */
+ if (!settings->bit.clock_mode_24h) {
+ /* If we are in 12 hour mode, do some cleanup. */
+ if (date_time.unit.hour < 12) {
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ } else {
+ watch_set_indicator(WATCH_INDICATOR_PM);
+ }
+ date_time.unit.hour %= 12;
+ if (date_time.unit.hour == 0)
+ date_time.unit.hour = 12;
+ }
+
+ pos = 0;
+ if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
+ if (!watch_tick_animation_is_running())
+ watch_start_tick_animation(500);
+
+ sprintf(buf, "%.2s%2d%2d%02d ",
+ zone_names[state->current_zone],
+ date_time.unit.day,
+ date_time.unit.hour,
+ date_time.unit.minute);
+ } else {
+ sprintf(buf, "%.2s%2d%2d%02d%02d",
+ zone_names[state->current_zone],
+ date_time.unit.day,
+ date_time.unit.hour,
+ date_time.unit.minute,
+ date_time.unit.second);
+ }
+ }
+ watch_display_string(buf, pos);
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ state->current_zone = find_selected_zone(state, FORWARD);
+ state->previous_date_time = REFRESH_TIME;
+ break;
+ case EVENT_LIGHT_BUTTON_DOWN:
+ /* Do nothing. */
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ state->current_zone = find_selected_zone(state, BACKWARD);
+ state->previous_date_time = REFRESH_TIME;
+ break;
+ case EVENT_LIGHT_LONG_PRESS:
+ movement_illuminate_led();
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ /* Switch to settings mode */
+ state->current_mode = WORLD_CLOCK2_MODE_SETTINGS;
+ refresh_face = true;
+ movement_request_tick_frequency(1);
+
+ if (settings->bit.button_should_sound)
+ watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+ break;
+ case EVENT_MODE_BUTTON_UP:
+ /* Reset frequency and move to next face */
+ movement_request_tick_frequency(1);
+ movement_move_to_next_face();
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+
+ return true;
+}
+
+static bool mode_settings(movement_event_t event, movement_settings_t *settings, world_clock2_state_t *state)
+{
+ char buf[11];
+ int8_t hours, minutes;
+ uint8_t zone;
+ div_t result;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ case EVENT_TICK:
+ case EVENT_LOW_ENERGY_UPDATE:
+ /* Update indicator and colon on refresh */
+ if (refresh_face) {
+ watch_clear_colon();
+ watch_clear_indicator(WATCH_INDICATOR_24H);
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ refresh_face = false;
+ }
+ result = div(movement_timezone_offsets[state->current_zone], 60);
+ hours = result.quot;
+ minutes = result.rem;
+
+ /*
+ * Display time zone. The range of the parameters is reduced
+ * to avoid accidentally overflowing the buffer and to suppress
+ * corresponding compiler warnings.
+ */
+ sprintf(buf, "%.2s%2d %c%02d%02d",
+ zone_names[state->current_zone],
+ state->current_zone % 100,
+ hours < 0 ? '-' : '+',
+ abs(hours) % 24,
+ abs(minutes) % 60);
+
+ /* Let the zone number blink */
+ if (event.subsecond % 2)
+ buf[2] = buf[3] = ' ';
+
+ if (state->zones[state->current_zone].selected)
+ watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+ else
+ watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+
+ watch_display_string(buf, 0);
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ state->current_zone = mod(state->current_zone + FORWARD, NUM_TIME_ZONES);
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ state->current_zone = mod(state->current_zone + BACKWARD, NUM_TIME_ZONES);
+ break;
+ case EVENT_LIGHT_BUTTON_DOWN:
+ /* Do nothing */
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ /* Find next selected zone */
+ if (!state->zones[state->current_zone].selected)
+ state->current_zone = find_selected_zone(state, FORWARD);
+
+ /* Switch to display mode */
+ state->current_mode = WORLD_CLOCK2_MODE_DISPLAY;
+ refresh_face = true;
+ movement_request_tick_frequency(1);
+
+ if (settings->bit.button_should_sound)
+ watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+ break;
+ case EVENT_LIGHT_LONG_PRESS:
+ /* Toggle selection of current zone */
+ zone = state->current_zone;
+ state->zones[zone].selected = !state->zones[zone].selected;
+
+ if (settings->bit.button_should_sound) {
+ if (state->zones[zone].selected) {
+ beep_enable();
+ } else {
+ beep_disable();
+ }
+ }
+ break;
+ case EVENT_MODE_BUTTON_UP:
+ /* Reset frequency and move to next face */
+ movement_request_tick_frequency(1);
+ movement_move_to_next_face();
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+
+ return true;
+}
+
+bool world_clock2_face_loop(movement_event_t event, movement_settings_t *settings, void *context)
+{
+ world_clock2_state_t *state = (world_clock2_state_t *) context;
+ switch (state->current_mode) {
+ case WORLD_CLOCK2_MODE_DISPLAY:
+ return mode_display(event, settings, state);
+ case WORLD_CLOCK2_MODE_SETTINGS:
+ return mode_settings(event, settings, state);
+ }
+ return false;
+}
+
+void world_clock2_face_resign(movement_settings_t *settings, void *context)
+{
+ (void) settings;
+ (void) context;
+}
diff --git a/movement/watch_faces/clock/world_clock2_face.h b/movement/watch_faces/clock/world_clock2_face.h
new file mode 100644
index 00000000..f70dca1f
--- /dev/null
+++ b/movement/watch_faces/clock/world_clock2_face.h
@@ -0,0 +1,63 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Konrad Rieck
+ * Copyright (c) 2022 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef WORLD_CLOCK2_FACE_H_
+#define WORLD_CLOCK2_FACE_H_
+
+/* Number of zones. See movement_timezone_offsets. */
+#define NUM_TIME_ZONES 41
+
+#include "movement.h"
+
+typedef enum {
+ WORLD_CLOCK2_MODE_DISPLAY,
+ WORLD_CLOCK2_MODE_SETTINGS
+} world_clock2_mode_t;
+
+typedef struct {
+ bool selected;
+} world_clock2_zone_t;
+
+typedef struct {
+ world_clock2_zone_t zones[NUM_TIME_ZONES];
+ world_clock2_mode_t current_mode;
+ uint8_t current_zone;
+ uint32_t previous_date_time;
+} world_clock2_state_t;
+
+void world_clock2_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr);
+void world_clock2_face_activate(movement_settings_t *settings, void *context);
+bool world_clock2_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void world_clock2_face_resign(movement_settings_t *settings, void *context);
+
+#define world_clock2_face ((const watch_face_t){ \
+ world_clock2_face_setup, \
+ world_clock2_face_activate, \
+ world_clock2_face_loop, \
+ world_clock2_face_resign, \
+ NULL, \
+})
+
+#endif /* WORLD_CLOCK2_FACE_H_ */
diff --git a/movement/watch_faces/complication/activity_face.c b/movement/watch_faces/complication/activity_face.c
new file mode 100644
index 00000000..f92984cd
--- /dev/null
+++ b/movement/watch_faces/complication/activity_face.c
@@ -0,0 +1,725 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* ** TODO
+ * ===================
+ * -- Additional power-saving optimizations
+ */
+
+#include
+#include
+#include "activity_face.h"
+#include "chirpy_tx.h"
+#include "watch.h"
+#include "watch_utility.h"
+
+// ===========================================================================
+// This part is configurable: you can edit values here to customize you activity face
+// In particular, with num_enabled_activities and enabled_activities you can choose a subset of the
+// activities that you want to see in your watch.
+// You can also add new items to activity_names, but don't redefine or remove existing ones.
+
+// If a logged activity is shorter than this, then it won't be added to log when it ends.
+// This way scarce log slots are not taken up by aborted events that weren't real activities.
+static const uint16_t activity_min_length_sec = 60;
+
+// Supported activities. ID of activity is index in this buffer
+// W e should never change order or redefine items, only add new items when needed.
+static const char activity_names[][7] = {
+ " bIKE ",
+ "uuaLK ",
+ " rUn ",
+ "DAnCE ",
+ " yOgA ",
+ "CrOSS ",
+ "Suuinn",
+ "ELLIP ",
+ " gYnn",
+ " rOuu",
+ "SOCCEr",
+ " FOOTb",
+ " bALL ",
+ " SKI ",
+};
+
+// Currently enabled activities. This makes picking on first subface easier: why show activities you personally never do.
+static const uint8_t enabled_activities[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
+
+// Number of currently enabled activities (size of enabled_activities).
+static const uint8_t num_enabled_activities = sizeof(enabled_activities) / sizeof(uint8_t);
+
+// End configurable section
+// ===========================================================================
+
+// One logged activity
+typedef struct __attribute__((__packed__)) {
+ // Activity's start time
+ watch_date_time start_time;
+
+ // Total duration of activity, including time spend in paus
+ uint16_t total_sec;
+
+ // Number of seconds the activity was paused
+ uint16_t pause_sec;
+
+ // Type of activity (index in activity_names)
+ uint8_t activity_type;
+
+} activity_item_t;
+
+#define MAX_ACTIVITY_SECONDS 28800 // 8 hours = 28800 sec
+
+// Size of (fixed) buffer to log activites. Takes up x9 bytes in SRAM if face is installed.
+#define ACTIVITY_LOG_SZ 99
+
+// Number of activities in buffer.
+static uint8_t activity_log_count = 0;
+
+// Buffer with all logged activities.
+static activity_item_t activity_log_buffer[ACTIVITY_LOG_SZ];
+
+#define CHIRPY_PREFIX_LEN 2
+// First two bytes chirped out, to identify transmission as from the activity face
+static const uint8_t activity_chirpy_prefix[CHIRPY_PREFIX_LEN] = {0x27, 0x00};
+
+// The face's different UI modes (views).
+typedef enum {
+ ACTM_CHOOSE = 0,
+ ACTM_LOGGING,
+ ACTM_PAUSED,
+ ACTM_DONE,
+ ACTM_LOGSIZE,
+ ACTM_CHIRP,
+ ACTM_CHIRPING,
+ ACTM_CLEAR,
+ ACTM_CLEAR_CONFIRM,
+ ACTM_CLEAR_DONE,
+} activity_mode_t;
+
+// The full state of the activity face
+typedef struct {
+ // Current mode (which secondary face, or ongoing operation like logging)
+ activity_mode_t mode;
+
+ // Index of currently selected activity in enabled_activities
+ uint8_t type_ix;
+
+ // Used for different things depending on mode
+ // In ACTM_DONE: countdown for animation, before returning to start face
+ // In ACTM_LOGGING and ACTM_PAUSED: drives blinking colon and alternating time display
+ // In ACTM_LOGSIZE, ACTM_CLEAR: enables timeout return to choose screen
+ uint16_t counter;
+
+ // Start of currently logged activity, if any
+ watch_date_time start_time;
+
+ // Total seconds elapsed since logging started
+ uint16_t curr_total_sec;
+
+ // Total paused seconds in current log
+ uint16_t curr_pause_sec;
+
+ // Helps us handle 1/64 ticks during transmission; including countdown timer
+ chirpy_tick_state_t chirpy_tick_state;
+
+ // Used by chirpy encoder during transmission
+ chirpy_encoder_state_t chirpy_encoder_state;
+
+ // 0: Running normally
+ // 1: In LE mode
+ // 2: Just woke up from LE mode. Will go to 0 after ignoring ALARM_BUTTON_UP.
+ uint8_t le_state;
+
+} activity_state_t;
+
+#define ACTIVITY_BUF_SZ 14
+
+// Temp buffer used for sprintf'ing content for the display.
+char activity_buf[ACTIVITY_BUF_SZ];
+
+// Needed by _activity_get_next_byte to keep track of where we are in transmission
+uint16_t *activity_seq_pos;
+
+static void _activity_clear_buffers() {
+ // Clear activity buffer; 0xcd is good for diagnostics
+ memset(activity_log_buffer, 0xcd, ACTIVITY_LOG_SZ * sizeof(activity_item_t));
+ // Clear display buffer
+ memset(activity_buf, 0, ACTIVITY_BUF_SZ);
+}
+
+static void _activity_display_choice(activity_state_t *state);
+static void _activity_update_logging_screen(movement_settings_t *settings, activity_state_t *state);
+static uint8_t _activity_get_next_byte(uint8_t *next_byte);
+
+void activity_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) {
+ (void)settings;
+ (void)watch_face_index;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(activity_state_t));
+ memset(*context_ptr, 0, sizeof(activity_state_t));
+ // This happens only at boot
+ _activity_clear_buffers();
+ }
+ // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void activity_face_activate(movement_settings_t *settings, void *context) {
+ (void)settings;
+ (void)context;
+
+ // Not using this function. Calling _activity_activate from the event handler.
+ // That is what we get both when the face is shown upon navigation by MODE,
+ // and when waking from low energy state.
+}
+
+// Called from the ACTIVATE event handler in the loop
+static void _activity_activate(movement_settings_t *settings, activity_state_t *state) {
+ // If waking from low-energy state and currently logging: update seconds values
+ // Those are not up-to-date because ticks have not been coming
+ if (state->le_state != 0 && state->mode == ACTM_LOGGING) {
+ state->le_state = 2;
+ watch_date_time now = watch_rtc_get_date_time();
+ uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
+ uint32_t start_timestamp = watch_utility_date_time_to_unix_time(state->start_time, 0);
+ uint32_t total_seconds = now_timestamp - start_timestamp;
+ state->curr_total_sec = total_seconds;
+ _activity_update_logging_screen(settings, state);
+ }
+ // Regular activation: start from defaults
+ else {
+ state->le_state = 0;
+ state->mode = 0;
+ state->type_ix = 0;
+ _activity_display_choice(state);
+ }
+}
+
+static void _activity_display_choice(activity_state_t *state) {
+ watch_display_string("AC", 0);
+ // If buffer is full: We say "FULL"
+ if (activity_log_count >= ACTIVITY_LOG_SZ) {
+ watch_display_string(" FULL ", 4);
+ }
+ // Otherwise, we show currently activity
+ else {
+ uint8_t activity_ix = enabled_activities[state->type_ix];
+ const char *name = activity_names[activity_ix];
+ watch_display_string((char *)name, 4);
+ }
+}
+
+const uint8_t activity_anim_pixels[][2] = {
+ {1, 4}, // TL
+ {0, 5}, // BL
+ {0, 6}, // BOT
+ {1, 6}, // BR
+ {2, 5}, // TR
+ {2, 4}, // TOP
+ // {2, 4}, // MID
+};
+
+static void _activity_update_logging_screen(movement_settings_t *settings, activity_state_t *state) {
+ watch_duration_t duration;
+
+ watch_display_string("AC ", 0);
+
+ // If we're in LE state: per-minute update is special
+ if (state->le_state == 1) {
+ watch_date_time now = watch_rtc_get_date_time();
+ uint32_t now_timestamp = watch_utility_date_time_to_unix_time(now, 0);
+ uint32_t start_timestamp = watch_utility_date_time_to_unix_time(state->start_time, 0);
+ uint32_t total_seconds = now_timestamp - start_timestamp;
+ duration = watch_utility_seconds_to_duration(total_seconds);
+ sprintf(activity_buf, " %d%02d ", duration.hours, duration.minutes);
+ watch_display_string(activity_buf, 4);
+ watch_set_colon();
+ watch_set_indicator(WATCH_INDICATOR_LAP);
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ watch_clear_indicator(WATCH_INDICATOR_24H);
+ return;
+ }
+
+ // Show elapsed time, or PAUSE
+ if ((state->counter % 5) < 3) {
+ watch_set_indicator(WATCH_INDICATOR_LAP);
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ watch_clear_indicator(WATCH_INDICATOR_24H);
+ if (state->mode == ACTM_PAUSED) {
+ watch_display_string(" PAUSE", 4);
+ watch_clear_colon();
+ } else {
+ duration = watch_utility_seconds_to_duration(state->curr_total_sec);
+ // Under 10 minutes: M:SS
+ if (state->curr_total_sec < 600) {
+ sprintf(activity_buf, " %01d%02d", duration.minutes, duration.seconds);
+ watch_display_string(activity_buf, 4);
+ watch_clear_colon();
+ }
+ // Under an hour: MM:SS
+ else if (state->curr_total_sec < 3600) {
+ sprintf(activity_buf, " %02d%02d", duration.minutes, duration.seconds);
+ watch_display_string(activity_buf, 4);
+ watch_clear_colon();
+ }
+ // Over an hour: H:MM:SS
+ // (We never go to two-digit hours; stop at 8)
+ else {
+ sprintf(activity_buf, " %d%02d%02d", duration.hours, duration.minutes, duration.seconds);
+ watch_display_string(activity_buf, 4);
+ watch_set_colon();
+ }
+ }
+ }
+ // Briefly, show time without seconds
+ else {
+ watch_clear_indicator(WATCH_INDICATOR_LAP);
+ watch_date_time now = watch_rtc_get_date_time();
+ uint8_t hour = now.unit.hour;
+ if (!settings->bit.clock_mode_24h) {
+ watch_clear_indicator(WATCH_INDICATOR_24H);
+ if (hour < 12)
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ else
+ watch_set_indicator(WATCH_INDICATOR_PM);
+ hour %= 12;
+ if (hour == 0) hour = 12;
+ }
+ else {
+ watch_set_indicator(WATCH_INDICATOR_24H);
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ }
+ sprintf(activity_buf, "%2d%02d ", hour, now.unit.minute);
+ watch_set_colon();
+ watch_display_string(activity_buf, 4);
+ }
+}
+
+static void _activity_quit_chirping() {
+ watch_clear_indicator(WATCH_INDICATOR_BELL);
+ watch_set_buzzer_off();
+ movement_request_tick_frequency(1);
+}
+
+static void _activity_chirp_tick_transmit(void *context) {
+ activity_state_t *state = (activity_state_t *)context;
+
+ uint8_t tone = chirpy_get_next_tone(&state->chirpy_encoder_state);
+ // Transmission over?
+ if (tone == 255) {
+ _activity_quit_chirping();
+ state->mode = ACTM_CHIRP;
+ state->counter = 0;
+ watch_display_string("AC CHIRP ", 0);
+ return;
+ }
+ uint16_t period = chirpy_get_tone_period(tone);
+ watch_set_buzzer_period(period);
+ watch_set_buzzer_on();
+}
+
+static void _activity_chirp_tick_countdown(void *context) {
+ activity_state_t *state = (activity_state_t *)context;
+
+ // Countdown over: start actual broadcast
+ if (state->chirpy_tick_state.seq_pos == 8 * 3) {
+ state->chirpy_tick_state.tick_compare = 3;
+ state->chirpy_tick_state.tick_count = 2; // tick_compare - 1, so it starts immediately
+ state->chirpy_tick_state.seq_pos = 0;
+ state->chirpy_tick_state.tick_fun = _activity_chirp_tick_transmit;
+ return;
+ }
+ // Sound or turn off buzzer
+ if ((state->chirpy_tick_state.seq_pos % 8) == 0) {
+ watch_set_buzzer_period(NotePeriods[BUZZER_NOTE_A5]);
+ watch_set_buzzer_on();
+ if (state->chirpy_tick_state.seq_pos == 0) {
+ watch_display_string(" --- ", 4);
+ } else if (state->chirpy_tick_state.seq_pos == 8) {
+ watch_display_string(" --", 5);
+ } else if (state->chirpy_tick_state.seq_pos == 16) {
+ watch_display_string(" -", 5);
+ }
+ } else if ((state->chirpy_tick_state.seq_pos % 8) == 1) {
+ watch_set_buzzer_off();
+ }
+ ++state->chirpy_tick_state.seq_pos;
+}
+
+static uint8_t _activity_get_next_byte(uint8_t *next_byte) {
+ uint16_t num_bytes = 2 + activity_log_count * sizeof(activity_item_t);
+ uint16_t pos = *activity_seq_pos;
+
+ // Init counter
+ if (pos == 0) {
+ sprintf(activity_buf, "%3d", activity_log_count);
+ watch_display_string(activity_buf, 5);
+ }
+
+ if (pos == num_bytes) {
+ return 0;
+ }
+ // Two-byte prefix
+ if (pos < 2) {
+ (*next_byte) = activity_chirpy_prefix[pos];
+ }
+ // Data
+ else {
+ pos -= 2;
+ uint16_t ix = pos / sizeof(activity_item_t);
+ const activity_item_t *itm = &activity_log_buffer[ix];
+ uint16_t ofs = pos % sizeof(activity_item_t);
+
+ // Update counter when starting new item
+ if (ofs == 0) {
+ sprintf(activity_buf, "%3d", activity_log_count - ix);
+ watch_display_string(activity_buf, 5);
+ }
+
+ // Do this the hard way, byte by byte, to avoid high/low endedness issues
+ // Higher order bytes first, is our serialization format
+ uint8_t val;
+ // watch_date_time start_time;
+ // uint16_t total_sec;
+ // uint16_t pause_sec;
+ // uint8_t activity_type;
+ if (ofs == 0)
+ val = (itm->start_time.reg & 0xff000000) >> 24;
+ else if (ofs == 1)
+ val = (itm->start_time.reg & 0x00ff0000) >> 16;
+ else if (ofs == 2)
+ val = (itm->start_time.reg & 0x0000ff00) >> 8;
+ else if (ofs == 3)
+ val = (itm->start_time.reg & 0x000000ff);
+ else if (ofs == 4)
+ val = (itm->total_sec & 0xff00) >> 8;
+ else if (ofs == 5)
+ val = (itm->total_sec & 0x00ff);
+ else if (ofs == 6)
+ val = (itm->pause_sec & 0xff00) >> 8;
+ else if (ofs == 7)
+ val = (itm->pause_sec & 0x00ff);
+ else
+ val = itm->activity_type;
+ (*next_byte) = val;
+ }
+ ++(*activity_seq_pos);
+ return 1;
+}
+
+static void _activity_finish_logging(activity_state_t *state) {
+ // Save this activity
+ // If shorter than minimum for log: don't save
+ // Sanity check about buffer length. This should never happen, but also we never want to overrun by error
+ if (state->curr_total_sec >= activity_min_length_sec && activity_log_count + 1 < ACTIVITY_LOG_SZ) {
+ activity_item_t *itm = &activity_log_buffer[activity_log_count];
+ itm->start_time = state->start_time;
+ itm->total_sec = state->curr_total_sec;
+ itm->pause_sec = state->curr_pause_sec;
+ itm->activity_type = state->type_ix;
+ ++activity_log_count;
+ }
+
+ // Go to DONE animation
+ // TODO: Not in LE mode
+ state->mode = ACTM_DONE;
+ watch_clear_indicator(WATCH_INDICATOR_LAP);
+ movement_request_tick_frequency(2);
+ state->counter = 6 * 1;
+ watch_clear_display();
+ watch_display_string("AC dONE ", 0);
+}
+
+static void _activity_handle_tick(movement_settings_t *settings, activity_state_t *state) {
+ // Display stopwatch-like duration while logging, alternating with time
+ if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
+ ++state->counter;
+ ++state->curr_total_sec;
+ if (state->mode == ACTM_PAUSED)
+ ++state->curr_pause_sec;
+ // If we've reached max activity length: finish logging
+ if (state->curr_total_sec == MAX_ACTIVITY_SECONDS) {
+ _activity_finish_logging(state);
+ }
+ // Still logging: refresh display
+ else {
+ _activity_update_logging_screen(settings, state);
+ }
+ }
+ // Display countown animation, and exit face when down
+ else if (state->mode == ACTM_DONE) {
+ if (state->counter == 0) {
+ movement_move_to_face(0);
+ movement_request_tick_frequency(1);
+ }
+ else {
+ uint8_t cd = state->counter % 6;
+ watch_clear_pixel(activity_anim_pixels[cd][0], activity_anim_pixels[cd][1]);
+ --state->counter;
+ cd = state->counter % 6;
+ watch_set_pixel(activity_anim_pixels[cd][0], activity_anim_pixels[cd][1]);
+ }
+ }
+ // Log size, chirp, clear: return to choose after some time
+ else if (state->mode == ACTM_LOGSIZE || state->mode == ACTM_CHIRP || state->mode == ACTM_CLEAR) {
+ ++state->counter;
+ // Leave Log Size after 20 seconds
+ // Leave Clear after only 10: this is danger zone, we don't like hanging around here
+ // Leave Chirp after 2 minutes: most likely need to the time to fiddle with mic & Chirpy RX on the computer
+ uint16_t timeout = 20;
+ if (state->mode == ACTM_CLEAR) timeout = 10;
+ else if (state->mode == ACTM_CHIRP) timeout = 120;
+ if (state->counter > timeout) {
+ state->mode = ACTM_CHOOSE;
+ _activity_display_choice(state);
+ }
+ }
+ // Chirping
+ else if (state->mode == ACTM_CHIRPING) {
+ ++state->chirpy_tick_state.tick_count;
+ if (state->chirpy_tick_state.tick_count == state->chirpy_tick_state.tick_compare) {
+ state->chirpy_tick_state.tick_count = 0;
+ state->chirpy_tick_state.tick_fun(state);
+ }
+ }
+ // Clear confirm: blink CLEAR
+ else if (state->mode == ACTM_CLEAR_CONFIRM) {
+ ++state->counter;
+ if ((state->counter % 2) == 0)
+ watch_display_string("CLEAR ", 4);
+ else
+ watch_display_string(" ", 4);
+ if (state->counter > 12) {
+ state->mode = ACTM_CHOOSE;
+ _activity_display_choice(state);
+ movement_request_tick_frequency(1);
+ }
+ }
+ // Clear done: fill up zeroes, then return to choose screen
+ else if (state->mode == ACTM_CLEAR_DONE) {
+ ++state->counter;
+ // Animation done
+ if (state->counter == 7) {
+ state->mode = ACTM_CHOOSE;
+ _activity_display_choice(state);
+ movement_request_tick_frequency(1);
+ return;
+ }
+ // Display current state of animation
+ sprintf(activity_buf, " ");
+ uint8_t nZeros = state->counter + 1;
+ if (nZeros > 6) nZeros = 6;
+ for (uint8_t i = 0; i < nZeros; ++i) {
+ activity_buf[i] = '0';
+ }
+ watch_display_string(activity_buf, 4);
+ }
+}
+
+static void _activity_alarm_long(movement_settings_t *settings, activity_state_t *state) {
+ // On choose face: start logging activity
+ if (state->mode == ACTM_CHOOSE) {
+ // If buffer is full: Ignore this long press
+ if (activity_log_count >= ACTIVITY_LOG_SZ)
+ return;
+ // OK, we go ahead and start logging
+ state->start_time = watch_rtc_get_date_time();
+ state->curr_total_sec = 0;
+ state->curr_pause_sec = 0;
+ state->counter = -1;
+ state->mode = ACTM_LOGGING;
+ watch_set_indicator(WATCH_INDICATOR_LAP);
+ _activity_update_logging_screen(settings, state);
+ }
+ // If logging or paused: end logging
+ else if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
+ _activity_finish_logging(state);
+ }
+ // If chirp: kick off chirping
+ else if (state->mode == ACTM_CHIRP) {
+ // Set up our tick handling for countdown beeps
+ activity_seq_pos = &state->chirpy_tick_state.seq_pos;
+ state->chirpy_tick_state.tick_compare = 8;
+ state->chirpy_tick_state.tick_count = 7; // tick_compare - 1, so it starts immediately
+ state->chirpy_tick_state.seq_pos = 0;
+ state->chirpy_tick_state.tick_fun = _activity_chirp_tick_countdown;
+ // Set up chirpy encoder
+ chirpy_init_encoder(&state->chirpy_encoder_state, _activity_get_next_byte);
+ // Show bell; switch to 64/sec ticks
+ watch_set_indicator(WATCH_INDICATOR_BELL);
+ movement_request_tick_frequency(64);
+ state->mode = ACTM_CHIRPING;
+ }
+ // If clear: confirm (unless empty)
+ else if (state->mode == ACTM_CLEAR) {
+ if (activity_log_count == 0)
+ return;
+ state->mode = ACTM_CLEAR_CONFIRM;
+ state->counter = -1;
+ movement_request_tick_frequency(4);
+ }
+ // If clear confirm: do clear.
+ else if (state->mode == ACTM_CLEAR_CONFIRM) {
+ _activity_clear_buffers();
+ activity_log_count = 0;
+ state->mode = ACTM_CLEAR_DONE;
+ state->counter = -1;
+ watch_display_string("0 ", 4);
+ movement_request_tick_frequency(2);
+ }
+}
+
+static void _activity_alarm_short(movement_settings_t *settings, activity_state_t *state) {
+ // In the choose face, short ALARM cycles through activities
+ if (state->mode == ACTM_CHOOSE) {
+ state->type_ix = (state->type_ix + 1) % num_enabled_activities;
+ _activity_display_choice(state);
+ }
+ // If logging: pause
+ else if (state->mode == ACTM_LOGGING) {
+ state->mode = ACTM_PAUSED;
+ state->counter = 0;
+ _activity_update_logging_screen(settings, state);
+ }
+ // If paused: Update paused seconds count and return to logging
+ else if (state->mode == ACTM_PAUSED) {
+ state->mode = ACTM_LOGGING;
+ state->counter = 0;
+ _activity_update_logging_screen(settings, state);
+ }
+ // If chirping: stoppit
+ else if (state->mode == ACTM_CHIRPING) {
+ _activity_quit_chirping();
+ state->mode = ACTM_CHIRP;
+ state->counter = 0;
+ watch_display_string("AC CHIRP ", 0);
+ }
+}
+
+static void _activity_light_short(activity_state_t *state) {
+ // If choose face: move to log size
+ if (state->mode == ACTM_CHOOSE) {
+ state->mode = ACTM_LOGSIZE;
+ state->counter = 0;
+ sprintf(activity_buf, "AC L#g%3d", activity_log_count);
+ watch_display_string(activity_buf, 0);
+ }
+ // If log size face: move to chirp
+ else if (state->mode == ACTM_LOGSIZE) {
+ state->mode = ACTM_CHIRP;
+ state->counter = 0;
+ watch_display_string("AC CHIRP ", 0);
+ }
+ // If chirp face: move to clear
+ else if (state->mode == ACTM_CHIRP) {
+ state->mode = ACTM_CLEAR;
+ state->counter = 0;
+ watch_display_string("AC CLEAR ", 0);
+ }
+ // If clear face: return to choose face
+ else if (state->mode == ACTM_CLEAR || state->mode == ACTM_CLEAR_CONFIRM) {
+ state->mode = ACTM_CHOOSE;
+ _activity_display_choice(state);
+ movement_request_tick_frequency(1);
+ }
+ // While logging or paused, light is light
+ else if (state->mode == ACTM_LOGGING || state->mode == ACTM_PAUSED) {
+ movement_illuminate_led();
+ }
+ // Otherwise, we don't do light.
+}
+
+bool activity_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ activity_state_t *state = (activity_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ _activity_activate(settings, state);
+ break;
+ case EVENT_TICK:
+ _activity_handle_tick(settings, state);
+ break;
+ case EVENT_MODE_BUTTON_UP:
+ if (state->mode != ACTM_LOGGING && state->mode != ACTM_PAUSED && state->mode != ACTM_CHIRPING) {
+ movement_request_tick_frequency(1);
+ movement_move_to_next_face();
+ }
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ _activity_light_short(state);
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ // We also receive ALARM press that woke us up from LE state
+ // Don't want to act on that as if it were a real button press for us
+ if (state->le_state != 2)
+ _activity_alarm_short(settings, state);
+ else
+ state->le_state = 0;
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ _activity_alarm_long(settings, state);
+ break;
+ case EVENT_TIMEOUT:
+ if (state->mode != ACTM_LOGGING && state->mode != ACTM_PAUSED &&
+ state->mode != ACTM_CHIRP && state->mode != ACTM_CHIRPING) {
+ movement_request_tick_frequency(1);
+ movement_move_to_face(0);
+ }
+ break;
+ case EVENT_LOW_ENERGY_UPDATE:
+ state->le_state = 1;
+ // If we're in paused logging mode: let's lose this activity. Pause is not meant for over an hour.
+ if (state->mode == ACTM_PAUSED) {
+ // When waking, face will revert to default screen
+ state->mode = ACTM_CHOOSE;
+ watch_display_string("AC SLEEP ", 0);
+ watch_clear_colon();
+ watch_clear_indicator(WATCH_INDICATOR_LAP);
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ }
+ else {
+ _activity_update_logging_screen(settings, state);
+ watch_start_tick_animation(500);
+ }
+ break;
+ default:
+ movement_default_loop_handler(event, settings);
+ break;
+ }
+
+ // Return true if the watch can enter standby mode. False needed when chirping.
+ if (state->mode == ACTM_CHIRPING)
+ return false;
+ else
+ return true;
+}
+
+void activity_face_resign(movement_settings_t *settings, void *context) {
+ (void)settings;
+ (void)context;
+
+ // Face should only ever temporarily request a higher frequency, so by the time we're resigning,
+ // this should not be needed. But we don't want an error to create a situation that drains the battery.
+ // Rather do this defensively here.
+ movement_request_tick_frequency(1);
+}
diff --git a/movement/watch_faces/complication/activity_face.h b/movement/watch_faces/complication/activity_face.h
new file mode 100644
index 00000000..c72f7099
--- /dev/null
+++ b/movement/watch_faces/complication/activity_face.h
@@ -0,0 +1,89 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef ACTIVITY_FACE_H_
+#define ACTIVITY_FACE_H_
+
+#include "movement.h"
+
+/*
+ * ACTIVITY WATCH FACE
+ *
+ * The Activity face lets you record activities like you would do with a fitness watch.
+ * It supports different activities like running, biking, rowing etc., and for each recorded activity
+ * it stores when it started and how long it was.
+ *
+ * You can save up to 99 activities this way. Every once in a while you can chirp them out
+ * using the watch's piezo buzzer as a modem, then clear the log in the watch.
+ * To record and decode a chirpy transmission on your computer, you can use the web app here:
+ * https://jealousmarkup.xyz/off/chirpy/rx/
+ *
+ * Using the face
+ *
+ * When you activate the face, it starts with the first screen to select the activity you want to log.
+ * ALARM cycles through the list of activities.
+ * LONG ALARM starts logging.
+ * While logging is in progress, the face alternates between the elapsed time and the current time.
+ * You can press ALARM to pause (e.g., while you hop in to the baker's for a croissant during your jog).
+ * Pressing ALARM again resumes the activity.
+ * LONG ALARM stops logging and saves the activity.
+ *
+ * When you're not loggin, you can press LIGHT to access the secondary faces.
+ * LIGHT #1 => Shows the size of the log (how many activities have been recorded).
+ * LIGHT #2 => The screen to chirp out the data. Press LONG ALARM to start chirping.
+ * LIGHT #3 => The screen to clear the log in the watch. Press LONG ALARM twice to clear data.
+ *
+ * Quirky details
+ *
+ * The face will discard short activities (less than a minute) when you press LONG ALARM to finish logging.
+ * These were probably logged by mistake, and it's better to save slots and chirping battery power for
+ * stuff that really matters.
+ *
+ * The face will continue to record an activity past the normal one-hour mark, when the watch
+ * enters low energy mode. However, it will always stop at 8 hours. If an activity takes that long,
+ * you probably just forgot to stop logging.
+ *
+ * The log is stored in regular memory. It will be lost when you remove the battery, so make
+ * sure you chirp it out before taking the watch apart.
+ *
+ * See the top of activity_face.c for some customization options. What you most likely want to do
+ * is reduce the list of activities shown on the first screen to the ones you are regularly doing.
+ *
+ */
+
+void activity_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void activity_face_activate(movement_settings_t *settings, void *context);
+bool activity_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void activity_face_resign(movement_settings_t *settings, void *context);
+
+#define activity_face ((const watch_face_t){ \
+ activity_face_setup, \
+ activity_face_activate, \
+ activity_face_loop, \
+ activity_face_resign, \
+ NULL, \
+})
+
+#endif // ACTIVITY_FACE_H_
+
diff --git a/movement/watch_faces/complication/breathing_face.c b/movement/watch_faces/complication/breathing_face.c
new file mode 100644
index 00000000..8c1fdec7
--- /dev/null
+++ b/movement/watch_faces/complication/breathing_face.c
@@ -0,0 +1,208 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Bernd Plontsch
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include "breathing_face.h"
+#include "watch.h"
+
+typedef struct {
+ uint8_t current_stage;
+ bool sound_on;
+} breathing_state_t;
+
+static void beep_in (void);
+static void beep_in_hold (void);
+static void beep_out (void);
+static void beep_out_hold (void);
+
+void breathing_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ // These next two lines just silence the compiler warnings associated with unused parameters.
+ // We have no use for the settings or the watch_face_index, so we make that explicit here.
+ (void) settings;
+ (void) watch_face_index;
+ // At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
+ if (*context_ptr == NULL) {
+ // in this case, we allocate an area of memory sufficient to store the stuff we need to track.
+ *context_ptr = malloc(sizeof(breathing_state_t));
+ }
+}
+
+void breathing_face_activate(movement_settings_t *settings, void *context) {
+ // same as above: silence the warning, we don't need to check the settings.
+ (void) settings;
+ // we do however need to set some things in our context. Here we cast it to the correct type...
+ breathing_state_t *state = (breathing_state_t *)context;
+ // ...and set the initial state of our watch face.
+ state->current_stage = 0;
+ state->sound_on = true;
+}
+
+const int NOTE_LENGTH = 80;
+
+void beep_in (void) {
+ const BuzzerNote notes[] = {
+ BUZZER_NOTE_C4,
+ BUZZER_NOTE_D4,
+ BUZZER_NOTE_E4,
+ };
+ const uint16_t durations[] = {
+ NOTE_LENGTH,
+ NOTE_LENGTH,
+ NOTE_LENGTH
+ };
+ for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
+ watch_buzzer_play_note(notes[i], durations[i]);
+ }
+}
+
+void beep_in_hold (void) {
+ const BuzzerNote notes[] = {
+ BUZZER_NOTE_E4,
+ BUZZER_NOTE_REST,
+ BUZZER_NOTE_E4,
+ };
+ const uint16_t durations[] = {
+ NOTE_LENGTH,
+ NOTE_LENGTH * 2,
+ NOTE_LENGTH,
+ };
+ for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
+ watch_buzzer_play_note(notes[i], durations[i]);
+ }
+}
+
+void beep_out (void) {
+ const BuzzerNote notes[] = {
+ BUZZER_NOTE_E4,
+ BUZZER_NOTE_D4,
+ BUZZER_NOTE_C4,
+ };
+ const uint16_t durations[] = {
+ NOTE_LENGTH,
+ NOTE_LENGTH,
+ NOTE_LENGTH,
+ };
+ for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
+ watch_buzzer_play_note(notes[i], durations[i]);
+ }
+}
+
+void beep_out_hold (void) {
+ const BuzzerNote notes[] = {
+ BUZZER_NOTE_C4,
+ BUZZER_NOTE_REST * 2,
+ BUZZER_NOTE_C4,
+ };
+ const uint16_t durations[] = {
+ NOTE_LENGTH,
+ NOTE_LENGTH,
+ NOTE_LENGTH,
+ };
+ for(size_t i = 0, count = sizeof(notes) / sizeof(notes[0]); i < count; i++) {
+ watch_buzzer_play_note(notes[i], durations[i]);
+ }
+}
+
+bool breathing_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ (void) settings;
+ breathing_state_t *state = (breathing_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ case EVENT_TICK:
+
+ if (state->sound_on == true) {
+ watch_set_indicator(WATCH_INDICATOR_BELL);
+ } else {
+ watch_clear_indicator(WATCH_INDICATOR_BELL);
+ }
+
+ switch (state->current_stage)
+ {
+ case 0: { watch_display_string("Breath", 4); if (state->sound_on) beep_in(); } break;
+ case 1: watch_display_string("In 3", 4); break;
+ case 2: watch_display_string("In 2", 4); break;
+ case 3: watch_display_string("In 1", 4); break;
+
+ case 4: { watch_display_string("Hold 4", 4); if (state->sound_on) beep_in_hold(); } break;
+ case 5: watch_display_string("Hold 3", 4); break;
+ case 6: watch_display_string("Hold 2", 4); break;
+ case 7: watch_display_string("Hold 1", 4); break;
+
+ case 8: { watch_display_string("Ou t 4", 4); if (state->sound_on) beep_out(); } break;
+ case 9: watch_display_string("Ou t 3", 4); break;
+ case 10: watch_display_string("Ou t 2", 4); break;
+ case 11: watch_display_string("Ou t 1", 4); break;
+
+ case 12: { watch_display_string("Hold 4", 4); if (state->sound_on) beep_out_hold(); } break;
+ case 13: watch_display_string("Hold 3", 4); break;
+ case 14: watch_display_string("Hold 2", 4); break;
+ case 15: watch_display_string("Hold 1", 4); break;
+ default:
+ break;
+ }
+
+ // and increment it so that it will update on the next tick.
+ state->current_stage = (state->current_stage + 1) % 16;
+
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ state->sound_on = !state->sound_on;
+ if (state->sound_on == true) {
+ watch_set_indicator(WATCH_INDICATOR_BELL);
+ } else {
+ watch_clear_indicator(WATCH_INDICATOR_BELL);
+ }
+ break;
+ case EVENT_LOW_ENERGY_UPDATE:
+ // This low energy mode update occurs once a minute, if the watch face is in the
+ // foreground when Movement enters low energy mode. We have the option of supporting
+ // this mode, but since our watch face animates once a second, the "Hello there" face
+ // isn't very useful in this mode. So we choose not to support it. (continued below)
+ break;
+ case EVENT_TIMEOUT:
+ // ... Instead, we respond to the timeout event. This event happens after a configurable
+ // interval on screen (1-30 minutes). The watch will give us this event as a chance to
+ // resign control if we want to, and in this case, we do.
+ // This function will return the watch to the first screen (usually a simple clock),
+ // and it will do it long before the watch enters low energy mode. This ensures we
+ // won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
+
+ // movement_move_to_face(0);
+ break;
+ default:
+ movement_default_loop_handler(event, settings);
+ break;
+ }
+
+ return true;
+}
+
+void breathing_face_resign(movement_settings_t *settings, void *context) {
+ // our watch face, like most watch faces, has nothing special to do when resigning.
+ // watch faces that enable a peripheral or interact with a sensor may want to turn it off here.
+ (void) settings;
+ (void) context;
+}
diff --git a/movement/watch_faces/complication/breathing_face.h b/movement/watch_faces/complication/breathing_face.h
new file mode 100644
index 00000000..2ff947a2
--- /dev/null
+++ b/movement/watch_faces/complication/breathing_face.h
@@ -0,0 +1,43 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Bernd Plontsch
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BREATHING_FACE_H_
+#define BREATHING_FACE_H_
+
+#include "movement.h"
+
+void breathing_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void breathing_face_activate(movement_settings_t *settings, void *context);
+bool breathing_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void breathing_face_resign(movement_settings_t *settings, void *context);
+
+#define breathing_face ((const watch_face_t){ \
+ breathing_face_setup, \
+ breathing_face_activate, \
+ breathing_face_loop, \
+ breathing_face_resign, \
+ NULL, \
+})
+
+#endif // BREATHING_FACE_H_
diff --git a/movement/watch_faces/complication/day_one_face.c b/movement/watch_faces/complication/day_one_face.c
index 7ce43bfa..25ce1c23 100644
--- a/movement/watch_faces/complication/day_one_face.c
+++ b/movement/watch_faces/complication/day_one_face.c
@@ -33,7 +33,7 @@ static uint32_t _day_one_face_juliandaynum(uint16_t year, uint16_t month, uint16
}
static void _day_one_face_update(day_one_state_t state) {
- char buf[14];
+ char buf[15];
watch_date_time date_time = watch_rtc_get_date_time();
uint32_t julian_date = _day_one_face_juliandaynum(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day);
uint32_t julian_birthdate = _day_one_face_juliandaynum(state.birth_year, state.birth_month, state.birth_day);
diff --git a/movement/watch_faces/complication/discgolf_face.c b/movement/watch_faces/complication/discgolf_face.c
new file mode 100644
index 00000000..0852bf1f
--- /dev/null
+++ b/movement/watch_faces/complication/discgolf_face.c
@@ -0,0 +1,326 @@
+#include
+#include
+#include "discgolf_face.h"
+#include "watch.h" // Remember to change number of courses in this file
+#include "watch_utility.h"
+
+/*
+ * Keep track of scores in discgolf or golf!
+ * The watch face operates in three different modes:
+ *
+ * - dg_setting: Select a course
+ * Enter this mode by holding down the light button. The screen will display
+ * the label for the hole and the lowest score since last boot.
+ * Press alarm to loop through the holes. Press the light button to make a
+ * selection. This will reset all scores and start a new game in dg_idle mode.
+ *
+ * -dg_idle: We're playing a hole
+ * This either shows your current score relative to par, or the score for a
+ * particular hole.
+ * At the start of a game, press alarm to loop through the holes and leave it
+ * your starting hole. For optimal experience, play the course linearly after that
+ * If you're viewing the hole you're supposed to be playing, the watch face will
+ * display your score relative to par.
+ * Use the alarm button to view other holes than the one you're playing, in which
+ * case the input score for that hole will be displayed, in case it needs changing.
+ * Long press the alarm button to snap back to currently playing hole.
+ * To input scores for a hole in this mode, press the light button.
+ *
+ * -dg_scoring: Input score for a hole
+ * In this mode, if the score is 0 (hasn't been entered during this round),
+ * it will blink, indicating we're in scoring mode. Press the alarm button
+ * to increment the score up until 15, in which case it loops back to 0.
+ * Press the light button to save the score for that hole, advance one hole
+ * if you're not editing an already input score, and returning to idle mode.
+ *
+ * When all scores have been entered, the LAP indicator turns on. At that point, if we enter
+ * dg_setting to select a course, the score for that round is evaluated against the current
+ * lowest score for that course, and saved if it is better.
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Enter course data
+/* Initialize lowest scores with a high number */
+int8_t best[courses];
+
+static const uint8_t pars[][18] = {
+ { 3, 3, 4, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, // Grafarholt
+ { 3, 4, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3 }, // Gufunes
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 }, // Vífilsstaðir
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 }, // Dalvegur
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 }, // Laugardalur
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 }, // Guðmundarlundur
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Víðistaðatún
+ { 3, 3, 3, 4, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Fossvogur
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Klambratún
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Seljahverfi
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Fella- og Hóla
+};
+static const uint8_t holes[] = { // Number of holes on each course
+ 18,
+ 18,
+ 10,
+ 10,
+ 10,
+ 10,
+ 9,
+ 9,
+ 9,
+ 9,
+ 9
+};
+/* Two-letter descriptive labels, second field can only be A, B, C, D, E, F, H, I, J, L, N, O, R, T, U and X. */
+static const char labels[][2] = {
+ { 'G', 'H' },
+ { 'G', 'N' },
+ { 'V', 'I' },
+ { 'D', 'V' },
+ { 'L', 'A' },
+ { 'G', 'L' },
+ { 'V', 'T' },
+ { 'F', 'V' },
+ { 'K', 'T' },
+ { 'S', 'E' },
+ { 'F', 'H' }
+};
+
+// End of course data
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+/* Beep function */
+static inline void beep(movement_settings_t *settings) {
+ if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+}
+
+/* Prep for a new round */
+static inline void reset(discgolf_state_t *state) {
+ for (int i = 0; i < holes[state->course]; i++) {
+ state->scores[i] = 0;
+ }
+ state->hole = 1;
+ watch_clear_indicator(WATCH_INDICATOR_LAP);
+ return;
+}
+
+/* Total number of throws so far */
+static inline uint8_t score_sum(discgolf_state_t *state) {
+ uint8_t sum = 0;
+ for (int i = 0; i < holes[state->course]; i++) {
+ sum = sum + state->scores[i];
+ }
+ return sum;
+}
+
+/* Count how many holes have been played */
+static inline uint8_t count_played(discgolf_state_t *state) {
+ uint8_t holes_played = 0;
+ for (int i = 0; i < holes[state->course]; i++) {
+ if (state->scores[i] > 0) holes_played++;
+ }
+ return holes_played;
+}
+
+
+/* Calculate the current score relative to par */
+static inline int8_t calculate_score(discgolf_state_t *state) {
+ uint8_t par_sum = 0;
+ uint8_t score_sum = 0;
+
+ for (int i = 0; i < holes[state->course]; i++) {
+ if (state->scores[i] > 0) {
+ par_sum = par_sum + pars[state->course][i];
+ score_sum = score_sum + state->scores[i];
+ }
+ }
+ return score_sum - par_sum;
+}
+
+/* Store score if it's the best so far */
+static inline void store_best(discgolf_state_t *state) {
+ uint8_t played = count_played(state);
+ if ( played == holes[state->course] ) {
+ int8_t high_score = calculate_score(state);
+ if (high_score < best[state->course] ) {
+ best[state->course] = high_score;
+ }
+ }
+}
+
+/* Configuration at boot, the high score array can be initialized with your high scores if they're known */
+void discgolf_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) settings;
+ (void) watch_face_index;
+
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(discgolf_state_t));
+ discgolf_state_t *state = (discgolf_state_t *)*context_ptr;
+ memset(*context_ptr, 0, sizeof(discgolf_state_t));
+ state->hole = 1;
+ state->course = 0;
+ state->playing = holes[state->course] + 1;
+ for (int i = 0; i < courses; i++) best[i] = 99;
+ state->mode = dg_setting;
+ }
+}
+
+void discgolf_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ watch_clear_colon();
+ discgolf_state_t *state = (discgolf_state_t *)context;
+
+ /* If we were playing, go to current hole */
+ if (state->playing <= holes[0]) {
+ state->hole = state->playing;
+ }
+ /* Set LAP if round finished */
+ if (count_played(state) == holes[state->course] ) {
+ watch_set_indicator(WATCH_INDICATOR_LAP);
+ }
+ movement_request_tick_frequency(4);
+}
+
+bool discgolf_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ (void) settings;
+
+ discgolf_state_t *state = (discgolf_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_TIMEOUT:
+ /* Snap to first screen if we're not playing*/
+ if ( count_played(state) == holes[state->course] || state->mode == dg_setting) {
+ movement_move_to_face(0);
+ }
+ /* Cancel scoring if timed out */
+ if (state->mode == dg_scoring) {
+ state->scores[state->hole] = 0;
+ state->mode = dg_idle;
+ }
+ break;
+ /* Advance if not scoring */
+ case EVENT_MODE_BUTTON_UP:
+ if ( state->mode != dg_scoring ) {
+ movement_move_to_next_face();
+ }
+ break;
+ /* Go to default face if not scoring */
+ case EVENT_MODE_LONG_PRESS:
+ if ( state->mode != dg_scoring ) {
+ movement_move_to_face(0);
+ }
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ switch ( state->mode ) {
+ case dg_idle:
+ /* Check if selected hole is the first one */
+ if ( score_sum(state) == 0 ) {
+ state->playing = state->hole;
+ }
+ /* Enter scoring mode */
+ state->mode = dg_scoring;
+ break;
+ case dg_scoring:
+ /* Set the LAP indicator if all scores are entered */
+ if (count_played(state) == holes[state->course] ) {
+ watch_set_indicator(WATCH_INDICATOR_LAP);
+ }
+ /* Advance to next hole if not editing previously set score */
+ if ( state->hole == state->playing ) {
+ if (state->hole < holes[state->course]) state->hole++;
+ else state->hole = 1;
+ if (state->playing < holes[state->course]) state->playing++;
+ else state->playing = 1;
+ }
+ /* Return to idle */
+ state->mode = dg_idle;
+ break;
+ case dg_setting:
+ /* Return to idle */
+ state->playing = holes[state->course] + 1;
+ state->mode = dg_idle;
+ break;
+ }
+ beep(settings);
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ switch (state->mode) {
+ /* Setting, loop through courses */
+ case dg_setting:
+ state->course = (state->course + 1) % courses;
+ break;
+ /* Scoring, increment score for current hole */
+ case dg_scoring:
+ state->scores[state->hole - 1] = (state->scores[state->hole - 1] + 1) % 16; // Loop around at 15
+ break;
+ /* Idle, loop through holes */
+ case dg_idle:
+ if (state->hole < holes[state->course]) {
+ state->hole++;
+ } else { state->hole = 1; }
+ break;
+ }
+ break;
+ case EVENT_LIGHT_LONG_PRESS:
+ /* Enter setting mode, reset state */
+ if ( state->mode == dg_idle ) {
+ state->mode = dg_setting;
+ store_best(state);
+ reset(state);
+ beep(settings);
+ }
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ /* Snap back to currently playing hole if we've established one*/
+ if ( (state->mode == dg_idle) && (state->hole != state->playing) && (state->playing <= holes[state->course]) ) {
+ state->hole = state->playing;
+ beep(settings);
+ }
+ break;
+ default:
+ break;
+ }
+
+ char buf[21];
+ char prefix;
+ int8_t diff;
+
+ switch (state->mode) {
+ /* Setting mode, display course label and high score */
+ case dg_setting:
+ if ( best[state->course] < 0 ) {
+ prefix = '-';
+ } else { prefix = ' '; }
+ sprintf(buf, "%c%c %c%2d ", labels[state->course][0], labels[state->course][1], prefix, abs(best[state->course]));
+ break;
+ /* Idle, show relative or input score */
+ case dg_idle:
+ if (state->hole == state->playing) {
+ diff = calculate_score(state);
+ if ( diff < 0 ) {
+ prefix = '-';
+ } else { prefix = ' '; }
+ sprintf(buf, "%c%c%2d %c%2d ", labels[state->course][0], labels[state->course][1], state->hole, prefix, abs(diff));
+ } else {
+ sprintf(buf, "%c%c%2d %2d ", labels[state->course][0], labels[state->course][1], state->hole, state->scores[state->hole - 1]);
+ }
+ break;
+ /* Scoring, show set score */
+ case dg_scoring:
+ sprintf(buf, "%c%c%2d %2d ", labels[state->course][0], labels[state->course][1], state->hole, state->scores[state->hole - 1]);
+ break;
+ }
+
+ /* Blink during scoring */
+ if (event.subsecond % 2 && state->mode == dg_scoring) {
+ buf[6] = buf[7] = ' ';
+ }
+
+ /* Draw screen */
+ watch_display_string(buf, 0);
+
+ return true;
+}
+
+void discgolf_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+ watch_clear_indicator(WATCH_INDICATOR_LAP);
+}
diff --git a/movement/watch_faces/complication/discgolf_face.h b/movement/watch_faces/complication/discgolf_face.h
new file mode 100644
index 00000000..5e8068e0
--- /dev/null
+++ b/movement/watch_faces/complication/discgolf_face.h
@@ -0,0 +1,95 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Þorsteinn Jón Gautason
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Keep track of scores in discgolf or golf!
+ * The watch face operates in three different modes:
+ *
+ * - dg_setting: Select a course
+ * Enter this mode by holding down the light button. The screen will display
+ * the label for the hole and the lowest score since last boot.
+ * Press alarm to loop through the holes. Press the light button to make a
+ * selection. This will reset all scores and start a new game in dg_idle mode.
+ *
+ * -dg_idle: We're playing a hole
+ * This either shows your current score relative to par, or the score for a
+ * particular hole.
+ * At the start of a game, press alarm to loop through the holes and leave it
+ * your starting hole. For optimal experience, play the course linearly after that
+ * If you're viewing the hole you're supposed to be playing, the watch face will
+ * display your score relative to par.
+ * Use the alarm button to view other holes than the one you're playing, in which
+ * case the input score for that hole will be displayed, in case it needs changing.
+ * Long press the alarm button to snap back to currently playing hole.
+ * To input scores for a hole in this mode, press the light button.
+ *
+ * -dg_scoring: Input score for a hole
+ * In this mode, if the score is 0 (hasn't been entered during this round),
+ * it will blink, indicating we're in scoring mode. Press the alarm button
+ * to increment the score up until 15, in which case it loops back to 0.
+ * Press the light button to save the score for that hole, advance one hole
+ * if you're not editing an already input score, and returning to idle mode.
+ *
+ * When all scores have been entered, the LAP indicator turns on. At that point, if we enter
+ * dg_setting to select a course, the score for that round is evaluated against the current
+ * lowest score for that course, and saved if it is better.
+*/
+
+
+#ifndef DISCGOLF_FACE_H_
+#define DISCGOLF_FACE_H_
+
+#include "movement.h"
+#define courses 11
+
+typedef enum {
+ dg_setting, // We are selecting a course
+ dg_scoring, // We are inputting our score
+ dg_idle, // We have input our score and are playing a hole
+} discgolf_mode_t;
+
+typedef struct {
+ uint8_t course; // Index for course selection, from 0
+ uint8_t hole; // Index for current hole, from 1
+ uint8_t playing; // Current hole
+ int scores[18]; // Scores for each played hole
+ discgolf_mode_t mode; // Watch face mode
+} discgolf_state_t;
+
+void discgolf_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void discgolf_face_activate(movement_settings_t *settings, void *context);
+bool discgolf_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void discgolf_face_resign(movement_settings_t *settings, void *context);
+
+#define discgolf_face ((const watch_face_t){ \
+ discgolf_face_setup, \
+ discgolf_face_activate, \
+ discgolf_face_loop, \
+ discgolf_face_resign, \
+ NULL, \
+})
+
+#endif // DISCGOLF_FACE_H_
diff --git a/movement/watch_faces/complication/dual_timer_face.c b/movement/watch_faces/complication/dual_timer_face.c
new file mode 100644
index 00000000..f98c35b4
--- /dev/null
+++ b/movement/watch_faces/complication/dual_timer_face.c
@@ -0,0 +1,334 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ * Copyright (c) 2022 Andreas Nebinger
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include "dual_timer_face.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "watch_rtc.h"
+
+/*
+ * IMPORTANT: This watch face uses the same TC2 callback counter as the Stock Stopwatch
+ * watch-face. It works through calling a global handler function. The two watch-faces
+ * therefore can't coexist within the same firmware. If you want to compile this watch-face
+ * then you need to remove the line <../watch_faces/complication/stock_stopwatch_face.c \>
+ * from the Makefile.
+ */
+
+// FROM stock_stopwatch_face.c ////////////////////////////////////////////////
+// Copyright (c) 2022 Andreas Nebinger
+
+#if __EMSCRIPTEN__
+#include
+#include
+#else
+#include "../../../watch-library/hardware/include/saml22j18a.h"
+#include "../../../watch-library/hardware/include/component/tc.h"
+#include "../../../watch-library/hardware/hri/hri_tc_l22.h"
+#endif
+
+static const watch_date_time distant_future = {.unit = {0, 0, 0, 1, 1, 63}};
+static bool _is_running;
+static uint32_t _ticks;
+
+#if __EMSCRIPTEN__
+
+static long _em_interval_id = 0;
+
+void em_dual_timer_cb_handler(void *userData) {
+ // interrupt handler for emscripten 128 Hz callbacks
+ (void) userData;
+ _ticks++;
+}
+
+static void _dual_timer_cb_initialize() { }
+
+static inline void _dual_timer_cb_stop() {
+ emscripten_clear_interval(_em_interval_id);
+ _em_interval_id = 0;
+ _is_running = false;
+}
+
+static inline void _dual_timer_cb_start() {
+ // initiate 128 hz callback
+ _em_interval_id = emscripten_set_interval(em_dual_timer_cb_handler, (double)(1000/128), (void *)NULL);
+}
+
+#else
+
+static inline void _dual_timer_cb_start() {
+ // start the TC2 timer
+ hri_tc_set_CTRLA_ENABLE_bit(TC2);
+ _is_running = true;
+}
+
+static inline void _dual_timer_cb_stop() {
+ // stop the TC2 timer
+ hri_tc_clear_CTRLA_ENABLE_bit(TC2);
+ _is_running = false;
+}
+
+static void _dual_timer_cb_initialize() {
+ // setup and initialize TC2 for a 64 Hz interrupt
+ hri_mclk_set_APBCMASK_TC2_bit(MCLK);
+ hri_gclk_write_PCHCTRL_reg(GCLK, TC2_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN);
+ _dual_timer_cb_stop();
+ hri_tc_write_CTRLA_reg(TC2, TC_CTRLA_SWRST);
+ hri_tc_wait_for_sync(TC2, TC_SYNCBUSY_SWRST);
+ hri_tc_write_CTRLA_reg(TC2, TC_CTRLA_PRESCALER_DIV64 | // 32 Khz divided by 64 divided by 4 results in a 128 Hz interrupt
+ TC_CTRLA_MODE_COUNT8 |
+ TC_CTRLA_RUNSTDBY);
+ hri_tccount8_write_PER_reg(TC2, 3);
+ hri_tc_set_INTEN_OVF_bit(TC2);
+ NVIC_ClearPendingIRQ(TC2_IRQn);
+ NVIC_EnableIRQ (TC2_IRQn);
+}
+
+// you need to take stock_stopwatch.c out of the Makefile or this will create a conflict
+// you have to choose between one of the stopwatches
+ void TC2_Handler(void) {
+ // interrupt handler for TC2 (globally!)
+ _ticks++;
+ TC2->COUNT8.INTFLAG.reg |= TC_INTFLAG_OVF;
+}
+
+#endif
+
+// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
+
+/** @brief converts tick counts to duration struct for time display
+ */
+static dual_timer_duration_t ticks_to_duration(uint32_t ticks) {
+ dual_timer_duration_t duration;
+ uint8_t hours = 0;
+ uint8_t days = 0;
+
+ // count hours and days
+ while (ticks >= (128 * 60 * 60)) {
+ ticks -= (128 * 60 * 60);
+ hours++;
+ if (hours >= 24) {
+ hours -= 24;
+ days++;
+ }
+ }
+
+ // convert minutes, seconds, centiseconds
+ duration.centiseconds = (ticks & 0x7F) * 100 / 128;
+ duration.seconds = (ticks >> 7) % 60;
+ duration.minutes = (ticks >> 7) / 60;
+ duration.hours = hours;
+ duration.days = days;
+
+ return duration;
+}
+
+/** @brief starts one of the dual timers
+ * @details Starts a dual timer. If no previous timer is running it starts the global
+ * tick counter. If a previous timer is already running it registers the current tick.
+ */
+static void start_timer(dual_timer_state_t *state, bool timer) {
+ // if it is not running yet, run it
+ if ( !_is_running ) {
+ _is_running = true;
+ movement_request_tick_frequency(16);
+ state->start_ticks[timer] = 0;
+ state->stop_ticks[timer] = 0;
+ _ticks = 0;
+ _dual_timer_cb_start();
+ movement_schedule_background_task(distant_future);
+ } else {
+ // if another timer is already running save the current tick
+ state->start_ticks[timer] = _ticks;
+ state->stop_ticks[timer] = _ticks;
+ }
+ state->running[timer] = true;
+}
+
+/** @brief stops one of the dual timers
+ * @details Stops a dual timer. If no other timer is running it stops the global
+ * tick counter. If another timer is already running it registers the current stop tick.
+ */
+static void stop_timer(dual_timer_state_t *state, bool timer) {
+ // stop timer and save duration
+ state->stop_ticks[timer] = _ticks;
+ state->duration[timer] = ticks_to_duration(state->stop_ticks[timer] - state->start_ticks[timer]);
+ state->running[timer] = false;
+ // if the other timer is not running, stop callback
+ if ( state->running[!timer] == false ) {
+ _is_running = false;
+ _dual_timer_cb_stop();
+ movement_request_tick_frequency(1);
+ movement_cancel_background_task();
+ }
+}
+
+/** @brief displays the measured time for each of the dual timers
+ * @details displays the dual timer. Below 1 hour it displays the timed minutes, seconds,
+ * and centiseconds. Above that it shows the timed hours, minutes, and seconds. If it
+ * has run for more than a day it shows the days, hours, and minutes.
+ * When the timer is running, the colon blinks every half second.
+ * It also indicates at the top if another counter is running and for how long.
+ */
+static void dual_timer_display(dual_timer_state_t *state) {
+ char buf[11];
+ char oi[3];
+ // get the current time count of the selected counter
+ dual_timer_duration_t timer = state->running[state->show] ? ticks_to_duration(state->stop_ticks[state->show] - state->start_ticks[state->show]) : state->duration[state->show];
+ // get the current time count of the other counter
+ dual_timer_duration_t other = ticks_to_duration(state->stop_ticks[!state->show] - state->start_ticks[!state->show]);
+
+ if ( timer.days > 0 )
+ sprintf(buf, "%02u%02u%02u", timer.days, timer.hours, timer.minutes);
+ else if ( timer.hours > 0 )
+ sprintf(buf, "%02u%02u%02u", timer.hours, timer.minutes, timer.seconds);
+ else
+ sprintf(buf, "%02u%02u%02u", timer.minutes, timer.seconds, timer.centiseconds);
+ watch_display_string(buf, 4);
+
+ // which counter is displayed
+ watch_display_string(state->show ? "B" : "A", 0);
+
+ // indicate whether other counter is running
+ watch_display_string(state->running[!state->show] && (_ticks % 100) < 50 ? "+" : " ", 1);
+
+ // indicate for how long the other counter has been running
+ sprintf(oi, "%2u", other.days > 0 ? other.days : (other.hours > 0 ? other.hours : (other.minutes > 0 ? other.minutes : (other.seconds > 0 ? other.seconds : other.centiseconds))));
+ watch_display_string( (state->stop_ticks[!state->show] - state->start_ticks[!state->show]) > 0 ? oi : " ", 2);
+
+ // blink colon when running
+ if ( timer.centiseconds > 50 || !state->running[state->show] ) watch_set_colon();
+ else watch_clear_colon();
+}
+
+// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
+
+void dual_timer_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) settings;
+ (void) watch_face_index;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(dual_timer_state_t));
+ memset(*context_ptr, 0, sizeof(dual_timer_state_t));
+ _ticks = 0;
+ }
+ if (!_is_running) {
+ _dual_timer_cb_initialize();
+ }
+}
+
+void dual_timer_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+ if (_is_running) {
+ movement_schedule_background_task(distant_future);
+ }
+}
+
+bool dual_timer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ dual_timer_state_t *state = (dual_timer_state_t *)context;
+
+ // timers stop at 99:23:59:59:99
+ if ( (_ticks - state->start_ticks[0]) >= 1105919999 )
+ stop_timer(state, 0);
+
+ if ( (_ticks - state->start_ticks[1]) >= 1105919999 )
+ stop_timer(state, 1);
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ watch_set_colon();
+ if (_is_running) {
+ movement_request_tick_frequency(16);
+ if ( state->running[0] )
+ state->show = 0;
+ else state->show = 1;
+ } else {
+ if (state->stop_ticks[0] > 0 || state->stop_ticks[1] > 0)
+ dual_timer_display(state);
+ else watch_display_string("A 000000", 0);
+ }
+ break;
+ case EVENT_TICK:
+ if ( _is_running ) {
+ // update stop ticks
+ if ( state->running[0] )
+ state->stop_ticks[0] = _ticks;
+ if ( state->running[1] )
+ state->stop_ticks[1] = _ticks;
+ dual_timer_display(state);
+ }
+ break;
+ case EVENT_LIGHT_BUTTON_DOWN:
+ // start/stop timer B
+ state->running[1] = !state->running[1];
+ if ( state->running[1] ) {
+ start_timer(state, 1);
+ } else {
+ stop_timer(state, 1);
+ }
+ break;
+ case EVENT_ALARM_BUTTON_DOWN:
+ // start/stop timer A
+ state->running[0] = !state->running[0];
+ if ( state->running[0] ) {
+ start_timer(state, 0);
+ } else {
+ stop_timer(state, 0);
+ }
+ break;
+ case EVENT_MODE_BUTTON_DOWN:
+ // switch between the timers
+ state->show = !state->show;
+ dual_timer_display(state);
+ break;
+ case EVENT_MODE_BUTTON_UP:
+ // don't switch to next face...
+ break;
+ case EVENT_MODE_LONG_PRESS:
+ // ...but do it on long press MODE!
+ movement_move_to_next_face();
+ break;
+ case EVENT_TIMEOUT:
+ // go back to
+ if (!_is_running) movement_move_to_face(0);
+ break;
+ case EVENT_LOW_ENERGY_UPDATE:
+ dual_timer_display(state);
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+
+ return true;
+}
+
+void dual_timer_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+ movement_cancel_background_task();
+ // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/movement/watch_faces/complication/dual_timer_face.h b/movement/watch_faces/complication/dual_timer_face.h
new file mode 100644
index 00000000..d7c6cfa0
--- /dev/null
+++ b/movement/watch_faces/complication/dual_timer_face.h
@@ -0,0 +1,111 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ * Copyright (c) 2022 Andreas Nebinger
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef DUAL_TIMER_FACE_H_
+#define DUAL_TIMER_FACE_H_
+
+#include "movement.h"
+
+/*
+ * IMPORTANT: This watch face uses the same TC2 callback counter as the Stock Stopwatch
+ * watch-face. It works through calling a global handler function. The two watch-faces
+ * therefore can't coexist within the same firmware. If you want to compile this watch-face
+ * then you need to remove the line <../watch_faces/complication/stock_stopwatch_face.c \>
+ * from the Makefile.
+ */
+
+/*
+ * DUAL TIMER
+ * ==========
+ *
+ * Inspired by special ops and tactical trope targeted watches like the Nixon Regulus
+ * that feature two chronographs for timing two simultaneous events, here is a watch
+ * face that expands upon Andreas Nebinger's Stock Stopwatch Face code to implement this
+ * functionality.
+ *
+ * ALARM starts/stops timer A, resets on the next start
+ * LIGHT starts/stops timer B, resets on the next start
+ *
+ * When a timer is running, tapping MODE toggles between displaying timers A or B, as
+ * indicated at the top of the display.
+ *
+ * The currently selected timer shows minutes, seconds, and 100ths of seconds until the
+ * timed event passes the hour mark. Then it shows hours, minutes, and seconds. Once
+ * it runs for more than a day it shows days, hours, and minutes. The blinking colon
+ * indicates that the timer is running.
+ *
+ * The longest time span the timers are able to track as 99 days and 23:59:59:99 hours and
+ * they will simply stop.
+ *
+ * If the other timer that is not currently selected is also running then a plus sign is
+ * blinking next to the A or B indicator. Its progress is indicated by showing the timer's
+ * currently highest time unit ( 100ths of seconds if less than a second, seconds if less
+ * than a minute, minutes if less than an hour, hours if less than a day, days if more than
+ * a day).
+ *
+ * Please Note: If at least one timer is running then the default function of the MODE
+ * button to move to the next watch face is disabled to be able to use it to toggle between
+ * the timers. In this case LONG PRESSING MODE will move to the next face instead of moving
+ * back to the default watch face.
+ *
+ */
+
+typedef struct {
+ uint8_t centiseconds : 7; // 0-59
+ uint8_t seconds : 6; // 0-59
+ uint8_t minutes : 6; // 0-59
+ uint8_t hours : 5; // 0-23
+ uint8_t days : 7; // 0-99
+} dual_timer_duration_t;
+
+typedef struct {
+ uint32_t start_ticks[2];
+ uint32_t stop_ticks[2];
+ dual_timer_duration_t duration[2];
+ bool running[2];
+ bool show;
+} dual_timer_state_t;
+
+void dual_timer_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void dual_timer_face_activate(movement_settings_t *settings, void *context);
+bool dual_timer_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void dual_timer_face_resign(movement_settings_t *settings, void *context);
+
+#if __EMSCRIPTEN__
+void em_dual_timer_cb_handler(void *userData);
+#else
+void TC2_Handler(void);
+#endif
+
+#define dual_timer_face ((const watch_face_t){ \
+ dual_timer_face_setup, \
+ dual_timer_face_activate, \
+ dual_timer_face_loop, \
+ dual_timer_face_resign, \
+ NULL, \
+})
+
+#endif // DUAL_TIMER_FACE_H_
+
diff --git a/movement/watch_faces/complication/geomancy_face.c b/movement/watch_faces/complication/geomancy_face.c
new file mode 100644
index 00000000..c741b2d7
--- /dev/null
+++ b/movement/watch_faces/complication/geomancy_face.c
@@ -0,0 +1,363 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include "toss_up_face.h"
+#include "geomancy_face.h"
+
+// CONSTANTS //////////////////////////////////////////////////////////////////
+
+// The Bagua 八卦 Trigrams encoded as 3bit tribbles, represented as binary integer
+static const uint32_t bagua = 0b00000101001110010111011100000000;
+
+// The King Wen Sequence 文王卦序 of the I Ching 易經 Hexagrams 卦 encoded as an array
+// of decimal integers in the order of two combined Trigram tribbles from 0b000000 to
+// 0b111111
+static const uint8_t wen_order[] = {
+ 1, 22, 7, 19, 15, 34, 44, 11,
+ 14, 51, 38, 52, 61, 55, 30, 32,
+ 6, 3, 28, 58, 39, 63, 46, 5,
+ 45, 17, 47, 56, 31, 49, 27, 43,
+ 23, 26, 2, 41, 50, 20, 16, 24,
+ 35, 21, 62, 36, 54, 29, 48, 12,
+ 18, 40, 59, 60, 53, 37, 57, 9,
+ 10, 25, 4, 8, 33, 13, 42, 0
+};
+
+// The geomantic figures encoded as 4 bit nibbles, represented as hexadecimal integer
+static const uint64_t geomantic = 0x4ABF39D25E76C180;
+
+// Abbreviations of the Names of the Geomantic Figures in the order of the 4 bit nibbles
+// from 0b0000 to 0b1111
+static const char figures[16][2] = {
+ "VI" /* Via */, "Hd" /* Head of the Dragon */, "PA" /* Puella */, "GF" /* Greater Fortune*/,
+ "PR" /* Puer */, "AQ" /* Acquisitio */, "CA" /* Carcer */, "TR" /* Tristitia */,
+ "Td" /* Tail of the Dragon */, "CO" /* Conjunctio */, "AM" /* Amissio */, "AL" /* Albus */,
+ "LF" /* Lesser Fortune */, "RU" /* Rubeus */, "LA" /* Laetitia */, "PO" /* Populus */
+};
+
+// DECLARATIONS ///////////////////////////////////////////////////////////////
+
+static void geomancy_face_display();
+static nibble_t _geomancy_pick_figure();
+static tribble_t _iching_pick_trigram();
+static uint8_t _iching_form_hexagram();
+static void _geomancy_display(nibble_t code);
+static void _display_hexagram(uint8_t hexagram, char* str);
+static void _fix_broken_line(uint8_t hexagram);
+static void _throw_animation(geomancy_state_t *state);
+
+// WATCH FACE FUNCTIONS ///////////////////////////////////////////////////////
+
+void geomancy_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) watch_face_index;
+ (void) settings;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(geomancy_state_t));
+ memset(*context_ptr, 0, sizeof(geomancy_state_t));
+ }
+}
+
+void geomancy_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+}
+
+bool geomancy_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ geomancy_state_t *state = (geomancy_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ state->animate = false;
+ state->animation = 0;
+ watch_display_string(" IChing", 0);
+ break;
+ case EVENT_TICK:
+ if ( state->animate ) {
+ state->animation = (state->animation + 1) % 39;
+ geomancy_face_display(state);
+ }
+ break;
+ case EVENT_LIGHT_BUTTON_DOWN:
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ if ( state->animate ) break;
+ if ( state->mode <= 1 ) state->mode = 2;
+ else if ( state->mode >= 2 ) state->mode = 0;
+ geomancy_face_display(state);
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ if ( state->animate ) break;
+ switch ( state->mode ) {
+ case 0:
+ state->mode++;
+ // fall through
+ case 1:
+ state->animate = true;
+ state->i_ching_hexagram = _iching_form_hexagram();
+ break;
+ case 2:
+ state->mode++;
+ // fall through
+ case 3:
+ state->animate = true;
+ state->geomantic_figure = _geomancy_pick_figure().bits;
+ break;
+ default:
+ break;
+ }
+ geomancy_face_display(state);
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ if ( state->animate ) break;
+ state->caption = !state->caption;
+ watch_display_string(" ", 0);
+ geomancy_face_display(state);
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+ return true;
+}
+
+void geomancy_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+}
+
+// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
+
+/** @brief display handler */
+static void geomancy_face_display(geomancy_state_t *state);
+static void geomancy_face_display(geomancy_state_t *state) {
+ char token[7] = {0};
+ nibble_t figure = *((nibble_t*) &state->geomantic_figure);
+ switch ( state->mode ) {
+ case 0:
+ watch_display_string(" IChing", 0);
+ break;
+ case 1:
+ _throw_animation(state);
+ if ( !state->animate ) {
+ _display_hexagram(state->i_ching_hexagram, token);
+ watch_display_string(token, 4);
+ _fix_broken_line(state->i_ching_hexagram);
+ if (state->caption) {
+ sprintf(token, "%2d", wen_order[state->i_ching_hexagram] + 1);
+ watch_display_string(token, 2);
+ }
+ }
+ break;
+ case 2:
+ watch_display_string(" GeomCy", 0);
+ break;
+ case 3:
+ _throw_animation(state);
+ if ( !state->animate ) {
+ if ( state->caption ) {
+ sprintf(token, "%c%c", figures[state->geomantic_figure][0], figures[state->geomantic_figure][1]);
+ watch_display_string(token, 0);
+ }
+ _geomancy_display(figure);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/** @brief screen clearing animation between castings */
+static void _throw_animation(geomancy_state_t *state) {
+ movement_request_tick_frequency(16);
+ switch ( state->animation ) {
+ case 0:
+ watch_set_pixel(0, 22);
+ break;
+ case 1:
+ watch_set_pixel(2, 22);
+ watch_set_pixel(2, 23);
+ watch_clear_pixel(0, 22);
+ break;
+ case 2:
+ watch_set_pixel(1, 22);
+ watch_set_pixel(0, 23);
+ break;
+ case 3:
+ watch_set_pixel(2, 0);
+ watch_set_pixel(1, 0);
+ watch_set_pixel(2, 21);
+ watch_set_pixel(1, 21);
+ watch_clear_pixel(2, 22);
+ watch_clear_pixel(1, 22);
+ watch_clear_pixel(2, 23);
+ watch_clear_pixel(0, 23);
+ watch_clear_pixel(1, 23);
+ break;
+ case 4:
+ watch_set_pixel(1, 17);
+ watch_set_pixel(0, 20);
+ watch_set_pixel(2, 10);
+ watch_set_pixel(0, 1);
+ break;
+ case 5:
+ watch_clear_pixel(2, 21);
+ watch_clear_pixel(1, 21);
+ watch_clear_pixel(2, 0);
+ watch_clear_pixel(1, 0);
+ watch_clear_pixel(1, 20);
+ watch_clear_pixel(2, 20);
+ watch_clear_pixel(0, 21);
+ watch_clear_pixel(1, 1);
+ watch_clear_pixel(0, 0);
+ watch_clear_pixel(2, 1);
+ watch_set_pixel(2, 19);
+ watch_set_pixel(0, 19);
+ watch_set_pixel(1, 2);
+ watch_set_pixel(0, 2);
+ break;
+ case 6:
+ watch_clear_pixel(1, 17);
+ watch_clear_pixel(0, 20);
+ watch_clear_pixel(2, 10);
+ watch_clear_pixel(0, 1);
+ watch_set_pixel(2, 18);
+ watch_set_pixel(0, 18);
+ watch_set_pixel(2, 3);
+ watch_set_pixel(0, 4);
+ break;
+ case 7:
+ watch_clear_pixel(2, 19);
+ watch_clear_pixel(0, 19);
+ watch_clear_pixel(1, 18);
+ watch_clear_pixel(1, 19);
+ watch_clear_pixel(1, 2);
+ watch_clear_pixel(0, 2);
+ watch_clear_pixel(1, 3);
+ watch_clear_pixel(0, 3);
+ watch_clear_pixel(2, 2);
+ watch_set_pixel(1, 4);
+ watch_set_pixel(0, 5);
+ break;
+ case 8:
+ watch_clear_pixel(2, 18);
+ watch_clear_pixel(0, 18);
+ watch_clear_pixel(2, 3);
+ watch_clear_pixel(0, 4);
+ watch_set_pixel(2, 5);
+ watch_set_pixel(1, 6);
+ break;
+ case 9:
+ watch_clear_pixel(1, 4);
+ watch_clear_pixel(0, 5);
+ watch_clear_pixel(1, 5);
+ watch_clear_pixel(2, 4);
+ watch_clear_pixel(0, 6);
+ break;
+ case 10:
+ watch_clear_pixel(2, 5);
+ watch_clear_pixel(1, 6);
+ break;
+ case 11:
+ state->animate = false;
+ state->animation = 0;
+ movement_request_tick_frequency(1);
+ break;
+ }
+}
+
+// I CHING FUNCTIONS //////////////////////////////////////////////////////////
+
+/** @brief form a trigram from three random bit picks
+ */
+static tribble_t _iching_pick_trigram(void) {
+ uint8_t index = (divine_bit() << 2) | (divine_bit() << 1) | divine_bit();
+ tribble_t trigram = {(bagua >> (3 * index)) & 0b111};
+ return trigram;
+}
+
+/** @brief form a hexagram from two trigrams
+ */
+static uint8_t _iching_form_hexagram(void);
+static uint8_t _iching_form_hexagram(void) {
+ tribble_t inner = _iching_pick_trigram();
+ tribble_t outer = _iching_pick_trigram();
+ uint8_t hexagram = (inner.bits << 3) | outer.bits;
+ return hexagram;
+}
+
+/** @brief display hexagram
+ * @details | for unbroken lines and Ξ for broken lines, left of display is bottom
+ */
+static void _display_hexagram(uint8_t hexagram, char* str);
+static void _display_hexagram(uint8_t hexagram, char* str) {
+ str[6] = '\0'; // Null-terminate the string
+ for (uint8_t i = 0; i < 6; i++) {
+ if (hexagram & (1 << (5 - i))) {
+ str[i] = '1';
+ } else {
+ str[i] = '=';
+ }
+ }
+}
+
+/** @brief when Ξ digits show as = then manually add a line on top
+ */
+static void _fix_broken_line(uint8_t hexagram) {
+ for (uint8_t i = 0; i < 6; i++) {
+ if (!(hexagram & (1 << (5 - i)))) {
+ if ( i == 1 ) watch_set_pixel(2, 20);
+ if ( i == 3 ) watch_set_pixel(2, 1);
+ if ( i == 4 ) watch_set_pixel(2, 2);
+ if ( i == 5 ) watch_set_pixel(2, 4);
+ }
+ }
+}
+
+// GEOMANCY FUNCTIONS /////////////////////////////////////////////////////////
+
+/** @brief choose a geomantic figure from four random bits
+ * @details 0 represents · and 1 represents : counting from the bottom
+ */
+static nibble_t _geomancy_pick_figure(void);
+static nibble_t _geomancy_pick_figure(void) {
+ uint8_t index = (divine_bit() << 3) | (divine_bit() << 2) | (divine_bit() << 1) | divine_bit();
+ nibble_t figure = {(geomantic >> (4 * (15 - index))) & 0xF};
+ return figure;
+}
+
+/** @brief display the geomantic figure, left of display is bottom
+ */
+static void _geomancy_display(nibble_t code) {
+ // draw geomantic figures
+ bool row1 = (code.bits >> 3) & 1;
+ bool row2 = (code.bits >> 2) & 1;
+ bool row3 = (code.bits >> 1) & 1;
+ bool row4 = code.bits & 1;
+
+ if ( row1 ) watch_set_pixel(1, 18); else watch_set_pixel(1, 19);
+ if ( row2 ) { watch_set_pixel(2, 20); watch_set_pixel(0, 21);} else watch_set_pixel(1, 20);
+ if ( row3 ) watch_set_pixel(0, 22); else watch_set_pixel(1, 23);
+ if ( row4 ) { watch_set_pixel(2, 1); watch_set_pixel(0, 0);} else watch_set_pixel(1, 1);
+}
\ No newline at end of file
diff --git a/movement/watch_faces/complication/geomancy_face.h b/movement/watch_faces/complication/geomancy_face.h
new file mode 100644
index 00000000..4a19ba85
--- /dev/null
+++ b/movement/watch_faces/complication/geomancy_face.h
@@ -0,0 +1,99 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef GEOMANCY_FACE_H_
+#define GEOMANCY_FACE_H_
+
+#include "movement.h"
+
+/*
+ * GEOMANCY WATCH FACE
+ *
+ * A simple and straightforward watch face for the ancient Eastern geomantic divination system
+ * of I Ching and the western system of "Geomancy". It is an optional addition to the Toss Up
+ * Face.
+ *
+ * The LIGHT button toggles between the two systems of geomancy.
+ *
+ * The ALARM button casts an I Ching hexagram or Geomantic figure based on drawing virtual
+ * stalks from the True Random Number Generator in the Sensor Watch.
+ *
+ * The figures are flipped 90 degrees clockwise, so the left side is the bottom and the
+ * right side the top.
+ *
+ * LONG PRESSING ALARM toggles the display of the King Wen sequence index for the cast I Ching
+ * Hexagram (https://en.wikipedia.org/wiki/King_Wen_sequence )or the abbreviated name for the
+ * cast Geomantic Figure:
+ *
+ * GF - Greater Fortune (Fortuna Major)
+ * LF - Lesser Fortune (Fortuna Minor)
+ * PO - Populus
+ * VI - Via
+ * AL - Albus
+ * CO - Conjunctio
+ * PA - Puella
+ * AM - Amissio
+ * PR - Puer
+ * RU - Rubeus
+ * AQ - Acquisitio
+ * LA - Laetitia
+ * TR - Tristitia
+ * CA - Carcer
+ * HD - Head of the Dragon (Caput Draconis)
+ * TD - Tail of the Dragon (Cauda Draconis)
+ *
+ */
+
+typedef struct {
+ uint8_t bits : 4;
+} nibble_t;
+
+typedef struct {
+ uint8_t bits : 3;
+} tribble_t;
+
+typedef struct {
+ uint8_t mode : 3;
+ uint8_t geomantic_figure;
+ uint8_t i_ching_hexagram : 6;
+ bool caption;
+ uint8_t animation;
+ bool animate;
+} geomancy_state_t;
+
+void geomancy_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void geomancy_face_activate(movement_settings_t *settings, void *context);
+bool geomancy_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void geomancy_face_resign(movement_settings_t *settings, void *context);
+
+#define geomancy_face ((const watch_face_t){ \
+ geomancy_face_setup, \
+ geomancy_face_activate, \
+ geomancy_face_loop, \
+ geomancy_face_resign, \
+ NULL, \
+})
+
+#endif // GEOMANCY_FACE_H_
+
diff --git a/movement/watch_faces/complication/interval_face.c b/movement/watch_faces/complication/interval_face.c
index e8ebe321..dabc6b1e 100644
--- a/movement/watch_faces/complication/interval_face.c
+++ b/movement/watch_faces/complication/interval_face.c
@@ -129,15 +129,15 @@ static const int8_t _sound_seq_break[] = {BUZZER_NOTE_B6, 15, BUZZER_NOTE_REST,
static const int8_t _sound_seq_cooldown[] = {BUZZER_NOTE_C7, 15, BUZZER_NOTE_REST, 1, -2, 1, BUZZER_NOTE_C7, 24, 0};
static const int8_t _sound_seq_finish[] = {BUZZER_NOTE_C7, 6, BUZZER_NOTE_E7, 6, BUZZER_NOTE_G7, 6, BUZZER_NOTE_C8, 18, 0};
-interval_setting_idx_t _setting_idx;
-int8_t _ticks;
-bool _erase_timer_flag;
-uint32_t _target_ts;
-uint32_t _now_ts;
-uint32_t _paused_ts;
-uint8_t _timer_work_round;
-uint8_t _timer_full_round;
-uint8_t _timer_run_state;
+static interval_setting_idx_t _setting_idx;
+static int8_t _ticks;
+static bool _erase_timer_flag;
+static uint32_t _target_ts;
+static uint32_t _now_ts;
+static uint32_t _paused_ts;
+static uint8_t _timer_work_round;
+static uint8_t _timer_full_round;
+static uint8_t _timer_run_state;
static inline void _inc_uint8(uint8_t *value, uint8_t step, uint8_t max) {
*value += step;
diff --git a/movement/watch_faces/complication/invaders_face.c b/movement/watch_faces/complication/invaders_face.c
new file mode 100644
index 00000000..c3b13c68
--- /dev/null
+++ b/movement/watch_faces/complication/invaders_face.c
@@ -0,0 +1,434 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Andreas Nebinger
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// Emulator only: need time() to seed the random number generator
+#if __EMSCRIPTEN__
+#include
+#endif
+
+#include
+#include
+#include "watch_private_display.h"
+#include "invaders_face.h"
+
+#define INVADERS_FACE_WAVES_PER_STAGE 9 // number of waves per stage (there are two stages)
+#define INVADERS_FACE_WAVE_INVADERS 16 // number of invaders attacking per wave
+
+static const uint8_t _defense_lines_segdata[3][2] = {{2, 12}, {2, 11}, {0, 11}};
+static const uint8_t _bonus_points_segdata[4][2] = {{2, 7}, {2, 8}, {2, 9}, {0, 10}};
+static const uint8_t _bonus_points_helper[] = {1, 5, 9, 11, 15, 19, 21, 25, 29};
+
+static const int8_t _sound_seq_game_start[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 1, BUZZER_NOTE_REST, 10, BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 1, 0};
+static const int8_t _sound_seq_shot_hit[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 2, 0};
+static const int8_t _sound_seq_shot_miss[] = {BUZZER_NOTE_A7, 1, 0};
+static const int8_t _sound_seq_ufo_hit[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 2, -2, 1, 0};
+static const int8_t _sound_seq_def_gone[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 3, BUZZER_NOTE_REST, 40, BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 4, 0};
+static const int8_t _sound_seq_next_wave[] = {BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, BUZZER_NOTE_REST, 8, BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, -2, 1,
+ BUZZER_NOTE_REST, 32,
+ BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, BUZZER_NOTE_REST, 8, BUZZER_NOTE_A6, 2, BUZZER_NOTE_A7, 2, -2, 1, 0};
+static const int8_t _sound_seq_game_over[] = {BUZZER_NOTE_A6, 1, BUZZER_NOTE_A7, 3, -2, 11, 0};
+
+typedef enum {
+ invaders_state_activated,
+ invaders_state_pre_game,
+ invaders_state_playing,
+ invaders_state_in_wave_break,
+ invaders_state_pre_next_wave,
+ invaders_state_next_wave,
+ invaders_state_game_over
+} invaders_current_state_t;
+
+typedef struct {
+ bool ufo_next : 1; // indicates whether next invader is a ufo
+ bool inv_checking : 1; // flag to indicate whether we are currently moving invaders (to prevent race conditions)
+ bool suspend_buttons : 1; // used while playing the game over sequence to prevent involuntary immediate restarts
+} invaders_signals_t;
+
+static int8_t _invaders[6]; // array of current invaders values (-1 = empty, 10 = ufo)
+static uint8_t _wave_invaders[INVADERS_FACE_WAVE_INVADERS]; // all invaders for the current wave. (Predefined to save cpu cycles when playing.)
+static invaders_current_state_t _current_state;
+static uint8_t _defense_lines; // number of defense lines which have been broken in the current wave
+static uint8_t _aim; // current "aim" digit
+static uint8_t _invader_idx; // index of next invader attacking in current wave (0 to 15)
+static uint8_t _wave_position; // current position of first invader. When > 6 the defense is broken
+static uint8_t _wave_tick_freq; // number of ticks passing until the next invader is inserted
+static uint8_t _ticks; // counts the ticks
+static uint8_t _bonus_countdown; // ticks countdown until the bonus point indicator is cleared
+static uint8_t _waves; // counts the waves (_wave_tick_freq decreases slowly depending on _wave value)
+static uint8_t _shots_in_wave; // number of shots in current wave. If 30 is reached, the game is over
+static uint8_t _invaders_shot; // number of sucessfully shot invaders in current wave
+static uint8_t _invaders_shot_sum; // current sum of invader digits shot (needed to determine if a ufo is coming)
+static invaders_signals_t _signals; // holds severals flags
+static uint16_t _score; // score of the current game
+
+/// @brief return a random number. 0 <= return_value < num_values
+static inline uint8_t _get_rand_num(uint8_t num_values) {
+#if __EMSCRIPTEN__
+ return rand() % num_values;
+#else
+ return arc4random_uniform(num_values);
+#endif
+}
+
+/// @brief callback function to re-enable light and alarm buttons after playing a sound sequence
+static inline void _resume_buttons() {
+ _signals.suspend_buttons = false;
+}
+
+/// @brief play a sound sequence if the game is in beepy mode
+static inline void _play_sequence(invaders_state_t *state, int8_t *sequence) {
+ if (state->sound_on) watch_buzzer_play_sequence((int8_t *)sequence, NULL);
+}
+
+/// @brief draw the remaining defense lines
+static void _display_defense_lines() {
+ watch_display_character(' ', 1);
+ for (uint8_t i = 0; i < 3 - _defense_lines; i++) watch_set_pixel(_defense_lines_segdata[i][0], _defense_lines_segdata[i][1]);
+}
+
+/** @brief draw label followed by the given score value
+ * @param label string displayed in the upper left corner
+ * @param score score to display
+ */
+static void _display_score(char *label, uint16_t score) {
+ watch_display_character(label[0], 0);
+ watch_display_character(label[1], 1);
+ char buf[10];
+ sprintf(buf, " %06d", (score * 10));
+ watch_display_string(buf, 2);
+}
+
+/// @brief draw an invader at the given position
+static inline void _display_invader(int8_t invader, uint8_t position) {
+ switch (invader) {
+ case 10:
+ watch_display_character('n', position);
+ break;
+ case -1:
+ watch_display_character(' ', position);
+ break;
+ default:
+ watch_display_character(invader + 48, position);
+ break;
+ }
+}
+
+/// @brief game over: show score and set state
+static void _game_over(invaders_state_t *state) {
+ _display_score("GO", _score);
+ _current_state = invaders_state_game_over;
+ movement_request_tick_frequency(1);
+ _signals.suspend_buttons = true;
+ if (state->sound_on) watch_buzzer_play_sequence((int8_t *)_sound_seq_game_over, _resume_buttons);
+ // save current score to highscore, if applicable
+ if (_score > state->highscore) state->highscore = _score;
+}
+
+/// @brief initialize the current wave
+static void _init_wave() {
+ uint8_t i;
+ if (_current_state == invaders_state_in_wave_break) {
+ _invader_idx = _invaders_shot;
+ } else {
+ _invader_idx = _invaders_shot = _invaders_shot_sum = _defense_lines = _shots_in_wave = 0;
+ }
+ // pre-fill invaders
+ for (i = _invader_idx; i < INVADERS_FACE_WAVE_INVADERS; i++) _wave_invaders[i] = _get_rand_num(10);
+ // init invaders field
+ for (i = 1; i < 6; i++) _invaders[i] = -1;
+ _invaders[0] = _wave_invaders[_invader_idx];
+ _wave_position = _aim = _bonus_countdown = 0;
+ _signals.ufo_next = _signals.inv_checking = _signals.suspend_buttons = false;
+ _current_state = invaders_state_playing;
+ // determine wave speed
+ _wave_tick_freq = 6 - ((_waves % INVADERS_FACE_WAVES_PER_STAGE) + 1) / 2;
+ if (_waves >= INVADERS_FACE_WAVES_PER_STAGE) _wave_tick_freq--;
+ // clear display
+ watch_display_string(" ", 2);
+ watch_display_character('0', 0);
+ _display_defense_lines();
+ // draw first invader
+ watch_display_character(_wave_invaders[_invader_idx] + 48, 9);
+}
+
+/** @brief move invaders and add a new one, if necessary
+ * @returns true, if invaders have reached position 6, false otherwise
+ */
+static bool _move_invaders() {
+ if (_wave_position == 5) return true;
+ _signals.inv_checking = true;
+ if (_invaders[_wave_position] >= 0) _wave_position++;
+ int8_t i;
+ // move invaders
+ for (i = _wave_position; i > 0; i--) _invaders[i] = _invaders[i - 1];
+ if (_invader_idx < INVADERS_FACE_WAVE_INVADERS - 1) {
+ // add invader
+ _invader_idx++;
+ if (_signals.ufo_next) {
+ _invaders[0] = 10;
+ _signals.ufo_next = false;
+ } else {
+ _invaders[0] = _wave_invaders[_invader_idx];
+ }
+ } else {
+ // just add an empty invader slot
+ _invaders[0] = -1;
+ }
+ // update display
+ for (i = 0; i <= _wave_position; i++) {
+ _display_invader(_invaders[i], 9 - i);
+ }
+ _signals.inv_checking = false;
+ return false;
+}
+
+void invaders_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) settings;
+ (void) watch_face_index;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(invaders_state_t));
+ memset(*context_ptr, 0, sizeof(invaders_state_t));
+ invaders_state_t *state = (invaders_state_t *)*context_ptr;
+ // default: sound on
+ state->sound_on = true;
+ }
+#if __EMSCRIPTEN__
+ // simulator only: seed the randon number generator
+ time_t t;
+ srand((unsigned) time(&t));
+#endif
+}
+
+void invaders_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+ _current_state = invaders_state_activated;
+ _signals.suspend_buttons = false;
+}
+
+bool invaders_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ invaders_state_t *state = (invaders_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ // show highscore
+ _display_score("GA", state->highscore);
+ break;
+ case EVENT_TICK:
+ _ticks++;
+ switch (_current_state) {
+ case invaders_state_in_wave_break:
+ case invaders_state_pre_game:
+ case invaders_state_next_wave:
+ // wait 2 secs to start the first round
+ if (_ticks >= 2) {
+ _ticks = 0;
+ _init_wave();
+ _current_state = invaders_state_playing;
+ movement_request_tick_frequency(4);
+ }
+ break;
+ case invaders_state_playing:
+ // game is playing
+ if (_ticks >= _wave_tick_freq) {
+ _ticks = 0;
+ if (_move_invaders()) {
+ // invaders broke through
+ if (_defense_lines < 2) {
+ // start current wave over
+ _defense_lines++;
+ _display_defense_lines();
+ _display_score("GA", _score);
+ _current_state = invaders_state_in_wave_break;
+ movement_request_tick_frequency(1);
+ _play_sequence(state, (int8_t *)_sound_seq_def_gone);
+ } else {
+ // game over
+ _game_over(state);
+ }
+ }
+ }
+ // handle bonus points indicators
+ if (_bonus_countdown) {
+ _bonus_countdown--;
+ if (!_bonus_countdown) {
+ watch_display_character(' ', 2);
+ watch_display_character(' ', 3);
+ }
+ }
+ break;
+ case invaders_state_pre_next_wave:
+ if (_ticks >= 3) {
+ // switch to next wave
+ _ticks = 0;
+ movement_request_tick_frequency(1);
+ _display_score("GA", _score);
+ watch_set_pixel(1, 9);
+ watch_display_character((_waves % INVADERS_FACE_WAVES_PER_STAGE) + 49, 3);
+ _current_state = invaders_state_next_wave;
+ _waves++;
+ if (_waves == INVADERS_FACE_WAVES_PER_STAGE * 2) _waves = 0;
+ _play_sequence(state, (int8_t *)_sound_seq_next_wave);
+ }
+ default:
+ break;
+ }
+ break;
+ case EVENT_LIGHT_BUTTON_DOWN:
+ if (!_signals.suspend_buttons) {
+ if (_current_state == invaders_state_playing) {
+ // cycle the aim
+ _aim = (_aim + 1) % 11;
+ _display_invader(_aim, 0);
+ } else if (_current_state == invaders_state_activated || _current_state == invaders_state_game_over) {
+ // just illuminate the LED
+ movement_illuminate_led();
+ }
+ }
+ break;
+ case EVENT_LIGHT_LONG_PRESS:
+ if ((_current_state == invaders_state_activated || _current_state == invaders_state_game_over) && !_signals.suspend_buttons) {
+ // switch between beepy and silent mode
+ state->sound_on = !state->sound_on;
+ watch_buzzer_play_note(BUZZER_NOTE_A7, state->sound_on ? 65 : 25);
+ }
+ break;
+ case EVENT_ALARM_BUTTON_DOWN:
+ if (!_signals.suspend_buttons) {
+ switch (_current_state) {
+ case invaders_state_game_over:
+ case invaders_state_activated:
+ // initialize the game
+ _waves = 0;
+ _score = 0;
+ movement_request_tick_frequency(1);
+ _ticks = 0;
+ _current_state = invaders_state_pre_game;
+ _play_sequence(state, (int8_t *)_sound_seq_game_start);
+ break;
+ case invaders_state_playing: {
+ // "shoot"
+ _shots_in_wave++;
+ if (_shots_in_wave == 30) {
+ // max number of shots reached: game over
+ _game_over(state);
+ } else {
+ // wait if we are currently deleting an invader
+ while (_signals.inv_checking);
+ // proceed
+ _signals.inv_checking = true;
+ bool skip = false;
+ for (int8_t i = _wave_position; i >= 0 && !skip; i--) {
+ // if (_invaders[i] == -1) break;
+ if (_invaders[i] == _aim) {
+ // invader is shot
+ skip = true;
+ _invaders_shot++;
+ _play_sequence(state, _aim == 10 ? (int8_t *)_sound_seq_ufo_hit : (int8_t *)_sound_seq_shot_hit);
+ if (_invaders_shot == INVADERS_FACE_WAVE_INVADERS) {
+ // last invader shot: wave sucessfully completed
+ watch_display_character(' ', 9 - _wave_position);
+ _ticks = 0;
+ _current_state = invaders_state_pre_next_wave;
+ _signals.inv_checking = false;
+ } else {
+ // check for ufo appearance
+ if (_aim && _aim < 10) {
+ _invaders_shot_sum = (_invaders_shot_sum + _aim) % 10;
+ if (_invaders_shot_sum == 0) _signals.ufo_next = true;
+ }
+ // remove invader
+ if (_wave_position == 0 || i == 5) {
+ _invaders[i] = -1;
+ } else {
+ for (uint8_t j = i; j < _wave_position; j++) {
+ _invaders[j] = _invaders[j + 1];
+ _display_invader(_invaders[j], 9 - j);
+ }
+ }
+ watch_display_character(' ', 9 - _wave_position);
+ if (_wave_position) _wave_position--;
+ // update score
+ if (_aim == 10) {
+ // ufo shot. The original game uses a ridiculously complicated scoring system here...
+ uint8_t bonus_points = 0;
+ uint8_t j;
+ for (j = 0; j < sizeof(_bonus_points_helper) && !bonus_points; j++) {
+ if (_shots_in_wave == _bonus_points_helper[j]) {
+ bonus_points = 30;
+ } else if (_shots_in_wave - 1 == _bonus_points_helper[j]) {
+ bonus_points = 20;
+ }
+ }
+ if (!bonus_points) bonus_points = 10;
+ bonus_points += (6 - i);
+ if ((_waves >= INVADERS_FACE_WAVES_PER_STAGE) && i) bonus_points += (6 - i);
+ _score += bonus_points;
+ // represent bonus points by bars
+ for (j = 0; j < (bonus_points / 10); j++) watch_set_pixel(_bonus_points_segdata[j][0], _bonus_points_segdata[j][1]);
+ _bonus_countdown = 9;
+ } else {
+ // regular invader
+ _score += (6 - _wave_position) * (_waves >= INVADERS_FACE_WAVES_PER_STAGE ? 2 : 1);
+ }
+ }
+ }
+ }
+ if (!skip) _play_sequence(state, (int8_t *)_sound_seq_shot_miss);
+ _signals.inv_checking = false;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ case EVENT_TIMEOUT:
+ movement_move_to_face(0);
+ break;
+ default:
+ // Movement's default loop handler will step in for any cases you don't handle above:
+ // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+ // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+ // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
+ // You can override any of these behaviors by adding a case for these events to this switch statement.
+ return movement_default_loop_handler(event, settings);
+ }
+
+ // return true if the watch can enter standby mode. Generally speaking, you should always return true.
+ // Exceptions:
+ // * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
+ // * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
+ // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
+ // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
+ return true;
+}
+
+void invaders_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+ _current_state = invaders_state_game_over;
+}
+
diff --git a/movement/watch_faces/complication/invaders_face.h b/movement/watch_faces/complication/invaders_face.h
new file mode 100644
index 00000000..59126dd5
--- /dev/null
+++ b/movement/watch_faces/complication/invaders_face.h
@@ -0,0 +1,82 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Andreas Nebinger
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef INVADERS_FACE_H_
+#define INVADERS_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Remake of the "famous" Casio Number Invaders Game
+ *
+ * This is an authentic remake of the invaders game, found on the Casio
+ * calculator wristwatch CA-85 or CA-851. There were also some calculators
+ * sold with this game, like MG-880.
+ *
+ * How to play:
+ *
+ * Press the alarm button to start the game.
+ * "Invaders" (just digits, tbh) will start coming in from the right hand side.
+ * Press the light button to "aim". The digit on the top of the display cycles
+ * from 0 to 9. If your aiming digit is identical to one of the invaders,
+ * press the alarm button to "shoot". The corresponding invader will disappear.
+ * If the invaders reach beneath the very first position, you loose one defense
+ * line. When all three defense lines are gone, the game is over.
+ * Also: If you shoot more than 29 times per round, you loose the game.
+ * Good to know: There are 16 invaders per wave. There is a short break between
+ * waves.
+ *
+ * What are the "n" invaders? Ufos!
+ *
+ * Whenever the sum of all invaders shot is divisible by 10 the next invader
+ * will be an ufo, represented by the n-symbol. Shooting a ufo gets you extra
+ * points. Example: shoot 2, 5, 3 --> ufo next
+ *
+ * As for points: the earlier you shoot an invader, the more points you get.
+ *
+ * Anything else? Long pressing the light button toggles sound on or off. (Not
+ * while playing.)
+ *
+ */
+
+typedef struct {
+ uint16_t highscore;
+ bool sound_on;
+} invaders_state_t;
+
+void invaders_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void invaders_face_activate(movement_settings_t *settings, void *context);
+bool invaders_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void invaders_face_resign(movement_settings_t *settings, void *context);
+
+#define invaders_face ((const watch_face_t){ \
+ invaders_face_setup, \
+ invaders_face_activate, \
+ invaders_face_loop, \
+ invaders_face_resign, \
+ NULL, \
+})
+
+#endif // INVADERS_FACE_H_
+
diff --git a/movement/watch_faces/complication/morsecalc_face.c b/movement/watch_faces/complication/morsecalc_face.c
index cdf0e198..ca03a1a9 100644
--- a/movement/watch_faces/complication/morsecalc_face.c
+++ b/movement/watch_faces/complication/morsecalc_face.c
@@ -26,7 +26,6 @@
## Morse-code-based RPN calculator
The calculator is operated by first composing a **token** in Morse code, then submitting it to the calculator. A token specifies either a calculator operation or a float value.
-
These two parts of the codebase are totally independent:
1. The Morse-code reader (`mc.h`, `mc.c`)
@@ -39,7 +38,7 @@ The user interface (`morsecalc_face.h`, `morsecalc_face.c`) lets you talk to the
- `light` is dash
- `alarm` is dot
- `mode` is "finish character"
- - long-press `mode` to quit
+ - long-press `mode` or submit a blank token to switch faces
- long-press `alarm` to show stack
- long-press `light` to toggle the light
@@ -48,7 +47,6 @@ As you enter `.`s and `-`s, the morse code char you've entered will appear in th
At the top right is the # of morse code `.`/`-` you've input so far. The character resets at the 6th `.`/`-`.
Once you have the character you want to enter, push `mode` to enter it.
The character will be appended to the current token, whose 6 trailing chars are shown on the main display.
-
Once you've typed in the token you want, enter a blank Morse code character and then push `mode`.
This submits it to the calculator.
@@ -77,7 +75,6 @@ This can get long, so for convenience numerals can also be written in binary wit
For example: "4.2e-3" can be entered directly, or as "4h2pC3"
similarly, "0.0042" can also be entered as "eheedn"
-
Once you submit a number to the watch face, it pushes it to the top of the stack if there's room.
## Number display
@@ -97,7 +94,6 @@ So for example, the watch face might look like this:
... representing `+4.200e-3` is in stack location 0 (the top) and it's one of five items in the stack.
## Looking at the stack
-
To show the top of the stack, push and hold `light`/`alarm` or submit a blank token by pushing `mode` a bunch of times.
To show the N-th stack item (0 through 9):
@@ -113,101 +109,25 @@ To see all the calculator operations and their token aliases, see the `calc_dict
#include
#include
-#include "morsecalc_face.h"
#include "watch.h"
#include "watch_utility.h"
#include "watch_private_display.h"
-// Display float on screen
-void morsecalc_print_float(double d) {
- // Special cases
- if(d == 0) {
- watch_display_string(" 0", 4);
- return;
- }
- else if(isnan(d)) {
- watch_display_string(" nan", 4);
- return;
- }
- else if(d == (1.0)/(0.0)) {
- watch_display_string(" inf", 4);
- return;
- }
- else if(d == (-1.0)/(0.0)) {
- watch_display_character('X', 1);
- watch_display_string(" inf", 4);
- return;
- }
+#include "morsecalc_face.h"
+#include "morsecalc_display.h"
- // Record number properties
- // Sign
- int is_negative = d<0;
- if(is_negative) d = -d;
-
- // Order of magnitude
- int om = (int) floor(log(d)/log(10));
- int om_is_negative = (om<0);
-
- // Get the first 4 significant figures
- int digits;
- digits = round(d*pow(10.0, 3-om));
- if(digits>9999) {
- digits = 1000;
- om++;
- }
-
- // Print signs
- if(is_negative) {
- // Xi; see https://joeycastillo.github.io/Sensor-Watch-Documentation/segmap
- watch_set_pixel(0,11);
- watch_set_pixel(2,12);
- watch_set_pixel(2,11);
- }
- else watch_display_character(' ', 1);
- if(om_is_negative) watch_set_pixel(1,9);
- else watch_display_character(' ', 2);
-
- // Print first 4 significant figures
- watch_display_character('0'+(digits/1000)%10, 4);
- watch_display_character('0'+(digits/100 )%10, 5);
- watch_display_character('0'+(digits/10 )%10, 6);
- watch_display_character('0'+(digits/1 )%10, 7);
-
- // Prinat exponent
- if(om_is_negative) om = -om; // Make exponent positive for display
- if(om<=99) {
- watch_display_character('0'+(om/10 )%10, 8);
- watch_display_character('0'+(om/1 )%10, 9);
- } else { // Over/underflow
- if(om_is_negative) watch_display_string(" uf", 4);
- else watch_display_string(" of", 4);
- if(om<9999) { // Use main display to show order of magnitude
- // (Should always succeed; max double is <2e308)
- watch_display_character('0'+(om/1000)%10, 4);
- watch_display_character('0'+(om/100 )%10, 5);
- watch_display_character('0'+(om/10 )%10, 6);
- watch_display_character('0'+(om/1 )%10, 7);
- }
- }
+/* mc_input Read an input into a morse code buffer
+ * Input: mc = index of MORSECODE_TREE[]
+ * len = max morse code char length
+ * in = character to read into buffer (0='.', 1='-', ignored otherwise).
+ * If the buffer is full, reset it instead of entering the new character.
+ */
+static void morsecode_input(unsigned int *mc, unsigned int len, char in) {
+ if(*mc >= (unsigned int) ((1<mc->b); // Decode the morse code buffer's current contents
- if('\0' == c) c = ' '; // Needed for watch_display_character
- watch_display_character(c, 0); // Display current morse code char in mode position
- watch_display_character('0'+(mcs->mc->bidx), 3); // Display buffer position in top right
-
- // Print last 6 chars of current input line
- uint8_t nlen = strlen(mcs->token); // number of characters in token
- uint8_t nprint = min(nlen,6); // number of characters to print
- watch_display_string(mcs->token+nlen-nprint, 10-nprint); // print right-aligned
- return;
-}
// Clear token buffer
void morsecalc_reset_token(morsecalc_state_t *mcs) {
@@ -216,75 +136,46 @@ void morsecalc_reset_token(morsecalc_state_t *mcs) {
return;
}
-// Print stack or memory register contents.
-void morsecalc_print_stack(morsecalc_state_t * mcs) {
- watch_display_string(" ", 0); // Clear display
-
- char c = mc_dec(mcs->mc->b);
- if('m' == c) { // Display memory
- morsecalc_print_float(mcs->cs->mem);
- watch_display_character(c, 0);
- }
- else {
- // If the morse code buffer has a numeral in it, print that stack item
- // Otherwise print top of stack
- uint8_t idx = 0;
- if(c >= '0' && c <= '9') idx = c - '0';
- if(idx >= mcs->cs->s) watch_display_string(" empty", 4); // Stack empty
- else morsecalc_print_float(mcs->cs->stack[mcs->cs->s-1-idx]); // Print stack item
-
- watch_display_character('0'+idx, 0); // Print which stack item this is top center
- }
- watch_display_character('0'+(mcs->cs->s), 3); // Print the # of stack items top right
- return;
-}
-
-// Write something into the morse code buffer.
-// Input: c = dot (0), dash (1), or 'complete' ('x')
-void morsecalc_input(morsecalc_state_t * mcs, char c) {
+// Write a completed morse code character to the calculator
+void morsecalc_input(morsecalc_state_t * mcs) {
int status = 0;
- if( c != 'x' ) { // Dot or dash received
- mc_input(mcs->mc, c);
- morsecalc_print_token(mcs);
- }
- else { // Morse code character finished
- char dec = mc_dec(mcs->mc->b);
- mc_reset(mcs->mc);
- switch(dec) {
- case '\0': // Invalid character, do nothing
- morsecalc_print_token(mcs);
- break;
-
- case ' ': // Submit token to calculator
- if(strlen(mcs->token) > 0) {
- status = calc_input(mcs->cs, mcs->token);
- morsecalc_reset_token(mcs);
- }
- morsecalc_print_stack(mcs);
- break;
-
- case '(': // -.--. Erase previous character in token
- if(mcs->idxt>0) {
- mcs->idxt--;
- mcs->token[mcs->idxt] = '\0';
- }
- morsecalc_print_token(mcs);
- break;
-
- case 'S': // -.-.- Erase entire token without submitting
+ char dec = MORSECODE_TREE[mcs->mc];
+ mcs->mc = 0;
+ switch(dec) {
+ case '\0': // Invalid character, do nothing
+ morsecalc_display_token(mcs);
+ break;
+
+ case ' ': // Submit token to calculator
+ if(mcs->idxt > 0) {
+ mcs->token[mcs->idxt] = '\0';
+ status = calc_input(mcs->cs, mcs->token);
morsecalc_reset_token(mcs);
- morsecalc_print_stack(mcs);
- break;
-
- default: // Add character to token
- if(mcs->idxt < MORSECALC_TOKEN_LEN-1) {
- mcs->token[mcs->idxt] = dec;
- mcs->idxt++;
- morsecalc_print_token(mcs);
- }
- else watch_display_string(" full", 4);
- break;
- }
+ }
+ morsecalc_display_stack(mcs);
+ break;
+
+ case '(': // -.--. Erase previous character in token
+ if(mcs->idxt>0) {
+ mcs->idxt--;
+ mcs->token[mcs->idxt] = '\0';
+ }
+ morsecalc_display_token(mcs);
+ break;
+
+ case 'S': // -.-.- Erase entire token without submitting
+ morsecalc_reset_token(mcs);
+ morsecalc_display_stack(mcs);
+ break;
+
+ default: // Add character to token
+ if(mcs->idxt < MORSECALC_TOKEN_LEN-1) {
+ mcs->token[mcs->idxt] = dec;
+ mcs->idxt = min(mcs->idxt+1, MORSECALC_TOKEN_LEN);
+ morsecalc_display_token(mcs);
+ }
+ else watch_display_string(" full", 4);
+ break;
}
// Print errors if there are any
@@ -308,10 +199,7 @@ void morsecalc_face_setup(movement_settings_t *settings, uint8_t watch_face_inde
mcs->cs = (calc_state_t *) malloc(sizeof(calc_state_t));
calc_init(mcs->cs);
-
- mcs->mc = (mc_state_t *) malloc(sizeof(mc_state_t));
- mc_reset(mcs->mc);
-
+ mcs->mc = 0;
mcs->led_is_on = 0;
}
return;
@@ -320,8 +208,8 @@ void morsecalc_face_setup(movement_settings_t *settings, uint8_t watch_face_inde
void morsecalc_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
morsecalc_state_t *mcs = (morsecalc_state_t *) context;
- mc_reset(mcs->mc);
- morsecalc_print_stack(mcs);
+ mcs->mc = 0;
+ morsecalc_display_stack(mcs);
return;
}
@@ -331,21 +219,24 @@ bool morsecalc_face_loop(movement_event_t event, movement_settings_t *settings,
// input
case EVENT_ALARM_BUTTON_UP:
// dot
- morsecalc_input(mcs, '.');
+ morsecode_input(&mcs->mc, MORSECODE_LEN, 0);
+ morsecalc_display_token(mcs);
break;
case EVENT_LIGHT_BUTTON_UP:
// dash
- morsecalc_input(mcs, '-');
+ morsecode_input(&mcs->mc, MORSECODE_LEN, 1);
+ morsecalc_display_token(mcs);
break;
case EVENT_MODE_BUTTON_UP:
- // submit character
- morsecalc_input(mcs, 'x');
+ // submit character (or quit)
+ if(mcs->mc || mcs->idxt) morsecalc_input(mcs);
+ else movement_move_to_next_face();
break;
// show stack
case EVENT_ALARM_LONG_PRESS:
- morsecalc_print_stack(mcs);
- mc_reset(mcs->mc);
+ morsecalc_display_stack(mcs);
+ mcs->mc = 0;
break;
// toggle light
@@ -364,7 +255,7 @@ bool morsecalc_face_loop(movement_event_t event, movement_settings_t *settings,
// quit
case EVENT_TIMEOUT:
- movement_move_to_next_face();
+ movement_move_to_face(0);
break;
case EVENT_MODE_LONG_PRESS:
movement_move_to_next_face();
diff --git a/movement/watch_faces/complication/morsecalc_face.h b/movement/watch_faces/complication/morsecalc_face.h
index bd0fd416..2ee18622 100644
--- a/movement/watch_faces/complication/morsecalc_face.h
+++ b/movement/watch_faces/complication/morsecalc_face.h
@@ -24,11 +24,27 @@
#ifndef MORSECALC_FACE_H_
#define MORSECALC_FACE_H_
-#define MORSECALC_TOKEN_LEN 9
+
+#define MORSECALC_TOKEN_LEN 32
+#define MORSECODE_LEN 5
#include "movement.h"
#include "calc.h"
-#include "mc.h"
+
+/*
+ * MC International Morse Code binary tree
+ * Levels of the tree are concatenated.
+ * '.' = 0 and '-' = 1.
+ *
+ * Capitals denote special characters:
+ * C = Ch digraph
+ * V = VERIFY (ITU-R "UNDERSTOOD")
+ * R = REPEAT
+ * W = WAIT
+ * S = START TRANSMISSION
+ * E = END OF WORK
+ */
+static const char MORSECODE_TREE[] = " etianmsurwdkgohvf\0l\0pjbxcyzq\0C\x35\x34V\x33\0R\0\x32W\0+\0\0\0\0\x31\x36=/\0\0S(\0\x37\0\0\0\x38\0\x39\x30\0\0\0\0\0E\0\0\0\0\0\0?_\0\0\0\0\"\0\0.\0\0\0\0@\0\0\0'\0\0-\0\0\0\0\0\0\0\0;!\0)\0\0\0\0\0,\0\0\0\0:\0\0\0\0\0\0";
void morsecalc_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void morsecalc_face_activate(movement_settings_t *settings, void *context);
@@ -37,17 +53,14 @@ void morsecalc_face_resign(movement_settings_t *settings, void *context);
typedef struct {
calc_state_t *cs;
- mc_state_t *mc;
+ unsigned int mc; // Morse code character
char token[MORSECALC_TOKEN_LEN];
uint8_t idxt;
uint8_t led_is_on;
} morsecalc_state_t;
-void morsecalc_print_float(double d);
-void morsecalc_print_token(morsecalc_state_t *mcs);
-void morsecalc_print_stack(morsecalc_state_t *mcs);
void morsecalc_reset_token(morsecalc_state_t *mcs);
-void morsecalc_input(morsecalc_state_t *mcs, char c);
+void morsecalc_input(morsecalc_state_t *mcs);
#define morsecalc_face ((const watch_face_t){ \
morsecalc_face_setup, \
diff --git a/movement/watch_faces/complication/planetary_hours_face.c b/movement/watch_faces/complication/planetary_hours_face.c
new file mode 100644
index 00000000..acded917
--- /dev/null
+++ b/movement/watch_faces/complication/planetary_hours_face.c
@@ -0,0 +1,402 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include
+#include "sunriset.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "planetary_hours_face.h"
+
+#if __EMSCRIPTEN__
+#include
+#endif
+
+// STATIC FUNCTIONS AND CONSTANTS /////////////////////////////////////////////
+
+/** @brief Planetary rulers in the Chaldean order from slowest to fastest
+ * @details Planetary rulers in the Chaldean order from slowest to fastest:
+ * Jupiter, Mars, Sun, Venus, Mercury, Moon
+ */
+static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin
+static const char planetes[7][3] = {"Ch", "Ze", "Ar", "He", "Af", "Hr", "Se"}; // Greek
+
+/** @brief Ruler of each weekday for easy lookup
+ */
+static const uint8_t plindex[7] = {3, 6, 2, 5, 1, 4, 0}; // day ruler index
+
+/** @brief Astrological symbol for each planet
+ */
+static void _planetary_icon(uint8_t planet) {
+
+ watch_clear_pixel(0, 13);
+ watch_clear_pixel(0, 14);
+ watch_clear_pixel(1, 13);
+ watch_clear_pixel(1, 14);
+ watch_clear_pixel(1, 15);
+ watch_clear_pixel(2, 13);
+ watch_clear_pixel(2, 14);
+ watch_clear_pixel(2, 15);
+
+ switch (planet) {
+ case 0: // Saturn
+ watch_set_pixel(0, 14);
+ watch_set_pixel(2, 14);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(2, 13);
+ break;
+ case 1: // Jupiter
+ watch_set_pixel(0, 14);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(1, 14);
+ break;
+ case 2: // Mars
+ watch_set_pixel(2, 14);
+ watch_set_pixel(2, 15);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(2, 13);
+ watch_set_pixel(1, 13);\
+ break;
+ case 3: // Sol
+ watch_set_pixel(0, 14);
+ watch_set_pixel(2, 14);
+ watch_set_pixel(1, 13);
+ watch_set_pixel(2, 13);
+ watch_set_pixel(0, 13);
+ watch_set_pixel(2, 15);
+ break;
+ case 4: // Venus
+ watch_set_pixel(0, 14);
+ watch_set_pixel(0, 13);
+ watch_set_pixel(1, 13);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(1, 14);
+ break;
+ case 5: // Mercury
+ watch_set_pixel(0, 14);
+ watch_set_pixel(1, 13);
+ watch_set_pixel(1, 14);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(2, 15);
+ break;
+ case 6: // Luna
+ watch_set_pixel(2, 14);
+ watch_set_pixel(2, 15);
+ watch_set_pixel(2, 13);
+ break;
+ }
+}
+
+/** @details A solar phase can be a day phase between sunrise and sunset or an alternating night phase.
+ * This function calculates the start and end of the current phase based on a given geographic location.
+ * It also calculates the start of the next following phase.
+ */
+static void _planetary_solar_phases(movement_settings_t *settings, planetary_hours_state_t *state) {
+ uint8_t phase, h;
+ double sunrise, sunset;
+ double hour_duration, next_hour_duration;
+ uint32_t now_epoch;
+ uint32_t sunrise_epoch_today, sunset_epoch_today, midnight_epoch_today;
+ uint32_t sunset_epoch_yesterday, midnight_epoch_yesterday;
+ uint32_t sunrise_epoch_tomorrow, sunset_epoch_tomorrow, midnight_epoch_tomorrow;
+ movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+
+ // check if we have a location. If not, display error
+ if (movement_location.reg == 0) {
+ watch_display_string(" no Loc", 0);
+ state->no_location = true;
+ return;
+ }
+
+ // location detected
+ state->no_location = false;
+
+ watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
+ watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC
+ watch_date_time scratch_time; // scratchpad, contains different values at different times
+ watch_date_time midnight;
+ scratch_time.reg = midnight.reg = utc_now.reg;
+ midnight.unit.hour = midnight.unit.minute = midnight.unit.second = 0; // start of the day at midnight
+
+ // get location coordinate
+ int16_t lat_centi = (int16_t)movement_location.bit.latitude;
+ int16_t lon_centi = (int16_t)movement_location.bit.longitude;
+ double lat = (double)lat_centi / 100.0;
+ double lon = (double)lon_centi / 100.0;
+
+ // save UTC offset
+ state->utc_offset = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0;
+
+ // calculate sunrise and sunset of current day in decimal hours after midnight
+ sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+
+ // calculate sunrise and sunset UNIX timestamps
+ midnight_epoch_today = watch_utility_date_time_to_unix_time(midnight, 0);
+ sunrise_epoch_today = midnight_epoch_today + sunrise * 3600;
+ sunset_epoch_today = midnight_epoch_today + sunset * 3600;
+
+ // go back to yesterday and calculate sunset
+ midnight_epoch_yesterday = midnight_epoch_today - 86400;
+ scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch_yesterday, 0);
+ sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+ sunset_epoch_yesterday = midnight_epoch_yesterday + sunset * 3600;
+
+ // go to tomorrow and calculate sunrise and sunset
+ midnight_epoch_tomorrow = midnight_epoch_today + 86400;
+ scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch_tomorrow, 0);
+ sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+ sunrise_epoch_tomorrow = midnight_epoch_tomorrow + sunrise * 3600;
+ sunset_epoch_tomorrow = midnight_epoch_tomorrow + sunset * 3600;
+
+ // get UNIX epoch time
+ now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
+
+ // by default we assume it is daytime (phase 1) between sunrise and sunset
+ phase = 1;
+ state->phase_start = sunrise_epoch_today;
+ state->phase_end = sunset_epoch_today;
+ state->phase_next = sunrise_epoch_tomorrow;
+ state->start_at_night = false;
+
+ // night time calculations
+ if ( now_epoch < sunrise_epoch_today && now_epoch < sunset_epoch_today ) phase = 0; // morning before dawn
+ if ( now_epoch > sunrise_epoch_today && now_epoch >= sunset_epoch_today ) phase = 2; // evening after dusk
+
+ // phase 0: we are before sunrise
+ if ( phase == 0) {
+ state->phase_start = sunset_epoch_yesterday;
+ state->phase_end = sunrise_epoch_today;
+ state->phase_next = sunset_epoch_today;
+ state->start_at_night = true;
+ }
+
+ // phase 2: we are after sunset
+ if ( phase == 2) {
+ state->phase_start = sunset_epoch_today;
+ state->phase_end = sunrise_epoch_tomorrow;
+ state->phase_next = sunset_epoch_tomorrow;
+ state->start_at_night = true;
+ }
+
+ // calculate the duration of a planetary hour during this and the next solar phase
+ hour_duration = ( state->phase_end - state->phase_start ) / 12.0;
+ next_hour_duration = ( state->phase_next - state->phase_end ) / 12.0;
+
+ // populate list of 24 planetary hour start points in UNIX timestamp format
+ // starting from the beginning of the current phase
+ for ( h = 0; h < 24; h++ ) {
+ if ( h < 12 ) state->planetary_hours[h] = state->phase_start + h * hour_duration; // current phase
+ else state->planetary_hours[h] = state->phase_end + ( h - 12 ) * next_hour_duration; // next phase
+ }
+
+ // initialize
+ state->hour = 0;
+ state->ruler = 0;
+ state->skip_to_current = true;
+
+}
+
+/** @details A planetary hour is one of exactly twelve hours of a solar phase. Its length varies.
+ * This function calculates the current planetary hour and divides it up into relative minutes and seconds.
+ * It also calculates the current planetary ruler of the hour and of the day.
+ */
+static void _planetary_hours(movement_settings_t *settings, planetary_hours_state_t *state) {
+ char buf[14];
+ char ruler[3];
+ uint8_t weekday, planet, planetary_hour;
+ uint32_t current_hour_epoch;
+ watch_date_time scratch_time;
+
+ // check if we have a location. If not, display error
+ if ( state->no_location ) {
+ watch_display_string(" no Loc", 0);
+ return;
+ }
+
+ // get current time
+ watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
+ watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC
+ current_hour_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
+
+ // set the current planetary hour as default screen
+ if ( state->skip_to_current ) {
+ state->hour = ( current_hour_epoch - state->phase_start ) / (( state->phase_end - state->phase_start ) / 12.0);
+ state->skip_to_current = false;
+ }
+
+
+ // when current phase ends calculate the next phase
+ if ( watch_utility_date_time_to_unix_time(utc_now, 0) >= state->phase_end ) {
+ _planetary_solar_phases(settings, state);
+ return;
+ }
+
+ if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
+
+ // roll over hour iterator
+ if ( state->hour < 0 ) state->hour = 23;
+ if ( state->hour > 23 ) state->hour = 0;
+ if ( state->ruler < 0 ) state->hour = 2;
+ if ( state->ruler > 2 ) state->hour = 0;
+
+ // clear indicators
+ watch_clear_indicator(WATCH_INDICATOR_BELL);
+ watch_clear_indicator(WATCH_INDICATOR_LAP);
+
+ // display bell indicator when displaying the current planetary hour
+ if ( state->hour < 24 )
+ if ( current_hour_epoch >= state->planetary_hours[state->hour] && current_hour_epoch < state->planetary_hours[state->hour + 1]) {
+ watch_set_indicator(WATCH_INDICATOR_BELL);
+ }
+
+ // display LAP indicator when the hours of the next phase belong to the next day
+ if ( state->start_at_night == true && state->hour > 11 )
+ watch_set_indicator(WATCH_INDICATOR_LAP);
+
+ // determine weekday from start of current phase
+ scratch_time = watch_utility_date_time_from_unix_time(state->phase_start, 0);
+ scratch_time = watch_utility_date_time_convert_zone(scratch_time, 0, state->utc_offset * 3600);
+ weekday = watch_utility_get_iso8601_weekday_number(scratch_time.unit.year, scratch_time.unit.month, scratch_time.unit.day) - 1;
+
+ // which planetary hour are we in?
+ planetary_hour = state->hour % 12;
+
+ // accomodate night hour count
+ if ( state->hour < 12 ) {
+ if ( state->start_at_night ) {
+ planetary_hour += 12;
+ }
+ } else {
+ if ( state->start_at_night ) {
+ weekday = ( weekday + 1 ) % 7;
+ } else {
+ planetary_hour += 12;
+ }
+ }
+
+ // make datetime object for selected planetary hour
+ scratch_time = watch_utility_date_time_from_unix_time(state->planetary_hours[state->hour], 0);
+ scratch_time = watch_utility_date_time_convert_zone(scratch_time, 0, state->utc_offset * 3600);
+
+ // round minutes
+ if (scratch_time.unit.second < 30 && scratch_time.unit.minute > 0 ) scratch_time.unit.minute--;
+ else if ( scratch_time.unit.minute < 59 ) scratch_time.unit.minute++;
+
+ // if we are in 12 hour mode, do some cleanup
+ if (!settings->bit.clock_mode_24h) {
+ if (scratch_time.unit.hour < 12) {
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ } else {
+ watch_set_indicator(WATCH_INDICATOR_PM);
+ }
+ scratch_time.unit.hour %= 12;
+ if (scratch_time.unit.hour == 0) scratch_time.unit.hour = 12;
+ }
+
+ // planetary ruler of the hour
+ planet = ( plindex[weekday] + planetary_hour ) % 7;
+
+ // latin or greek ruler names or astrological symbol
+ if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3);
+ if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3);
+ if ( state->ruler == 2 ) strncpy(ruler, " ", 3);
+
+ // display planetary time with ruler of the hour or ruler of the day
+ sprintf(buf, "%s%2d%2d%02d ", ruler, (planetary_hour % 24) + 1, scratch_time.unit.hour, scratch_time.unit.minute);
+
+ watch_set_colon();
+ watch_display_string(buf, 0);
+
+ if ( state->ruler == 2 ) _planetary_icon(planet);
+}
+
+// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
+
+void planetary_hours_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) watch_face_index;
+ (void) settings;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(planetary_hours_state_t));
+ memset(*context_ptr, 0, sizeof(planetary_hours_state_t));
+ }
+}
+
+void planetary_hours_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ if (watch_tick_animation_is_running()) watch_stop_tick_animation();
+
+#if __EMSCRIPTEN__
+ int16_t browser_lat = EM_ASM_INT({ return lat; });
+ int16_t browser_lon = EM_ASM_INT({ return lon; });
+ if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
+ movement_location_t browser_loc;
+ browser_loc.bit.latitude = browser_lat;
+ browser_loc.bit.longitude = browser_lon;
+ watch_store_backup_data(browser_loc.reg, 1);
+ }
+#endif
+
+ planetary_hours_state_t *state = (planetary_hours_state_t *)context;
+ _planetary_solar_phases(settings, state);
+
+}
+
+bool planetary_hours_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ planetary_hours_state_t *state = (planetary_hours_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ // Show your initial UI here.
+ watch_clear_indicator(WATCH_INDICATOR_PM);
+ watch_clear_indicator(WATCH_INDICATOR_24H);
+ _planetary_hours(settings, state);
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ state->ruler = (state->ruler + 1) % 3;
+ _planetary_hours(settings, state);
+ break;
+ case EVENT_LIGHT_LONG_PRESS:
+ state->skip_to_current = true;
+ _planetary_hours(settings, state);
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ state->hour++;
+ _planetary_hours(settings, state);
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ state->hour--;
+ _planetary_hours(settings, state);
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+ return true;
+}
+
+void planetary_hours_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+}
+
diff --git a/movement/watch_faces/complication/planetary_hours_face.h b/movement/watch_faces/complication/planetary_hours_face.h
new file mode 100644
index 00000000..53237df2
--- /dev/null
+++ b/movement/watch_faces/complication/planetary_hours_face.h
@@ -0,0 +1,107 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ * Copyright (c) 2022 Joey Castillo (sunrise_sunset_face)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef planetary_hours_face_H_
+#define planetary_hours_face_H_
+
+#include "movement.h"
+#include "sunrise_sunset_face.h"
+
+/*
+ * BACKGROUND
+
+ * Both the 24 hour day and the order of our weekdays have quite esoteric roots.
+ * The ancient Egyptians divided the day up into 12 hours of sunlight and 12 hours
+ * of night time. Obviously the length of these hours varied throughout the year.
+ *
+ * The Greeks assigned each hour a ruler of the planetary gods in the ancient
+ * "Chaldean" order from slowest (Chronos for Saturn) to fastest (Selene for Moon).
+ * Because 24 hours cannot be equally divided by seven, the planetary rulers carried
+ * over to the first hour of the next day, effectively ruling over the entire day
+ * and lending the whole day their name. The seven day week was born.
+ *
+ * PLANETARY HOUR CHART COMPLICATION
+ *
+ * This complication watch face displays the start time of the current planetary hour
+ * according to the given location and day of the year. The number of the current
+ * planetary hour (1 - 24) is indicated at the top right.
+ *
+ * Short pressing the ALARM button flips through the start times of the following
+ * planetary hours, long pressing it flips backwards in time. A long press of the
+ * LIGHT button immediately switches back to the start time of the current hour.
+ * The Bell indicator always marks the current planetary hour in the list.
+ * The LAP indicator shows up when the hours of the next phase are part of the
+ * upcoming day instead of the current one. This happens when the watch face is
+ * launched after sunset.
+ *
+ * The planetary ruler of the current hour and day is displayed at the top in
+ * Latin or Greek shorthand notation:
+ *
+ * Saturn (SA) / Chronos (CH) / ♄
+ * Jupiter (JU) / Zeus (ZE) / ♃
+ * Mars (MA) / Ares (AR) / ♂
+ * Sol (SO) / Helios (HE) / ☉
+ * Venus (VE) / Aphrodite (AF) / ♀
+ * Mercury (ME) / Hermes (HR) / ☿
+ * Luna (LU) / Selene (SE) / ☾
+ *
+ * A short press of the LIGHT button toggles between Latin and Greek ruler shorthand
+ * notation.
+ *
+ * (IMPORTANT: Make sure the watch's time, timezone and location are set correctly for this
+ * watch face to work properly!)
+ */
+
+typedef struct {
+ // Anything you need to keep track of, put it here!
+ uint32_t planetary_hours[24];
+ uint32_t phase_start;
+ uint32_t phase_end;
+ uint32_t phase_next;
+ bool next;
+ double utc_offset;
+ bool no_location;
+ int8_t hour;
+ int8_t ruler;
+ bool start_at_night;
+ bool skip_to_current;
+ sunrise_sunset_state_t sunstate;
+} planetary_hours_state_t;
+
+void planetary_hours_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void planetary_hours_face_activate(movement_settings_t *settings, void *context);
+bool planetary_hours_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void planetary_hours_face_resign(movement_settings_t *settings, void *context);
+
+#define planetary_hours_face ((const watch_face_t){ \
+ planetary_hours_face_setup, \
+ planetary_hours_face_activate, \
+ planetary_hours_face_loop, \
+ planetary_hours_face_resign, \
+ NULL, \
+})
+
+#endif // planetary_hours_face_H_
+
diff --git a/movement/watch_faces/complication/planetary_time_face.c b/movement/watch_faces/complication/planetary_time_face.c
new file mode 100644
index 00000000..56a18cf2
--- /dev/null
+++ b/movement/watch_faces/complication/planetary_time_face.c
@@ -0,0 +1,338 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include
+#include "sunriset.h"
+#include "watch.h"
+#include "watch_utility.h"
+#include "planetary_time_face.h"
+
+#if __EMSCRIPTEN__
+#include
+#endif
+
+// STATIC FUNCTIONS AND CONSTANTS /////////////////////////////////////////////
+
+/** @brief Planetary rulers in the Chaldean order from slowest to fastest
+ * @details Planetary rulers in the Chaldean order from slowest to fastest:
+ * Jupiter, Mars, Sun, Venus, Mercury, Moon
+ */
+static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin
+static const char planetes[7][3] = {"Ch", "Ze", "Ar", "He", "Af", "Hr", "Se"}; // Greek
+
+/** @brief Ruler of each weekday for easy lookup
+ */
+static const uint8_t plindex[7] = {3, 6, 2, 5, 1, 4, 0}; // day ruler index
+
+/** @brief Astrological symbol for each planet
+ */
+static void _planetary_icon(uint8_t planet) {
+
+ watch_clear_pixel(0, 13);
+ watch_clear_pixel(0, 14);
+ watch_clear_pixel(1, 13);
+ watch_clear_pixel(1, 14);
+ watch_clear_pixel(1, 15);
+ watch_clear_pixel(2, 13);
+ watch_clear_pixel(2, 14);
+ watch_clear_pixel(2, 15);
+
+ switch (planet) {
+ case 0: // Saturn
+ watch_set_pixel(0, 14);
+ watch_set_pixel(2, 14);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(2, 13);
+ break;
+ case 1: // Jupiter
+ watch_set_pixel(0, 14);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(1, 14);
+ break;
+ case 2: // Mars
+ watch_set_pixel(2, 14);
+ watch_set_pixel(2, 15);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(2, 13);
+ watch_set_pixel(1, 13);\
+ break;
+ case 3: // Sol
+ watch_set_pixel(0, 14);
+ watch_set_pixel(2, 14);
+ watch_set_pixel(1, 13);
+ watch_set_pixel(2, 13);
+ watch_set_pixel(0, 13);
+ watch_set_pixel(2, 15);
+ break;
+ case 4: // Venus
+ watch_set_pixel(0, 14);
+ watch_set_pixel(0, 13);
+ watch_set_pixel(1, 13);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(1, 14);
+ break;
+ case 5: // Mercury
+ watch_set_pixel(0, 14);
+ watch_set_pixel(1, 13);
+ watch_set_pixel(1, 14);
+ watch_set_pixel(1, 15);
+ watch_set_pixel(2, 15);
+ break;
+ case 6: // Luna
+ watch_set_pixel(2, 14);
+ watch_set_pixel(2, 15);
+ watch_set_pixel(2, 13);
+ break;
+ }
+}
+
+/** @details solar phase can be a day phase between sunrise and sunset or an alternating night phase.
+ * This function calculates the start and end of the current phase based on a given geographic location.
+ */
+static void _planetary_solar_phase(movement_settings_t *settings, planetary_time_state_t *state) {
+ uint8_t phase;
+ double sunrise, sunset;
+ uint32_t now_epoch, sunrise_epoch, sunset_epoch, midnight_epoch;
+ movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+
+ // check if we have a location. If not, display error
+ if (movement_location.reg == 0) {
+ watch_display_string(" no Loc", 0);
+ state->no_location = true;
+ return;
+ }
+
+ // location detected
+ state->no_location = false;
+
+ watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
+ watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC
+ watch_date_time scratch_time; // scratchpad, contains different values at different times
+ watch_date_time midnight;
+ scratch_time.reg = midnight.reg = utc_now.reg;
+ midnight.unit.hour = midnight.unit.minute = midnight.unit.second = 0; // start of the day at midnight
+
+ // get location coordinate
+ int16_t lat_centi = (int16_t)movement_location.bit.latitude;
+ int16_t lon_centi = (int16_t)movement_location.bit.longitude;
+ double lat = (double)lat_centi / 100.0;
+ double lon = (double)lon_centi / 100.0;
+
+ // save UTC offset
+ state->utc_offset = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0;
+
+ // get UNIX epoch time
+ now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
+ midnight_epoch = watch_utility_date_time_to_unix_time(midnight, 0);
+
+ // calculate sunrise and sunset of current day in decimal hours after midnight
+ sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+
+ // calculate sunrise and sunset UNIX timestamps
+ sunrise_epoch = midnight_epoch + sunrise * 3600;
+ sunset_epoch = midnight_epoch + sunset * 3600;
+
+ // by default we assume it is daytime (phase 1) between sunrise and sunset
+ phase = 1;
+ state->night = false;
+ state->phase_start = sunrise_epoch;
+ state->phase_end = sunset_epoch;
+
+ // night time calculations
+ if ( now_epoch < sunrise_epoch && now_epoch < sunset_epoch ) phase = 0; // morning before dawn
+ if ( now_epoch > sunrise_epoch && now_epoch >= sunset_epoch ) phase = 2; // evening after dusk
+
+ // phase 0: we are before sunrise
+ if ( phase == 0) {
+ // go back to yesterday and calculate sunset
+ midnight_epoch -= 86400;
+ scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch, 0);
+ sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+ sunset_epoch = midnight_epoch + sunset * 3600;
+ // we are still in yesterday's night hours
+ state->night = true;
+ state->phase_start = sunset_epoch;
+ state->phase_end = sunrise_epoch;
+ }
+
+ // phase 2: we are after sunset
+ if ( phase == 2) {
+ // skip to tomorrow and calculate sunrise
+ midnight_epoch += 86400;
+ scratch_time = watch_utility_date_time_from_unix_time(midnight_epoch, 0);
+ sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
+ sunrise_epoch = midnight_epoch + sunrise * 3600;
+ // we are still in yesterday's night hours
+ state->night = true;
+ state->phase_start = sunset_epoch;
+ state->phase_end = sunrise_epoch;
+ }
+
+ // calculate the duration of a planetary second during this solar phase
+ // and convert to Hertz so we can call a faster tick rate
+ state->freq = (1 / ((double)( state->phase_end - state->phase_start ) / 43200));
+}
+
+/** @details A planetary hour is one of exactly twelve hours of a solar phase. Its length varies.
+ * This function calculates the current planetary hour and divides it up into relative minutes and seconds.
+ * It also calculates the current planetary ruler of the hour and of the day.
+ */
+static void _planetary_time(movement_event_t event, movement_settings_t *settings, planetary_time_state_t *state) {
+ char buf[14];
+ char ruler[3];
+ double night_hour_count = 0.0;
+ uint8_t weekday, planet, planetary_hour;
+ double hour_duration, current_hour, current_minute, current_second;
+
+ watch_set_colon();
+
+ // get current time and convert to UTC
+ state->scratch = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0);
+
+ // when current phase ends calculate the next phase
+ if ( watch_utility_date_time_to_unix_time(state->scratch, 0) >= state->phase_end ) {
+ _planetary_solar_phase(settings, state);
+ return;
+ }
+
+ if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
+
+ // PM for night hours, otherwise the night hours are counted from 13
+ if ( state->night ) {
+ if (settings->bit.clock_mode_24h) night_hour_count = 12;
+ else watch_set_indicator(WATCH_INDICATOR_PM);
+ }
+
+ // calculate the duration of a planetary hour during this solar phase
+ hour_duration = (( state->phase_end - state->phase_start)) / 12.0;
+
+ // which planetary hour are we in?
+
+ // RTC only provides full second precision, so we have to manually add subseconds with each tick
+ current_hour = ((( watch_utility_date_time_to_unix_time(state->scratch, 0) ) + event.subsecond * 0.11111111) - state->phase_start ) / hour_duration;
+ planetary_hour = floor(current_hour) + ( state->night ? 12 : 0 );
+ current_hour += night_hour_count; //adjust for 24hr display
+ current_minute = modf(current_hour, ¤t_hour) * 60.0;
+ current_second = modf(current_minute, ¤t_minute) * 60.0;
+
+ // the day changes after sunrise, so if we are at night it is yesterday's planetary day
+ // hence we take the datetime object of when the last solar phase started as the current day
+ // and then fill in the hours and minutes
+ state->scratch = watch_utility_date_time_from_unix_time(state->phase_start, 0);
+ state->scratch.unit.hour = floor(current_hour);
+ state->scratch.unit.minute = floor(current_minute);
+ state->scratch.unit.second = (uint8_t)floor(current_second) % 60;
+
+ // what weekday is it (0 - 6)
+ weekday = watch_utility_get_iso8601_weekday_number(state->scratch.unit.year, state->scratch.unit.month, state->scratch.unit.day) - 1;
+
+ // planetary ruler of the hour or the day
+ if ( state->day_ruler ) planet = plindex[weekday];
+ else planet = ( plindex[weekday] + planetary_hour ) % 7;
+
+ // latin or greek ruler names or astrological symbol
+ if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3);
+ if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3);
+ if ( state->ruler == 2 ) strncpy(ruler, " ", 3);
+
+ // display planetary time with ruler of the hour or ruler of the day
+ if ( state->day_ruler ) sprintf(buf, "%s d%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second);
+ else sprintf(buf, "%s h%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second);
+
+ watch_display_string(buf, 0);
+
+ if ( state->ruler == 2 ) _planetary_icon(planet);
+
+}
+
+// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
+
+void planetary_time_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) watch_face_index;
+ (void) settings;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(planetary_time_state_t));
+ memset(*context_ptr, 0, sizeof(planetary_time_state_t));
+ }
+}
+
+void planetary_time_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ if (watch_tick_animation_is_running()) watch_stop_tick_animation();
+
+#if __EMSCRIPTEN__
+ int16_t browser_lat = EM_ASM_INT({ return lat; });
+ int16_t browser_lon = EM_ASM_INT({ return lon; });
+ if ((watch_get_backup_data(1) == 0) && (browser_lat || browser_lon)) {
+ movement_location_t browser_loc;
+ browser_loc.bit.latitude = browser_lat;
+ browser_loc.bit.longitude = browser_lon;
+ watch_store_backup_data(browser_loc.reg, 1);
+ }
+#endif
+
+ planetary_time_state_t *state = (planetary_time_state_t *)context;
+
+ // calculate phase
+ _planetary_solar_phase(settings, state);
+}
+
+bool planetary_time_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ planetary_time_state_t *state = (planetary_time_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ _planetary_time(event, settings, state);
+ if ( state->freq > 1 )
+ // for hours with shorter seconds let's increase the tick to not skip seconds in the display
+ movement_request_tick_frequency( 8 );
+ break;
+ case EVENT_TICK:
+ _planetary_time(event, settings, state);
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ state->ruler = (state->ruler + 1) % 3;
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ // Just in case you have need for another button.
+ state->day_ruler = !state->day_ruler;
+ break;
+ case EVENT_LOW_ENERGY_UPDATE:
+ watch_start_tick_animation(500);
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+
+ return true;
+}
+
+void planetary_time_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+ movement_request_tick_frequency( 1 );
+}
+
diff --git a/movement/watch_faces/complication/planetary_time_face.h b/movement/watch_faces/complication/planetary_time_face.h
new file mode 100644
index 00000000..0ecc11af
--- /dev/null
+++ b/movement/watch_faces/complication/planetary_time_face.h
@@ -0,0 +1,108 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ * Copyright (c) 2022 Joey Castillo (sunrise_sunset_face)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef planetary_time_face_H_
+#define planetary_time_face_H_
+
+#include "movement.h"
+#include "sunrise_sunset_face.h"
+
+/*
+ * BACKGROUND
+
+ * Both the 24 hour day and the order of our weekdays have quite esoteric roots.
+ * The ancient Egyptians divided the day up into 12 hours of sunlight and 12 hours
+ * of night time. Obviously the length of these hours varied throughout the year.
+ *
+ * The Greeks assigned each hour a ruler of the planetary gods in the ancient
+ * "Chaldean" order from slowest (Chronos for Saturn) to fastest (Selene for Moon).
+ * Because 24 hours cannot be equally divided by seven, the planetary rulers carried
+ * over to the first hour of the next day, effectively ruling over the entire day
+ * and lending the whole day their name. The seven day week was born.
+ *
+ * PLANETARY TIME COMPLICATION
+ *
+ * The hour digits of this complication watch-face display the current planetary hour
+ * according to the given location and day of the year (First hour from 12am to 1am,
+ * the second hour from 1am to 2am, and so forth).
+ *
+ * Like with normal clocks the minutes and seconds help dividing the hour into smaller
+ * units. On this watch-face, all units naturally vary in length because the planetary
+ * hours are not fixed by duration but by the moments of sunrise and sunset which
+ * obviously vary throughout the year, especially in higher latitudes.
+ *
+ * On this watch-face the hours indicated as 12am to 12pm (00:00 - 12:00) are used for
+ * the planetary daytime hours between sunrise and sunset and hours indicated as 12pm
+ * to 12am (12:00 - 00:00) are used for the planetary night hours after sunset and before
+ * sunrise.
+ *
+ * The planetary ruler of the current hour and day is displayed at the top in Latin or
+ * Greek shorthand notation:
+ *
+ * Saturn (SA) / Chronos (CH) / ♄
+ * Jupiter (JU) / Zeus (ZE) / ♃
+ * Mars (MA) / Ares (AR) / ♂
+ * Sol (SO) / Helios (HE) / ☉
+ * Venus (VE) / Aphrodite (AF) / ♀
+ * Mercury (ME) / Hermes (HR) / ☿
+ * Luna (LU) / Selene (SE) / ☾
+ *
+ * The ALARM button toggles between displaying the ruler of the hour and the ruler of the day
+ *
+ * The LIGHT button toggles between Latin and Greek ruler shorthand notation
+ *
+ * (IMPORTANT: Make sure the watch's time, timezone and location are set correctly for this
+ * watch face to work properly!)
+ */
+
+typedef struct {
+ // Anything you need to keep track of, put it here!
+ uint32_t phase_start;
+ uint32_t phase_end;
+ bool night;
+ double utc_offset;
+ double freq;
+ uint8_t ruler;
+ bool day_ruler;
+ bool no_location;
+ sunrise_sunset_state_t sunstate;
+ watch_date_time scratch;
+} planetary_time_state_t;
+
+void planetary_time_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void planetary_time_face_activate(movement_settings_t *settings, void *context);
+bool planetary_time_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void planetary_time_face_resign(movement_settings_t *settings, void *context);
+
+#define planetary_time_face ((const watch_face_t){ \
+ planetary_time_face_setup, \
+ planetary_time_face_activate, \
+ planetary_time_face_loop, \
+ planetary_time_face_resign, \
+ NULL, \
+})
+
+#endif // planetary_time_face_H_
+
diff --git a/movement/watch_faces/complication/randonaut_face.c b/movement/watch_faces/complication/randonaut_face.c
new file mode 100644
index 00000000..1a3eb21d
--- /dev/null
+++ b/movement/watch_faces/complication/randonaut_face.c
@@ -0,0 +1,414 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// Emulator only: need time() to seed the random number generator.
+#if __EMSCRIPTEN__
+#include
+#else
+#include "saml22j18a.h"
+#endif
+
+#include
+#include
+#include
+#include "filesystem.h"
+#include "randonaut_face.h"
+
+#define R 6371 // Earth's radius in km
+#define PI 3.14159265358979323846
+
+static void _get_location_from_file(randonaut_state_t *state);
+static void _save_point_to_file(randonaut_state_t *state);
+static void _get_entropy(randonaut_state_t *state);
+static void _generate_blindspot(randonaut_state_t *state);
+static void _randonaut_face_display(randonaut_state_t *state);
+static void _generate_blindspot(randonaut_state_t *state);
+static uint32_t _get_pseudo_entropy(uint32_t max);
+static uint32_t _get_true_entropy(void);
+static void _get_entropy(randonaut_state_t *state);
+
+// MOVEMENT WATCH FACE FUNCTIONS //////////////////////////////////////////////
+
+void randonaut_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) settings;
+ (void) watch_face_index;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(randonaut_state_t));
+ memset(*context_ptr, 0, sizeof(randonaut_state_t));
+ // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+ }
+ // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void randonaut_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ randonaut_state_t *state = (randonaut_state_t *)context;
+ _get_location_from_file(state);
+ state->face.mode = 0;
+ state->radius = 1000;
+ _get_entropy(state);
+ state->chance = true;
+ // Handle any tasks related to your watch face coming on screen.
+}
+
+bool randonaut_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ randonaut_state_t *state = (randonaut_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ // Show your initial UI here.
+ break;
+ case EVENT_TICK:
+ // If needed, update your display here.
+ break;
+ case EVENT_LIGHT_BUTTON_DOWN:
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ switch ( state->face.mode ) {
+ case 0: // home
+ state->face.mode = 2; //point
+ state->face.location_format = 0; // title
+ break;
+ case 1: // generate
+ state->face.mode = 0; //home
+ break;
+ case 2: // point
+ state->face.mode = 0; //home
+ break;
+ case 3: // setup radius
+ state->face.mode = 4; // toggle to RNG
+ break;
+ case 4: // setup RNG
+ state->face.mode = 3; // toggle to Radius
+ break;
+ case 5: // data processing
+ break;
+ }
+ break;
+ case EVENT_LIGHT_LONG_PRESS:
+ switch ( state->face.mode ) {
+ case 3: // setup
+ case 4:
+ state->face.mode = 0; //home
+ break;
+ default:
+ state->face.mode = 3; //setup
+ watch_clear_display();
+ }
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ switch ( state->face.mode ) {
+ case 0: //home
+ state->face.mode = 1; // generate
+ break;
+ case 2: // point
+ state->face.location_format = (( state->face.location_format + 1) % (7));
+ if ( state->face.location_format == 0 )
+ state->face.location_format++;
+ break;
+ case 3: //setup radius
+ state->radius += 500;
+ if ( state->radius > 10000 )
+ state->radius = 1000;
+ break;
+ case 4: //setup RNG
+ state->face.rng = (state->face.rng + 1) % 3;
+ switch ( state->face.rng ) {
+ case 0:
+ state->chance = true;
+ break;
+ case 1:
+ state->chance = false;
+ state->quantum = true;
+ break;
+ case 2:
+ state->chance = false;
+ state->quantum = false;
+ break;
+ }
+ break;
+ case 5: // data processing
+ _save_point_to_file(state);
+ break;
+ default:
+ break;
+ }
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ if ( state->face.mode == 5 )
+ state->face.mode = 0; // home
+ else
+ state->face.mode = 5; // data processing
+ break;
+ case EVENT_TIMEOUT:
+ // Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
+ // you may uncomment this line to move back to the first watch face in the list:
+ // movement_move_to_face(0);
+ break;
+ case EVENT_LOW_ENERGY_UPDATE:
+ // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
+ // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
+ // You should also consider starting the tick animation, to show the wearer that this is sleep mode:
+ // watch_start_tick_animation(500);
+ break;
+ default:
+ // Movement's default loop handler will step in for any cases you don't handle above:
+ // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+ // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+ // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
+ // You can override any of these behaviors by adding a case for these events to this switch statement.
+ return movement_default_loop_handler(event, settings);
+ }
+
+ _randonaut_face_display(state);
+
+ // return true if the watch can enter standby mode. Generally speaking, you should always return true.
+ // Exceptions:
+ // * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
+ // * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
+ // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
+ // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
+ return true;
+}
+
+void randonaut_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+
+ // handle any cleanup before your watch face goes off-screen.
+}
+
+// PRIVATE STATIC FUNCTIONS ///////////////////////////////////////////////////
+
+/** @brief display handler
+ */
+static void _randonaut_face_display(randonaut_state_t *state) {
+ char buf[12];
+ watch_clear_colon();
+ switch ( state->face.mode ) {
+ case 0: //home
+ sprintf(buf, "RA Rando");
+ break;
+ case 1: //generate
+ if ( state->quantum )
+ // All Hail Steve /;[;[/.;]/[.;[/;/;/;/;.;.];.]]--=/
+ for ( uint8_t c = 100; c > 0; c--) {
+ watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(0x33-0x1C));
+ watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(3432-3409));
+ watch_set_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(0xE +9));
+ watch_set_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(23));
+ watch_set_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(12+7+11));
+ if( c < 70 ) {
+ watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(12+7+11));
+ }
+ if ( c < 60 ) {
+ watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(0xD68-0xD4A));
+ }
+ if ( c < 50 ) {
+ watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(14+9));
+ }
+ delay_ms(_get_pseudo_entropy(c)+20);
+ if ( c < 30 ) {
+ watch_display_string(" ",_get_pseudo_entropy(10));
+ }
+ watch_clear_pixel(_get_pseudo_entropy(02),_get_pseudo_entropy(3432-3409));
+ watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(51-28));
+ watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(23));
+ if ( c < 20 ) {
+ watch_clear_pixel(_get_pseudo_entropy(02),_get_pseudo_entropy(51-28));
+ watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(14+9));
+ watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(0xD68-0xD4A));
+ watch_clear_pixel(_get_pseudo_entropy(0x2),_get_pseudo_entropy(3432-3409));
+ watch_clear_pixel(_get_pseudo_entropy(002),_get_pseudo_entropy(12+7+11));
+ watch_clear_pixel(_get_pseudo_entropy(2),_get_pseudo_entropy(51-28));
+ }
+ }
+ else
+ for ( uint8_t c = 30; c > 0; c--) {
+ watch_display_string("1", _get_pseudo_entropy(10));
+ watch_display_string("0", _get_pseudo_entropy(10));
+ watch_display_string("11", _get_pseudo_entropy(10));
+ watch_display_string("00", _get_pseudo_entropy(10));
+ delay_ms(50);
+ watch_display_string(" ", _get_pseudo_entropy(10));
+ watch_display_string(" ", _get_pseudo_entropy(10));
+ watch_display_string(" ", _get_pseudo_entropy(10));
+ watch_display_string(" ", _get_pseudo_entropy(10));
+ }
+ _generate_blindspot(state);
+ watch_clear_display();
+ state->face.mode = 2; // point
+ state->face.location_format = 1; // distance
+ watch_display_string("RA Found", 0);
+ delay_ms(500);
+ sprintf(buf, "RA Found");
+ break;
+ case 2: //point
+ switch ( state->face.location_format ) {
+ case 0:
+ sprintf(buf, "RA Point");
+ break;
+ case 1: // distance to point
+ watch_clear_display();
+ sprintf(buf, "DI m %d", state->point.distance );
+ break;
+ case 2: // bearing relative to point
+ watch_clear_display();
+ sprintf(buf, "BE # %d", state->point.bearing );
+ break;
+ case 3: // latitude DD._____
+ sprintf(state->scratchpad, "%07d", abs(state->point.latitude));
+ sprintf(buf, "LA #%c %c%c ", state->point.latitude < 0 ? '-' : '+', state->scratchpad[0], state->scratchpad[1]);
+ break;
+ case 4: // latitude __.DDDDD
+ sprintf(buf, "LA , %c%c%c%c%c", state->scratchpad[2], state->scratchpad[3],state->scratchpad[4], state->scratchpad[5],state->scratchpad[6]);
+ break;
+ case 5: // longitude DD._____
+ sprintf(state->scratchpad, "%08d", abs(state->point.longitude));
+ sprintf(buf, "LO #%c%c%c%c ", state->point.longitude < 0 ? '-' : '+',state->scratchpad[0], state->scratchpad[1], state->scratchpad[2]);
+ break;
+ case 6: // longitude __.DDDDD
+ sprintf(buf, "LO , %c%c%c%c%c", state->scratchpad[3], state->scratchpad[4],state->scratchpad[5], state->scratchpad[6],state->scratchpad[7]);
+ break;
+ }
+ break;
+ case 3: // setup radius
+ watch_set_colon();
+ if ( state->radius < 10000 )
+ sprintf(buf, "RA m %d ", state->radius);
+ else
+ sprintf(buf, "RA m%d ", state->radius);
+ break;
+ case 4: // setup RNG
+ sprintf(buf, "RN G %s ", state->chance ? "Chnce" : (state->quantum ? "True" : "Psudo"));
+ break;
+ case 5: // data processing
+ sprintf(buf, "WR File ");
+ }
+ watch_display_string(buf, 0);
+}
+
+/** @brief Official Randonautica Blindspot Algorithm
+ */
+static void _generate_blindspot(randonaut_state_t *state) {
+
+ _get_entropy(state);
+
+ double lat = (double)state->location.latitude / 100000;
+ double lon = (double)state->location.longitude / 100000;
+ uint16_t radius = state->radius;
+
+ const double random_distance = radius * sqrt( (double)state->entropy / INT32_MAX ) / 1000.0;
+ const double random_bearing = 2.0 * PI * (double)state->entropy / INT32_MAX;
+
+ const double phi = lat * PI / 180;
+ const double lambda = lon * PI / 180;
+ const double alpha = random_distance / R;
+
+ lat = asin( sin(phi) * cos(alpha) + cos(phi) * sin(alpha) * cos(random_bearing) );
+ lon = lambda + atan2( sin(random_bearing) * sin(alpha) * cos(phi), cos(alpha) - sin(phi) * sin( lat ));
+
+ state->point.latitude = (int)round(lat * (180 / PI) * 100000);
+ state->point.longitude = (int)round(lon * (180 / PI) * 100000);
+ state->point.distance = random_distance * 1000;
+ state->point.bearing = (uint16_t)round(random_bearing * (180 / PI) < 0 ? random_bearing * (180 / PI) + 360 : random_bearing * (180 / PI));
+}
+
+
+/** @brief pseudo random number generator
+ */
+static uint32_t _get_pseudo_entropy(uint32_t max) {
+ #if __EMSCRIPTEN__
+ return rand() % max;
+ #else
+ return arc4random_uniform(max);
+ #endif
+}
+
+/** @brief true random number generator
+ */
+static uint32_t _get_true_entropy(void) {
+ #if __EMSCRIPTEN__
+ return rand() % INT32_MAX;
+ #else
+ hri_mclk_set_APBCMASK_TRNG_bit(MCLK);
+ hri_trng_set_CTRLA_ENABLE_bit(TRNG);
+
+ while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready
+
+ hri_trng_clear_CTRLA_ENABLE_bit(TRNG);
+ hri_mclk_clear_APBCMASK_TRNG_bit(MCLK);
+ return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it
+ #endif
+}
+
+/** @brief get location from place.loc
+ */
+static void _get_location_from_file(randonaut_state_t *state) {
+ movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+ coordinate_t place;
+ if (filesystem_file_exists("place.loc")) {
+ if (filesystem_read_file("place.loc", (char*)&place, sizeof(place)))
+ state->location = place;
+ } else {
+ watch_set_indicator(WATCH_INDICATOR_BELL);
+ state->location.latitude = movement_location.bit.latitude * 1000;
+ state->location.longitude = movement_location.bit.longitude * 1000;
+ }
+}
+
+/** @brief save generated point to place.loc
+ */
+static void _save_point_to_file(randonaut_state_t *state) {
+ watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+ coordinate_t place;
+ place.latitude = state->point.latitude;
+ place.longitude = state->point.longitude;
+ if (filesystem_write_file("place.loc", (char*)&place, sizeof(place))) {
+ delay_ms(100);
+ watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+ } else {
+ watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
+ watch_set_indicator(WATCH_INDICATOR_BELL);
+ delay_ms(500);
+ watch_clear_indicator(WATCH_INDICATOR_BELL);
+
+ }
+}
+
+/** @brief get pseudo/quantum entropy and filter modulo bias
+ */
+static void _get_entropy(randonaut_state_t *state) {
+ if ( state->chance ) {
+ state->quantum = (bool)(state->entropy % 2);
+ }
+ do {
+ if ( !state->quantum ) {
+ state->entropy = _get_pseudo_entropy(INT32_MAX);
+ } else {
+ state->entropy = _get_true_entropy();
+ }
+ } while (state->entropy >= INT32_MAX || state->entropy <= 0);
+ state->entropy %= INT32_MAX;
+}
\ No newline at end of file
diff --git a/movement/watch_faces/complication/randonaut_face.h b/movement/watch_faces/complication/randonaut_face.h
new file mode 100644
index 00000000..fabde798
--- /dev/null
+++ b/movement/watch_faces/complication/randonaut_face.h
@@ -0,0 +1,113 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef RANDONAUT_FACE_H_
+#define RANDONAUT_FACE_H_
+
+#include "movement.h"
+#include "place_face.h"
+
+/*
+ * RANDONAUT FACE
+ * ==============
+ *
+ * Randonauting is a way to turn the world around you into an adventure and get the user outside
+ * of their day-to-day routine by using a random number generator to derive a coordinate to journey
+ * to. In Randonauts lore so-called "Blind Spots" are places you cannot reach methodologically. They
+ * may exist in your own backyard for your whole life and you will never even notice them, because
+ * you simply have no reason to go to that exact place or look in its direction. Since the very
+ * limitations of our behavioral algorithms are the reason for the existence of blindspots, they
+ * can only be found using a randomizer.
+ *
+ * This watch face generates a random location based on the watch's location and a set radius using
+ * the official Randonautica Blind Spot algorithm.
+ *
+ * The ALARM button starts the random location generation and then automatically displays the found
+ * Blind Spot.
+ *
+ * By pressing ALARM again the user can flip through different pieces of information about the Blind
+ * Spot: Distance (DI), Bearing Degree (BE), Latitude degrees and decimal digits (LA), Longitude
+ * degrees and decimal digits (LO).
+ *
+ * Pressing LIGHT switches between generating a new blind spot ("Rando") and displaying the info of
+ * the last generated one ("Point").
+ *
+ * LONG PRESSING LIGHT toggles setup mode. Here pressing LIGHT switches between setting the desired
+ * radius (RA) and setting the random number generator (RNG) for generating the blind spot.
+ *
+ * ALARM changes the values respectively:
+ *
+ * - The radius can be set in 500 meter steps between 1000 and 10,000 meters
+ *
+ * - The RNG can be set to "true" which utilizes the SAML22J's internal True Random Number Generator
+ * - Setting it to "psudo" will use the pseudorandom number generation algorithm arc4random
+ * - Setting it to "chance" will randomly chose either of the RNGs for each generation (default)
+ *
+ * LONG PRESSING ALARM toggles DATA mode in which the currently generated Blind Spot coordinate can
+ * be written to the file on the watch (press ALARM) and set as active high precision
+ * location used by other watch faces. It does not overwrite the low precision location information
+ * in the watch register commonly used for astronomical watch faces.
+ *
+ */
+
+typedef struct {
+ uint8_t mode :3;
+ uint8_t location_format :3;
+ uint8_t rng: 2;
+} randonaut_face_mode_t;
+
+typedef struct {
+ int32_t latitude : 26;
+ int32_t longitude : 26;
+ uint16_t distance : 14;
+ uint16_t bearing : 9;
+} randonaut_coordinate_t;
+
+typedef struct {
+ // Anything you need to keep track of, put it here!
+ coordinate_t location;
+ randonaut_coordinate_t point;
+ uint16_t radius : 14;
+ uint32_t entropy;
+ bool quantum;
+ bool chance;
+ randonaut_face_mode_t face;
+ char scratchpad[10];
+} randonaut_state_t;
+
+void randonaut_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void randonaut_face_activate(movement_settings_t *settings, void *context);
+bool randonaut_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void randonaut_face_resign(movement_settings_t *settings, void *context);
+
+#define randonaut_face ((const watch_face_t){ \
+ randonaut_face_setup, \
+ randonaut_face_activate, \
+ randonaut_face_loop, \
+ randonaut_face_resign, \
+ NULL, \
+})
+
+#endif // RANDONAUT_FACE_H_
+
diff --git a/movement/watch_faces/complication/time_left_face.c b/movement/watch_faces/complication/time_left_face.c
new file mode 100644
index 00000000..cc1077aa
--- /dev/null
+++ b/movement/watch_faces/complication/time_left_face.c
@@ -0,0 +1,357 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andreas Nebinger, based on the work of Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include "time_left_face.h"
+#include "watch.h"
+#include "watch_private_display.h"
+
+const char _state_titles[][3] = {{'D', 'L', ' '}, {'D', 'L', ' '}, {'D', 'A', ' '}, {'D', 'A', ' '}, {'Y', 'R', 'b'}, {'M', 'O', 'b'}, {'D', 'A', 'b'},
+ {'Y', 'R', 'd'}, {'M', 'O', 'd'}, {'D', 'A', 'd'}};
+const uint8_t TIME_LEFT_FACE_STATES = sizeof(_state_titles) / 3; // total number of state pages
+#define TIME_LEFT_FACE_SETTINGS_STATE 4 // number of first settings state
+const uint8_t _percentage_segdata[][2] = {{1, 2}, {2, 2}, {2, 3}, {1, 3}}; // segment data for drawing the percentage sign
+const uint8_t _animation_segdata[][2] = {{2, 8}, {1, 8}, {2, 7}, {2, 6}}; // segment data for the ticking animation
+
+static bool _quick_ticks_running;
+
+static uint32_t _juliandaynum(uint16_t year, uint16_t month, uint16_t day) {
+ // from here: https://en.wikipedia.org/wiki/Julian_day#Julian_day_number_calculation
+ return (1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100))/4 + day - 32075;
+}
+
+/// @brief displays an integer value with or without using positions 8 and 9
+static void _display_integer(char *buf) {
+ if (buf[1] == ' ') {
+ // display at position 4 if the value is short enough, don't use positions 8 and 9
+ watch_display_character(' ', 8);
+ watch_display_character(' ', 9);
+ watch_display_string((char *)buf + 2, 4);
+ } else {
+ // otherwise just display using position 8 and 9
+ watch_display_string(buf, 4);
+ }
+ watch_clear_colon();
+}
+
+///@brief display a percentage value
+static void _display_percentage(float percentage, char *buf) {
+ // always stay positive
+ if (percentage < 0) {
+ percentage *= -1;
+ // Switch display to days 'O'ver
+ watch_display_character('O', 1);
+ }
+ int32_t integral = percentage;
+ if (integral >= 100) {
+ // percentage equals 100 or more: don't do fractional part
+ sprintf(buf, " %3li o", integral);
+ watch_clear_colon();
+ } else {
+ // display percentage with two decimal places
+ uint8_t fraction = (int)(percentage * (float)100) % 100;
+ sprintf(buf, "%2li%02u o", integral, fraction);
+ watch_set_colon();
+ }
+ watch_display_string(buf, 4);
+ // draw (parts of) percentage symbol
+ for (uint8_t i = 0; i < sizeof(_percentage_segdata) / 2; i++)
+ watch_set_pixel(_percentage_segdata[i][0], _percentage_segdata[i][1]);
+}
+
+/// @brief draw the current state to the display
+static void _draw(time_left_state_t *state, uint8_t subsecond) {
+ char buf[17];
+ watch_display_character(_state_titles[state->current_page][0], 0);
+ watch_display_character(_state_titles[state->current_page][1], 1);
+ watch_display_character(' ', 2);
+ watch_display_character(_state_titles[state->current_page][2], 3);
+ if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) {
+ // we are displaying days left or days from birth
+ watch_date_time date_time = watch_rtc_get_date_time();
+ uint32_t julian_current_day = _juliandaynum(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day);
+ uint32_t julian_target_day = _juliandaynum(state->target_date.bit.year, state->target_date.bit.month, state->target_date.bit.day);
+ int32_t days_left = julian_target_day - julian_current_day;
+ if (state->current_page == 0) {
+ // display number of days left
+ sprintf(buf, "%6li", days_left);
+ _display_integer(buf);
+ } else {
+ // calculate starting date
+ uint32_t julian_start_day = _juliandaynum(state->birth_date.bit.year, state->birth_date.bit.month, state->birth_date.bit.day);
+ if ((state->current_page & 1) == 1) {
+ float percentage_left;
+ if (julian_start_day == julian_target_day) {
+ // failsafe
+ percentage_left = 0;
+ } else {
+ // display correct percentages
+ percentage_left = (float)days_left * (float)100 / (float)(julian_target_day - julian_start_day);
+ }
+ _display_percentage(state->current_page == 1 ? percentage_left : 100 - percentage_left, buf);
+ } else {
+ // display days from birth
+ sprintf(buf, "%6li", (int32_t)julian_current_day - julian_start_day);
+ _display_integer(buf);
+ }
+ }
+ } else {
+ // we are in settings mode
+ switch (state->current_page) {
+ case TIME_LEFT_FACE_SETTINGS_STATE:
+ // birth year
+ sprintf(buf, "%04u ", state->birth_date.bit.year);
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 1:
+ // birth month
+ sprintf(buf, " %02u", state->birth_date.bit.month);
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 2:
+ // birth day of month
+ sprintf(buf, " %02u", state->birth_date.bit.day);
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 3:
+ // target year
+ sprintf(buf, "%04u", state->target_date.bit.year);
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 4:
+ // target month
+ sprintf(buf, " %02u", state->target_date.bit.month);
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 5:
+ // target day of month
+ sprintf(buf, " %02u", state->target_date.bit.day);
+ break;
+ default:
+ break;
+ }
+ // blink current settings position or display value
+ if ((subsecond & 1) == 1)
+ watch_display_string(" ", 4);
+ else
+ watch_display_string(buf, 4);
+ }
+}
+
+/// @brief handle short or long pressing the alarm button
+static void _handle_alarm_button(time_left_state_t *state) {
+
+ const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31};
+ uint32_t tmp_day;
+ switch (state->current_page) {
+ case TIME_LEFT_FACE_SETTINGS_STATE: // birth year
+ state->birth_date.bit.year++;
+ if (state->birth_date.bit.year > state->current_year + 10) state->birth_date.bit.year = 1959;
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 1: // birth month
+ state->birth_date.bit.month = (state->birth_date.bit.month % 12) + 1;
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 2: // birth day
+ tmp_day = state->birth_date.bit.day; // use a temporary variable to avoid messing up the months
+ tmp_day++;
+ // handle February 29th on a leap year
+ if (((tmp_day > days_in_month[state->birth_date.bit.month - 1]) && (state->birth_date.bit.month != 2 || (state->birth_date.bit.year % 4) != 0))
+ || (state->birth_date.bit.month == 2 && (state->birth_date.bit.year % 4) == 0 && tmp_day > 29)) {
+ tmp_day = 1;
+ }
+ state->birth_date.bit.day = tmp_day;
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 3: // target year
+ state->target_date.bit.year++;
+ if (state->target_date.bit.year >2083) state->target_date.bit.year = state->current_year - 10;
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 4: // target month
+ state->target_date.bit.month = (state->target_date.bit.month % 12) + 1;
+ break;
+ case TIME_LEFT_FACE_SETTINGS_STATE + 5: // target day
+ tmp_day = state->target_date.bit.day;
+ tmp_day++;
+ // handle February 29th on a leap year
+ if (((tmp_day > days_in_month[state->target_date.bit.month - 1]) && (state->target_date.bit.month != 2 || (state->target_date.bit.year % 4) != 0))
+ || (state->target_date.bit.month == 2 && (state->target_date.bit.year % 4) == 0 && tmp_day > 29)) {
+ tmp_day = 1;
+ }
+ state->target_date.bit.day = tmp_day;
+ break;
+ }
+}
+
+static void _initiate_setting(time_left_state_t *state) {
+ state->current_page = TIME_LEFT_FACE_SETTINGS_STATE;
+ watch_clear_colon();
+ movement_request_tick_frequency(4);
+}
+
+static void _resume_setting(time_left_state_t *state) {
+ state->current_page = 0;
+ movement_request_tick_frequency(1);
+}
+
+static void _abort_quick_ticks() {
+ if (_quick_ticks_running) {
+ _quick_ticks_running = false;
+ movement_request_tick_frequency(4);
+ }
+}
+
+void time_left_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) settings;
+ (void) watch_face_index;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(time_left_state_t));
+ memset(*context_ptr, 0, sizeof(time_left_state_t));
+ time_left_state_t *state = (time_left_state_t *)*context_ptr;
+ state->birth_date.reg = watch_get_backup_data(2);
+ if (state->birth_date.reg == 0) {
+ // if birth date is totally blank, set a reasonable starting date. this works well for anyone under 63, but
+ // you can keep pressing to go back to 1900; just pass the current year. also picked this date because if you
+ // set it to 1959-01-02, it counts up from the launch of Luna-1, the first spacecraft to leave the well.
+ state->birth_date.bit.year = 1959;
+ state->birth_date.bit.month = 1;
+ state->birth_date.bit.day = 1;
+ watch_store_backup_data(state->birth_date.reg, 2);
+ // set target date to today + 10 years (just to have any value)
+ watch_date_time date_time = watch_rtc_get_date_time();
+ state->target_date.bit.year = date_time.unit.year + WATCH_RTC_REFERENCE_YEAR + 10;
+ state->target_date.bit.month = date_time.unit.month;
+ state->target_date.bit.day = date_time.unit.day;
+ }
+ }
+}
+
+void time_left_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ time_left_state_t *state = (time_left_state_t *)context;
+
+ // stash the current year, useful in birthday setting mode
+ watch_date_time date_time = watch_rtc_get_date_time();
+ state->current_year = date_time.unit.year + WATCH_RTC_REFERENCE_YEAR;
+ _quick_ticks_running = false;
+ // fetch the user's birth date from the birthday register
+ state->birth_date.reg = watch_get_backup_data(2);
+ // store original value of the birthdate to be able to detect changes on resigning
+ state->birth_date_when_activated.reg = state->birth_date.reg;
+}
+
+bool time_left_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ (void) settings;
+ time_left_state_t *state = (time_left_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ _draw(state, event.subsecond);
+ break;
+ case EVENT_LOW_ENERGY_UPDATE:
+ case EVENT_TICK: {
+ uint8_t subsecond = event.subsecond;
+ if (_quick_ticks_running) {
+ if (watch_get_pin_level(BTN_ALARM)) {
+ _handle_alarm_button(state);
+ subsecond = 0;
+ } else _abort_quick_ticks();
+ }
+ if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
+ // we are in settings mode. Draw to blink
+ _draw(state, subsecond);
+ } else {
+ // otherwise, check if we have to update. the display only needs to change at midnight
+ watch_date_time date_time = watch_rtc_get_date_time();
+ if (date_time.unit.hour == 0 && date_time.unit.minute == 0 && date_time.unit.second == 0) {
+ _draw(state, subsecond);
+ }
+ // handle the ticking animation
+ uint8_t animation_step = date_time.unit.second % (sizeof(_animation_segdata) / 2);
+ watch_set_pixel(_animation_segdata[animation_step][0], _animation_segdata[animation_step][1]);
+ if (animation_step == 0) animation_step = (sizeof(_animation_segdata) / 2) - 1;
+ else animation_step--;
+ watch_clear_pixel(_animation_segdata[animation_step][0], _animation_segdata[animation_step][1]);
+ }
+ break;
+ }
+ case EVENT_LIGHT_BUTTON_DOWN:
+ // do not illuminate here (this is done when releasing the button)
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) movement_illuminate_led();
+ else {
+ // cycle through the settings pages
+ state->current_page++;
+ if (state->current_page >= TIME_LEFT_FACE_STATES) {
+ // we have done a full settings cycle, so resume to normal
+ _resume_setting(state);
+ _draw(state, event.subsecond);
+ }
+ }
+ break;
+ case EVENT_LIGHT_LONG_PRESS:
+ if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
+ _resume_setting(state);
+ } else {
+ _initiate_setting(state);
+ }
+ _draw(state, event.subsecond);
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ _abort_quick_ticks();
+ if (state->current_page < TIME_LEFT_FACE_SETTINGS_STATE) {
+ // alternate between days left and percentage left
+ state->current_page = (state->current_page + 1) % TIME_LEFT_FACE_SETTINGS_STATE;
+ } else {
+ // we are in settings mode, so increment the current settings value
+ _handle_alarm_button(state);
+ }
+ _draw(state, 0);
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) {
+ // initiate fast cycling for settings values
+ movement_request_tick_frequency(8);
+ _quick_ticks_running = true;
+ _handle_alarm_button(state);
+ _draw(state, 0);
+ }
+ break;
+ case EVENT_TIMEOUT:
+ movement_move_to_face(0);
+ break;
+ default:
+ movement_default_loop_handler(event, settings);
+ break;
+ }
+
+ return true;
+}
+
+void time_left_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ time_left_state_t *state = (time_left_state_t *)context;
+
+ if (state->current_page >= TIME_LEFT_FACE_SETTINGS_STATE) _resume_setting(state);
+
+ // if the user changed their birth date, store it to the birth date register
+ if (state->birth_date_when_activated.reg != state->birth_date.reg) {
+ watch_store_backup_data(state->birth_date.reg, 2);
+ }
+}
diff --git a/movement/watch_faces/complication/time_left_face.h b/movement/watch_faces/complication/time_left_face.h
new file mode 100644
index 00000000..0ed7fd28
--- /dev/null
+++ b/movement/watch_faces/complication/time_left_face.h
@@ -0,0 +1,89 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Andreas Nebinger, based on the work of Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef TIME_LEFT_FACE_H_
+#define TIME_LEFT_FACE_H_
+
+#include "movement.h"
+
+/*
+ * The Time Left Face helps you to visualize how far you have proceeded in a certain
+ * time span. Much like the Day One Face, you can set your beginning date. In addition
+ * to that, you also set your target or destination date. You can then use the face
+ * to display your progress in different ways.
+ *
+ * Usage:
+ *
+ * - Long pressing of the light button starts the settings mode:
+ * - First, you set the beginning date (indicated by a 'b' in the upper right corner).
+ * - Start by setting the year (indicated by the letter 'YR'). Use the alarm button
+ * to cycle the value. Short pressing the light button brings you to the next
+ * settings page.
+ * - Set the values in this order:
+ * a. beginning date (indicated by a 'b'): year - month - day
+ * b. destination date (indicated by a 'd'): year - month - day
+ * - After cycling through all settings pages, the face resumes to display mode.
+ *
+ * - In display mode, use the alarm button (short press) to cycle through these four
+ * types of display:
+ * a. number of days left ('DL') until the destination date is reached.
+ * b. remaining days expressed as percentage of total time span. The value is shown
+ * with two decimals, using the colon as decimal point.
+ * c. number of days passed ('DA') since the beginning date.
+ * d. number of days passed expressed as percentage of total time span. (Two decimal
+ * points.)
+ *
+ * What is this for?
+ *
+ * You can use this watch face to be reminded of any kind of progess between a set
+ * start and end date. The brave among us can use it as a kind of memento mori
+ * visualization. Set your date of birth and look up the average life expectancy of
+ * your age cohort based on publicly available mortality tables. Then, set the
+ * statistically expected day of death as the target date and you will be able to
+ * see how much of your time has passed and how much is still to come.
+ *
+ */
+
+typedef struct {
+ uint8_t current_page;
+ uint16_t current_year;
+ movement_birthdate_t birth_date;
+ movement_birthdate_t birth_date_when_activated;
+ movement_birthdate_t target_date;
+} time_left_state_t;
+
+void time_left_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void time_left_face_activate(movement_settings_t *settings, void *context);
+bool time_left_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void time_left_face_resign(movement_settings_t *settings, void *context);
+
+#define time_left_face ((const watch_face_t){ \
+ time_left_face_setup, \
+ time_left_face_activate, \
+ time_left_face_loop, \
+ time_left_face_resign, \
+ NULL, \
+})
+
+#endif // TIME_LEFT_FACE_H_
diff --git a/movement/watch_faces/complication/toss_up_face.c b/movement/watch_faces/complication/toss_up_face.c
new file mode 100644
index 00000000..08dd0052
--- /dev/null
+++ b/movement/watch_faces/complication/toss_up_face.c
@@ -0,0 +1,793 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include "toss_up_face.h"
+#if __EMSCRIPTEN__
+#include
+#else
+#include "saml22j18a.h"
+#endif
+
+static const char heads[] = { '8', 'h', '4', 'E', '(' };
+static const char tails[] = { '0', '+', 'N', '3', ')' };
+static const uint8_t dd[] = {2, 4, 6, 8, 10,12,20,24,30,32,36,48,99};
+
+static void _roll_dice_multiple(char* result, uint8_t* dice, uint8_t num_dice);
+static void _sort_coins(char* token, uint8_t num_bits, uint8_t bits, char* heads, char* tails);
+void _display_coins(char* token, bool* bit_array, uint8_t length, toss_up_state_t *state);
+static void _toss_up_face_display(toss_up_state_t *state);
+static void _dice_animation(toss_up_state_t *state);
+static void _coin_animation(toss_up_state_t *state);
+
+// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
+
+void toss_up_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) watch_face_index;
+ (void) settings;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(toss_up_state_t));
+ memset(*context_ptr, 0, sizeof(toss_up_state_t));
+ toss_up_state_t *state = (toss_up_state_t *)*context_ptr;
+
+ // defaults
+ state->coin_num = 1;
+ state->dice_num = 1;
+ state->dice_sides[0] = 6;
+ state->dice_sides[1] = 6;
+ state->dice_sides[2] = 6;
+ state->coin_style[0] = '8';
+ state->coin_style[1] = '0';
+ }
+}
+
+void toss_up_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+}
+
+bool toss_up_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ toss_up_state_t *state = (toss_up_state_t *)context;
+ uint8_t i = 0;
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ watch_display_string(" Coins ", 0);
+ break;
+ case EVENT_TICK:
+ if ( state->animate ) {
+ state->animation = (state->animation + 1);
+ _toss_up_face_display(state);
+ }
+ break;
+ case EVENT_LIGHT_BUTTON_DOWN:
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ if ( state->animate ) break;
+ // change between coins and dice
+ if ( state->mode <= 1 ) state->mode = 2;
+ else if ( state->mode >= 2 ) state->mode = 0;
+ _toss_up_face_display(state);
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ // toss
+ if ( state->animate ) break;
+ switch (state->mode) {
+ case 0:
+ state->mode++;
+ // fall through
+ case 1:
+ state->animate = true;
+ for (i = 0; i < state->coin_num; i++) {
+ state->coins[i] = divine_bit();
+ }
+ break;
+ case 2:
+ state->mode++;
+ // fall through
+ case 3:
+ state->animate = true;
+ for (i = 0; i < state->dice_num; i++) {
+ state->dice[i] = roll_dice(state->dice_sides[i]);
+ }
+ break;
+ default:
+ break;
+ }
+ _toss_up_face_display(state);
+ break;
+ case EVENT_LIGHT_LONG_PRESS:
+ if ( state->animate ) break;
+ state->animate = false;
+ switch (state->mode) {
+ case 0: // change to default coin style
+ state->coin_style[0] = heads[0];
+ state->coin_style[1] = tails[0];
+ state->coinface = 0;
+ break;
+ case 1: // change the coin style
+ state->coinface = (state->coinface + 1) % 5;
+ state->coin_style[0] = heads[state->coinface];
+ state->coin_style[1] = tails[state->coinface];
+ break;
+ case 2: // change to default dice sides
+ state->dice_sides[0] = 6;
+ state->dice_sides[1] = 6;
+ state->dice_sides[2] = 6;
+ state->dd = 0;
+ break;
+ case 3: // change the sides of the dice
+ state->dd = (state->dd + 1) % 13;
+ state->dice_sides[state->dice_num-1] = dd[state->dd];
+ state->dice[state->dice_num-1] = dd[state->dd];
+ break;
+ default:
+ break;
+ }
+ _toss_up_face_display(state);
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ if ( state->animate ) break;
+ state->animate = false;
+ switch (state->mode) {
+ case 0: // back to one coin
+ state->coin_num = 1;
+ break;
+ case 1: // up to 6 coins total
+ state->coin_num = (state->coin_num % 6) + 1;
+ break;
+ case 2: // back to one dice
+ state->dice_num = 1;
+ break;
+ case 3: // add up to 3 dice total
+ state->dice_num = (state->dice_num % 3) + 1;
+ state->dd = 0;
+ break;
+ default:
+ break;
+ }
+ _toss_up_face_display(state);
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+
+ return true;
+}
+
+void toss_up_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+}
+
+// STATIC FUNCTIONS ///////////////////////////////////////////////////////////
+
+/** @brief handles the display
+ */
+static void _toss_up_face_display(toss_up_state_t *state) {
+ char buf[11] = {0};
+ char token[7] = {0};
+ switch ( state->mode ) {
+ case 0: // coins title
+ sprintf(buf, " Coins ");
+ break;
+ case 1: // coins divination
+ _coin_animation(state);
+ if ( !state->animate ) {
+ watch_clear_display();
+ _display_coins(token, state->coins, state->coin_num, state);
+ sprintf(buf, " %s", token);
+ }
+ break;
+ case 2: // dice title
+ sprintf(buf, " Dice ");
+ break;
+ case 3: // dice divination
+ _dice_animation(state);
+ if ( !state->animate ) {
+ _roll_dice_multiple(token, state->dice, state->dice_num + 1);
+ sprintf(buf, " %s", token);
+ }
+ break;
+ default:
+ break;
+ }
+ watch_display_string(buf, 0);
+}
+
+/** @brief divination method to derive a bit from 32 TRNG bits
+ */
+uint8_t divine_bit(void) {
+ uint32_t stalks;
+ do { // modulo bias filter
+ stalks = get_true_entropy(); // get 32 TRNG bits as stalks
+ } while (stalks >= INT32_MAX || stalks <= 0);
+
+ uint8_t pile1_xor = 0;
+ uint8_t pile2_xor = 0;
+ // Divide the stalks into two piles, alternating ends
+ for (uint8_t i = 0; i < 16; i++) {
+ uint8_t left_bit = (stalks >> (31 - 2*i)) & 1;
+ uint8_t right_bit = (stalks >> (30 - 2*i)) & 1;
+ if (i % 2 == 0) {
+ pile1_xor ^= left_bit;
+ pile2_xor ^= right_bit;
+ } else {
+ pile1_xor ^= right_bit;
+ pile2_xor ^= left_bit;
+ }
+ }
+ // Take the XOR of the pile results
+ uint8_t result_xor = pile1_xor ^ pile2_xor;
+ // Output 1 if result_xor is 1, 0 otherwise
+ return result_xor;
+}
+
+/** @brief get 32 True Random Number bits
+ */
+uint32_t get_true_entropy(void) {
+ #if __EMSCRIPTEN__
+ return rand() % INT32_MAX;
+ #else
+ hri_mclk_set_APBCMASK_TRNG_bit(MCLK);
+ hri_trng_set_CTRLA_ENABLE_bit(TRNG);
+
+ while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready
+
+ hri_trng_clear_CTRLA_ENABLE_bit(TRNG);
+ hri_mclk_clear_APBCMASK_TRNG_bit(MCLK);
+ return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it
+ #endif
+}
+
+// COIN FUNCTIONS /////////////////////////////////////////////////////////////
+
+/** @brief sort tossed coins into a pile of heads and a pile of tails
+ */
+static void _sort_coins(char* token, uint8_t num_bits, uint8_t bits, char* heads, char* tails) {
+ uint8_t num_ones = 0;
+ for (uint8_t i = 0; i < num_bits; i++) {
+ if ((bits >> i) & 1) {
+ *token++ = *heads;
+ num_ones++;
+ }
+ }
+ if ( num_bits < 6 ) {
+ for (uint8_t i = 0; i < (6 - num_bits); i++) {
+ *token++ = ' ';
+ }
+ }
+ for (uint8_t i = 0; i < (num_bits - num_ones); i++) {
+
+ *token++ = *tails;
+ }
+}
+
+/** @brief convert bool array of coinflips to integer for sorting
+ */
+void _display_coins(char* token, bool* bit_array, uint8_t length, toss_up_state_t *state) {
+ uint8_t bits = 0;
+ for (uint8_t i = 0; i < length; i++) {
+ if (bit_array[i]) {
+ bits |= (1 << (length - 1 - i));
+ }
+ }
+ _sort_coins(token, length, bits, &state->coin_style[0], &state->coin_style[1]);
+}
+
+/** @brief coin animation
+ */
+static void _coin_animation(toss_up_state_t *state) {
+ bool heads = false;
+ bool tails = false;
+ for (uint8_t i = 0; i < state->coin_num; i++) {
+ if (state->coins[i] == true) {
+ heads = true;
+ } else {
+ tails = true;
+ }
+ }
+ movement_request_tick_frequency(32);
+ switch ( state->animation ) {
+ case 0:
+ watch_display_string(" ", 4);
+ if ( heads ) {
+ watch_set_pixel(0, 18);
+ watch_set_pixel(2, 18);
+ } else {
+ state->animation = 12;
+ }
+ break;
+ case 1:
+ if ( heads ) {
+ watch_set_pixel(1, 18);
+ }
+ break;
+ case 2:
+ if ( heads ) {
+ watch_set_pixel(0, 19);
+ watch_set_pixel(2, 19);
+ }
+ break;
+ case 3:
+ if ( heads ) {
+ watch_clear_pixel(0, 18);
+ watch_clear_pixel(2, 18);
+ }
+ break;
+ case 4:
+ if ( heads ) {
+ watch_clear_pixel(1, 18);
+ }
+ break;
+ case 5:
+ if ( heads ) {
+ watch_clear_pixel(0, 19);
+ watch_clear_pixel(2, 19);
+ watch_set_pixel(1, 17);
+ watch_set_pixel(0, 20);
+ }
+ break;
+ case 6:
+ if ( heads ) {
+ watch_set_pixel(2, 20);
+ watch_set_pixel(0, 21);
+ }
+ break;
+ case 7:
+ if ( heads ) {
+ watch_set_pixel(1, 21);
+ watch_set_pixel(2, 21);
+ }
+ break;
+ case 8:
+ if ( heads ) {
+ watch_clear_pixel(1, 17);
+ watch_clear_pixel(0, 20);
+ }
+ break;
+ case 9:
+ if ( heads ) {
+ watch_clear_pixel(2, 20);
+ watch_clear_pixel(0, 21);
+ }
+ break;
+ case 10:
+ if ( heads ) {
+ watch_clear_pixel(1, 21);
+ watch_clear_pixel(2, 21);
+ watch_set_pixel(1, 22);
+ watch_set_pixel(2, 22);
+ }
+ break;
+ case 11:
+ if ( heads ) {
+ watch_set_pixel(0, 22);
+ }
+ break;
+ case 12:
+ if ( heads ) {
+ watch_set_pixel(2, 23);
+ watch_set_pixel(0, 23);
+ }
+ if ( tails ) {
+ watch_set_pixel(0, 18);
+ watch_set_pixel(2, 18);
+ }
+ break;
+ case 13:
+ if ( heads ) {
+ watch_clear_pixel(1, 22);
+ watch_clear_pixel(2, 22);
+ }
+ if ( tails ) {
+ watch_set_pixel(1, 18);
+ }
+ break;
+ case 14:
+ if ( heads ) {
+ watch_clear_pixel(0, 22);
+ }
+ if ( tails ) {
+ watch_set_pixel(0, 19);
+ watch_set_pixel(2, 19);
+ }
+ break;
+ case 15:
+ if ( heads ) {
+ watch_clear_pixel(2, 23);
+ watch_clear_pixel(0, 23);
+ watch_set_pixel(2, 0);
+ watch_set_pixel(1, 0);
+ }
+ if ( tails ) {
+ watch_clear_pixel(0, 18);
+ watch_clear_pixel(2, 18);
+ }
+ break;
+ case 16:
+ if ( heads ) {
+ watch_set_pixel(2, 1);
+ watch_set_pixel(0, 0);
+ }
+ if ( tails ) {
+ watch_clear_pixel(1, 18);
+ }
+ break;
+ case 17:
+ if ( heads ) {
+ watch_set_pixel(2, 10);
+ watch_set_pixel(0, 1);
+ }
+ if ( tails ) {
+ watch_clear_pixel(0, 19);
+ watch_clear_pixel(2, 19);
+ watch_set_pixel(1, 17);
+ watch_set_pixel(0, 20);
+ }
+ break;
+ case 18:
+ if ( heads ) {
+ watch_clear_pixel(2, 0);
+ watch_clear_pixel(1, 0);
+ }
+ if ( tails ) {
+ watch_set_pixel(2, 20);
+ watch_set_pixel(0, 21);
+ }
+ break;
+ case 19:
+ if ( heads ) {
+ watch_clear_pixel(2, 1);
+ watch_clear_pixel(0, 0);
+ }
+ if ( tails ) {
+ watch_set_pixel(1, 21);
+ watch_set_pixel(2, 21);
+ }
+ break;
+ case 20:
+ if ( heads ) {
+ watch_set_pixel(2, 1);
+ watch_set_pixel(0, 0);
+ }
+ if ( tails ) {
+ watch_clear_pixel(1, 17);
+ watch_clear_pixel(0, 20);
+ }
+ break;
+ case 21:
+ if ( heads ) {
+ watch_set_pixel(2, 0);
+ watch_set_pixel(1, 0);
+ }
+ if ( tails ) {
+ watch_clear_pixel(2, 20);
+ watch_clear_pixel(0, 21);
+ }
+ break;
+ case 22:
+ if ( heads ) {
+ watch_clear_pixel(2, 10);
+ watch_clear_pixel(0, 1);
+ }
+ if ( tails ) {
+ watch_clear_pixel(1, 21);
+ watch_clear_pixel(2, 21);
+ watch_set_pixel(1, 22);
+ watch_set_pixel(2, 22);
+ }
+ break;
+ case 23:
+ if ( heads ) {
+ watch_clear_pixel(2, 1);
+ watch_clear_pixel(0, 0);
+ }
+ if ( tails ) {
+ watch_set_pixel(0, 22);
+ }
+ break;
+ case 24:
+ if ( heads ) {
+ watch_set_pixel(2, 23);
+ watch_set_pixel(0, 23);
+ watch_clear_pixel(2, 0);
+ watch_clear_pixel(1, 0);
+ }
+ if ( tails ) {
+ watch_set_pixel(2, 23);
+ watch_set_pixel(0, 23);
+ }
+ break;
+ case 25:
+ if ( heads ) {
+ watch_set_pixel(0, 22);
+ }
+ if ( tails ) {
+ watch_clear_pixel(1, 22);
+ watch_clear_pixel(2, 22);
+ }
+ break;
+ case 26:
+ if ( heads ) {
+ watch_set_pixel(1, 22);
+ watch_set_pixel(2, 22);
+ }
+ if ( tails ) {
+ watch_clear_pixel(0, 22);
+ }
+ break;
+ case 27:
+ if ( heads ) {
+ watch_clear_pixel(2, 23);
+ watch_clear_pixel(0, 23);
+ }
+ if ( tails ) {
+ watch_clear_pixel(2, 23);
+ watch_clear_pixel(0, 23);
+ watch_set_pixel(2, 0);
+ watch_set_pixel(1, 0);
+ }
+ break;
+ case 28:
+ if ( heads ) {
+ watch_clear_pixel(0, 22);
+ }
+ if ( tails ) {
+ watch_set_pixel(2, 1);
+ watch_set_pixel(0, 0);
+ }
+ break;
+ case 29:
+ if ( heads ) {
+ watch_set_pixel(1, 21);
+ watch_set_pixel(2, 21);
+ watch_clear_pixel(1, 22);
+ watch_clear_pixel(2, 22);
+ }
+ if ( tails ) {
+ watch_set_pixel(2, 10);
+ watch_set_pixel(0, 1);
+ }
+ break;
+ case 30:
+ if ( heads ) {
+ watch_set_pixel(2, 20);
+ watch_set_pixel(0, 21);
+ }
+ if ( tails ) {
+ watch_clear_pixel(1, 0);
+ watch_clear_pixel(2, 0);
+ }
+ break;
+ case 31:
+ if ( heads ) {
+ watch_set_pixel(1, 17);
+ watch_set_pixel(0, 20);
+ }
+ if ( tails ) {
+ watch_clear_pixel(2, 1);
+ watch_clear_pixel(0, 0);
+ }
+ break;
+ case 32:
+ if ( heads ) {
+ watch_clear_pixel(1, 21);
+ watch_clear_pixel(2, 21);
+ }
+ if ( tails ) {
+ watch_clear_pixel(2, 10);
+ watch_clear_pixel(0, 1);
+ watch_set_pixel(0, 2);
+ watch_set_pixel(1, 2);
+ }
+ break;
+ case 33:
+ if ( heads ) {
+ watch_clear_pixel(2, 20);
+ watch_clear_pixel(0, 21);
+ }
+ if ( tails ) {
+ watch_set_pixel(2, 2);
+ watch_set_pixel(0, 3);
+ }
+ break;
+ case 34:
+ if ( heads ) {
+ watch_set_pixel(0, 19);
+ watch_set_pixel(2, 19);
+ watch_clear_pixel(1, 17);
+ watch_clear_pixel(0, 20);
+ }
+ if ( tails ) {
+ watch_set_pixel(2, 3);
+ watch_set_pixel(0, 4);
+ }
+ break;
+ case 35:
+ if ( heads ) {
+ watch_set_pixel(1, 18);
+ }
+ if ( tails ) {
+ watch_clear_pixel(1, 2);
+ watch_clear_pixel(0, 2);
+ }
+ break;
+ case 36:
+ if ( heads ) {
+ watch_set_pixel(0, 18);
+ watch_set_pixel(2, 18);
+ }
+ if ( tails ) {
+ watch_clear_pixel(2, 2);
+ watch_clear_pixel(0, 3);
+ }
+ break;
+ case 37:
+ if ( heads ) {
+ watch_clear_pixel(0, 19);
+ watch_clear_pixel(2, 19);
+ }
+ if ( tails ) {
+ watch_clear_pixel(2, 3);
+ watch_clear_pixel(0, 4);
+ watch_set_pixel(1, 4);
+ watch_set_pixel(0, 5);
+ }
+ break;
+ case 38:
+ if ( heads ) {
+ watch_clear_pixel(1, 18);
+ }
+ if ( tails ) {
+ watch_set_pixel(2, 4);
+ watch_set_pixel(0, 6);
+ }
+ break;
+ case 39:
+ if ( heads ) {
+ watch_clear_pixel(0, 18);
+ watch_clear_pixel(2, 18);
+ }
+ if ( tails ) {
+ watch_set_pixel(1, 6);
+ watch_set_pixel(2, 5);
+ }
+ state->animate = false;
+ state->animation = 0;
+ movement_request_tick_frequency(1);
+ }
+}
+
+// DICE FUNCTIONS /////////////////////////////////////////////////////////////
+
+/** @brief rolls a dice
+ */
+uint8_t roll_dice(uint8_t sides) {
+ uint8_t bits_needed = 0;
+ uint8_t temp_sides = sides - 1;
+ uint8_t result = 0;
+ while (temp_sides > 0) {
+ bits_needed++; // how many bits do we need to represent this number?
+ temp_sides >>= 1; // Shift right to check the next bit
+ }
+ do {
+ result = 0;
+ for (int i = 0; i < bits_needed; i++) {
+ result <<= 1; // Shift left to make room for the next bit
+ result |= divine_bit(); // Add the next bit to the result
+ }
+ } while ( result > sides -1 );
+ return result + 1; // Add 1 to convert the range from 0 to sides-1 to 1 to sides
+}
+
+/** @brief roll multiple dice and print a char array for displaying them
+ */
+static void _roll_dice_multiple(char* result, uint8_t* dice, uint8_t num_dice) {
+ // initialize the result array to all spaces
+ memset(result, ' ', 6);
+
+ // roll the dice and write the result to the result array
+ for (uint8_t i = 0; i < num_dice-1; i++) {
+ uint8_t dice_result = dice[i];
+ uint8_t tens_digit = dice_result / 10;
+ uint8_t ones_digit = dice_result % 10;
+ result[(i * 2)] = tens_digit == 0 ? ' ' : (char)('0' + tens_digit);
+ result[(i * 2) + 1] = (char)('0' + ones_digit);
+ }
+}
+
+/** @brief dice animation
+ */
+static void _dice_animation(toss_up_state_t *state) {
+ watch_display_string(" ", 4);
+ for (uint8_t i = 0; i < state->dice_num; i++) {
+ watch_display_string("0",i*2 + 5);
+ }
+ movement_request_tick_frequency(16);
+ switch ( state->animation ) {
+ case 0:
+ watch_clear_pixel(1, 17);
+ watch_clear_pixel(0, 0);
+ watch_clear_pixel(1, 6);
+ break;
+ case 1:
+ watch_clear_pixel(2, 20);
+ watch_clear_pixel(1, 0);
+ watch_clear_pixel(0, 6);
+ break;
+ case 2:
+ watch_clear_pixel(2, 21);
+ watch_clear_pixel(2, 0);
+ watch_clear_pixel(0, 5);
+ break;
+ case 3:
+ watch_clear_pixel(1, 21);
+ watch_clear_pixel(2, 1);
+ watch_clear_pixel(1, 4);
+ break;
+ case 4:
+ watch_clear_pixel(0, 21);
+ watch_clear_pixel(2, 10);
+ watch_clear_pixel(2, 4);
+ break;
+ case 5:
+ watch_clear_pixel(0, 20);
+ watch_clear_pixel(0, 1);
+ watch_clear_pixel(2, 5);
+ break;
+ case 6:
+ watch_clear_pixel(1, 17);
+ watch_clear_pixel(0, 0);
+ watch_clear_pixel(1, 6);
+ break;
+ case 7:
+ watch_clear_pixel(2, 20);
+ watch_clear_pixel(1, 0);
+ watch_clear_pixel(0, 6);
+ break;
+ case 8:
+ watch_clear_pixel(2, 21);
+ watch_clear_pixel(2, 0);
+ watch_clear_pixel(0, 5);
+ break;
+ case 9:
+ watch_clear_pixel(1, 21);
+ watch_clear_pixel(2, 1);
+ watch_clear_pixel(1, 4);
+ break;
+ case 10:
+ watch_clear_pixel(0, 21);
+ watch_clear_pixel(2, 10);
+ watch_clear_pixel(2, 4);
+ break;
+ case 11:
+ watch_clear_pixel(0, 20);
+ watch_clear_pixel(0, 1);
+ watch_clear_pixel(2, 5);
+ state->animate = false;
+ state->animation = 0;
+ movement_request_tick_frequency(1);
+ }
+}
\ No newline at end of file
diff --git a/movement/watch_faces/complication/toss_up_face.h b/movement/watch_faces/complication/toss_up_face.h
new file mode 100644
index 00000000..ca6136a7
--- /dev/null
+++ b/movement/watch_faces/complication/toss_up_face.h
@@ -0,0 +1,112 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef TOSS_UP_FACE_H_
+#define TOSS_UP_FACE_H_
+
+#include "movement.h"
+
+/*
+ * TOSS UP FACE
+ * ============
+ *
+ * Playful watch face for games of chance or divination using coins or dice.
+ *
+ * LIGHT switches between Coins and Dice mode
+ *
+ * COINS
+ * =====
+ *
+ * ALARM tosses a coin. If it lands on heads it gets sorted to the left side of the
+ * display, if it lands on tails then sorted to the right side.
+ *
+ * LONG PRESSING ALARM adds up to 5 more coins to the toss for more nuance in the decision
+ * making (e.g. three heads vs two tails could be read as "yes, but with serious doubts").
+ *
+ * LONG PRESSING LIGHT flips through additional style for the coins from the default Ө/O
+ * to H/T (heads/tails), Y/N (yes/no), E/Ǝ, C/Ↄ
+ *
+ * LONG PRESSING ALARM on the "Coins" title page resets to one coin.
+ * LONG PRESSING LIGHT on the "Coins" title page resets the style to Ө/O
+ *
+ * DICE
+ * ====
+ *
+ * ALARM rolls a six sided dice.
+ *
+ * LONG PRESSING ALARM adds up to 2 more dice to the roll.
+ *
+ * LONG PRESSING LIGHT flips through other available polyhedral dice types with less or more
+ * than the default 6 sides. The options are D2, D4, D6, D8, D10, D12, D20, D24, D30, D32, D36,
+ * D48, and a hypothetical D99.
+ *
+ * When more than one dice is used for a roll this changes only the last added dice. (see Note
+ * below)
+ *
+ * LONG PRESSING ALARM on the "Dice" title page resets to one dice.
+ * LONG PRESSING LIGHT on the "Dice" title page resets the dice to D6.
+ *
+ * Please Note: If you need let's say a D8, D12, and D20 for your rolls then the procedure to
+ * set this up would be as follows: from the default screen where you can roll the one D6 dice
+ * you would LONG PRESS LIGHT a few times to change the D6 to a D8, then LONG PRESS ALARM to add
+ * a second dice, LONG PRESS LIGHT again until the second dice changes to D12, then LONG PRESS
+ * ALARM to add the third dice and LONG PRESS LIGHT again a few times until it becomes a D20.
+ *
+ */
+
+typedef struct {
+ // Anything you need to keep track of, put it here!
+ uint32_t entropy;
+ uint8_t mode : 4; // 1 coin, 2 coins, 3 coins, 4 coins, dice, iching, geomnc
+ bool setup;
+ bool coins[6];
+ uint8_t coin_num : 3;
+ char coin_style[2];
+ uint8_t coinface : 3;
+ uint8_t dice[3];
+ uint8_t dice_num : 2;
+ uint8_t dd : 6;
+ uint8_t dice_sides[3];
+ uint8_t animation;
+ bool animate;
+} toss_up_state_t;
+
+uint32_t get_true_entropy(void);
+uint8_t divine_bit(void);
+uint8_t roll_dice(uint8_t sides);
+void toss_up_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void toss_up_face_activate(movement_settings_t *settings, void *context);
+bool toss_up_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void toss_up_face_resign(movement_settings_t *settings, void *context);
+
+#define toss_up_face ((const watch_face_t){ \
+ toss_up_face_setup, \
+ toss_up_face_activate, \
+ toss_up_face_loop, \
+ toss_up_face_resign, \
+ NULL, \
+})
+
+#endif // TOSS_UP_FACE_H_
+
diff --git a/movement/watch_faces/demo/chirpy_demo_face.c b/movement/watch_faces/demo/chirpy_demo_face.c
new file mode 100644
index 00000000..ee3e5b41
--- /dev/null
+++ b/movement/watch_faces/demo/chirpy_demo_face.c
@@ -0,0 +1,335 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Gabor L Ugray
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include "chirpy_demo_face.h"
+#include "chirpy_tx.h"
+#include "filesystem.h"
+
+typedef enum {
+ CDM_CHOOSE = 0,
+ CDM_CHIRPING,
+} chirpy_demo_mode_t;
+
+typedef enum {
+ CDP_SCALE = 0,
+ CDP_INFO_SHORT,
+ CDP_INFO_LONG,
+ CDP_INFO_NANOSEC,
+} chirpy_demo_program_t;
+
+typedef struct {
+ // Current mode
+ chirpy_demo_mode_t mode;
+
+ // Selected program
+ chirpy_demo_program_t program;
+
+ // Helps us handle 1/64 ticks during transmission; including countdown timer
+ chirpy_tick_state_t tick_state;
+
+ // Used by chirpy encoder during transmission
+ chirpy_encoder_state_t encoder_state;
+
+} chirpy_demo_state_t;
+
+static uint8_t long_data_str[] =
+ "There once was a ship that put to sea\n"
+ "The name of the ship was the Billy of Tea\n"
+ "The winds blew up, her bow dipped down\n"
+ "O blow, my bully boys, blow (huh)\n"
+ "\n"
+ "Soon may the Wellerman come\n"
+ "To bring us sugar and tea and rum\n"
+ "One day, when the tonguin' is done\n"
+ "We'll take our leave and go\n";
+
+static uint16_t short_data_len = 20;
+
+static uint8_t short_data[] = {
+ 0x27,
+ 0x00,
+ 0x0c,
+ 0x42,
+ 0xa3,
+ 0xd4,
+ 0x06,
+ 0x54,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0x0c,
+ 0x6b,
+ 0x05,
+ 0x5a,
+ 0x09,
+ 0xd8,
+ 0x00,
+ 0xf5,
+ 0x00,
+};
+
+#define NANOSEC_INI_FILE_NAME "nanosec.ini"
+
+static uint8_t *nanosec_buffer = 0;
+static uint16_t nanosec_buffer_size = 0;
+
+void chirpy_demo_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) {
+ (void)settings;
+ (void)watch_face_index;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(chirpy_demo_state_t));
+ memset(*context_ptr, 0, sizeof(chirpy_demo_state_t));
+ // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+ }
+ // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void chirpy_demo_face_activate(movement_settings_t *settings, void *context) {
+ (void)settings;
+ chirpy_demo_state_t *state = (chirpy_demo_state_t *)context;
+
+ memset(context, 0, sizeof(chirpy_demo_state_t));
+ state->mode = CDM_CHOOSE;
+ state->program = CDP_SCALE;
+
+ // Do we have nanosec data? Load it.
+ int32_t sz = filesystem_get_file_size(NANOSEC_INI_FILE_NAME);
+ if (sz > 0) {
+ // We will free this in resign.
+ // I don't like any kind of dynamic allocation in long-running embedded software...
+ // But there's no way around it here; I don't want to hard-wire (and squat) any fixed size structure
+ // Nanosec data may change in the future too
+ nanosec_buffer_size = sz + 2;
+ nanosec_buffer = malloc(nanosec_buffer_size);
+ // First two bytes of prefix, so Chirpy RX can recognize this data type
+ nanosec_buffer[0] = 0xc0;
+ nanosec_buffer[1] = 0x00;
+ // Read file
+ filesystem_read_file(NANOSEC_INI_FILE_NAME, (char*)&nanosec_buffer[2], sz);
+ }
+}
+
+// To create / check test file in emulator:
+// echo TestData > nanosec.ini
+// cat nanosec.ini
+
+static void _cdf_update_lcd(chirpy_demo_state_t *state) {
+ watch_display_string("CH", 0);
+ if (state->program == CDP_SCALE)
+ watch_display_string(" SCALE", 4);
+ else if (state->program == CDP_INFO_SHORT)
+ watch_display_string("SHORT ", 4);
+ else if (state->program == CDP_INFO_LONG)
+ watch_display_string(" LOng ", 4);
+ else if (state->program == CDP_INFO_NANOSEC)
+ watch_display_string("nAnO ", 4);
+ else
+ watch_display_string("---- ", 4);
+}
+
+static void _cdf_quit_chirping(chirpy_demo_state_t *state) {
+ state->mode = CDM_CHOOSE;
+ watch_set_buzzer_off();
+ watch_clear_indicator(WATCH_INDICATOR_BELL);
+ movement_request_tick_frequency(1);
+}
+
+static void _cdf_scale_tick(void *context) {
+ chirpy_demo_state_t *state = (chirpy_demo_state_t *)context;
+ chirpy_tick_state_t *tick_state = &state->tick_state;
+
+ // Scale goes in 200Hz increments from 700 Hz to 12.3 kHz -> 58 steps
+ if (tick_state->seq_pos == 58) {
+ _cdf_quit_chirping(state);
+ return;
+ }
+ uint32_t freq = 700 + tick_state->seq_pos * 200;
+ uint32_t period = 1000000 / freq;
+ watch_set_buzzer_period(period);
+ watch_set_buzzer_on();
+ ++tick_state->seq_pos;
+}
+
+static void _cdf_data_tick(void *context) {
+ chirpy_demo_state_t *state = (chirpy_demo_state_t *)context;
+
+ uint8_t tone = chirpy_get_next_tone(&state->encoder_state);
+ // Transmission over?
+ if (tone == 255) {
+ _cdf_quit_chirping(state);
+ return;
+ }
+ uint16_t period = chirpy_get_tone_period(tone);
+ watch_set_buzzer_period(period);
+ watch_set_buzzer_on();
+}
+
+static uint8_t *curr_data_ptr;
+static uint16_t curr_data_ix;
+static uint16_t curr_data_len;
+
+static uint8_t _cdf_get_next_byte(uint8_t *next_byte) {
+ if (curr_data_ix == curr_data_len)
+ return 0;
+ *next_byte = curr_data_ptr[curr_data_ix];
+ ++curr_data_ix;
+ return 1;
+}
+
+static void _cdf_countdown_tick(void *context) {
+ chirpy_demo_state_t *state = (chirpy_demo_state_t *)context;
+ chirpy_tick_state_t *tick_state = &state->tick_state;
+
+ // Countdown over: start actual broadcast
+ if (tick_state->seq_pos == 8 * 3) {
+ tick_state->tick_compare = 3;
+ tick_state->tick_count = -1;
+ tick_state->seq_pos = 0;
+ // We'll be chirping out a scale
+ if (state->program == CDP_SCALE) {
+ tick_state->tick_fun = _cdf_scale_tick;
+ }
+ // We'll be chirping out data
+ else {
+ // Set up the encoder
+ chirpy_init_encoder(&state->encoder_state, _cdf_get_next_byte);
+ tick_state->tick_fun = _cdf_data_tick;
+ // Set up the data
+ curr_data_ix = 0;
+ if (state->program == CDP_INFO_SHORT) {
+ curr_data_ptr = short_data;
+ curr_data_len = short_data_len;
+ } else if (state->program == CDP_INFO_LONG) {
+ curr_data_ptr = long_data_str;
+ curr_data_len = strlen((const char *)long_data_str);
+ } else if (state->program == CDP_INFO_NANOSEC) {
+ curr_data_ptr = nanosec_buffer;
+ curr_data_len = nanosec_buffer_size;
+ }
+ }
+ return;
+ }
+ // Sound or turn off buzzer
+ if ((tick_state->seq_pos % 8) == 0) {
+ watch_set_buzzer_period(NotePeriods[BUZZER_NOTE_A5]);
+ watch_set_buzzer_on();
+ } else if ((tick_state->seq_pos % 8) == 1) {
+ watch_set_buzzer_off();
+ }
+ ++tick_state->seq_pos;
+}
+
+static void _cdm_setup_chirp(chirpy_demo_state_t *state) {
+ // We want frequent callbacks from now on
+ movement_request_tick_frequency(64);
+ watch_set_indicator(WATCH_INDICATOR_BELL);
+ state->mode = CDM_CHIRPING;
+ // Set up tick state; start with countdown
+ state->tick_state.tick_count = -1;
+ state->tick_state.tick_compare = 8;
+ state->tick_state.seq_pos = 0;
+ state->tick_state.tick_fun = _cdf_countdown_tick;
+}
+
+bool chirpy_demo_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ (void)settings;
+ chirpy_demo_state_t *state = (chirpy_demo_state_t *)context;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ _cdf_update_lcd(state);
+ break;
+ case EVENT_MODE_BUTTON_UP:
+ // Do not exit face while we're chirping
+ if (state->mode != CDM_CHIRPING) {
+ movement_move_to_next_face();
+ }
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ // We don't do light.
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ // If in choose mode: select next program
+ if (state->mode == CDM_CHOOSE) {
+ if (state->program == CDP_SCALE)
+ state->program = CDP_INFO_SHORT;
+ else if (state->program == CDP_INFO_SHORT)
+ state->program = CDP_INFO_LONG;
+ else if (state->program == CDP_INFO_LONG) {
+ if (nanosec_buffer_size > 0)
+ state->program = CDP_INFO_NANOSEC;
+ else
+ state->program = CDP_SCALE;
+ } else if (state->program == CDP_INFO_NANOSEC)
+ state->program = CDP_SCALE;
+ _cdf_update_lcd(state);
+ }
+ // If chirping: stoppit
+ else if (state->mode == CDM_CHIRPING) {
+ _cdf_quit_chirping(state);
+ }
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ // If in choose mode: start chirping
+ if (state->mode == CDM_CHOOSE) {
+ _cdm_setup_chirp(state);
+ }
+ break;
+ case EVENT_TICK:
+ if (state->mode == CDM_CHIRPING) {
+ ++state->tick_state.tick_count;
+ if (state->tick_state.tick_count == state->tick_state.tick_compare) {
+ state->tick_state.tick_count = 0;
+ state->tick_state.tick_fun(context);
+ }
+ }
+ break;
+ case EVENT_TIMEOUT:
+ // Do not time out while we're chirping
+ if (state->mode != CDM_CHIRPING) {
+ movement_move_to_face(0);
+ }
+ default:
+ break;
+ }
+
+ // Return true if the watch can enter standby mode. False needed when chirping.
+ if (state->mode == CDM_CHIRPING)
+ return false;
+ else
+ return true;
+}
+
+void chirpy_demo_face_resign(movement_settings_t *settings, void *context) {
+ (void)settings;
+ (void)context;
+
+ if (nanosec_buffer != 0) {
+ free(nanosec_buffer);
+ nanosec_buffer = 0;
+ nanosec_buffer_size = 0;
+ }
+}
diff --git a/movement/watch_faces/demo/chirpy_demo_face.h b/movement/watch_faces/demo/chirpy_demo_face.h
new file mode 100644
index 00000000..2d34107a
--- /dev/null
+++ b/movement/watch_faces/demo/chirpy_demo_face.h
@@ -0,0 +1,70 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 <#author_name#>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef CHIRPY_DEMO_FACE_H_
+#define CHIRPY_DEMO_FACE_H_
+
+#include "movement.h"
+
+/*
+ * CHIRPY DEMO FACE
+ *
+ * This face demonstrates the chirpy-tx library. It is intended to help you
+ * include chirpy-tx in your own watch faces that need to transmit data out
+ * of the watch.
+ *
+ * The face's first screen lets you select from a few built-in transmissions:
+ *
+ * SCALE cycles through frequencies in fixed increments. This is intended to
+ * collect and analyze audio samples from different watches. With this info
+ * it may be possible to improve chirpy-tx's parameters like the frequencies
+ * it uses to make the method more robust.
+ *
+ * SHORT is a small transmission that contains data taked from the activity_face.
+ *
+ * LONG is a longer transmission that contains the first two strophes of a
+ * famous sea shanty.
+ *
+ * Select the transmission you want with ALARM, the press LONG ALARM to chirp.
+ *
+ * To record and decode a chirpy transmission on your computer, you can use the web app here:
+ * https://jealousmarkup.xyz/off/chirpy/rx/
+ *
+ */
+
+void chirpy_demo_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void chirpy_demo_face_activate(movement_settings_t *settings, void *context);
+bool chirpy_demo_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void chirpy_demo_face_resign(movement_settings_t *settings, void *context);
+
+#define chirpy_demo_face ((const watch_face_t){ \
+ chirpy_demo_face_setup, \
+ chirpy_demo_face_activate, \
+ chirpy_demo_face_loop, \
+ chirpy_demo_face_resign, \
+ NULL, \
+})
+
+#endif // CHIRPY_DEMO_FACE_H_
+
diff --git a/movement/watch_faces/sensor/lightmeter_face.c b/movement/watch_faces/sensor/lightmeter_face.c
new file mode 100644
index 00000000..861e28d8
--- /dev/null
+++ b/movement/watch_faces/sensor/lightmeter_face.c
@@ -0,0 +1,206 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 CC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* Aperture-priority Light Meter Face
+ *
+ * Tested with the "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001" flexboard.
+ * This flexboard could use a revision:
+ *
+ * - The thermistor components should be moved west a mm or flipped to the backside
+ * to avoid stressing the flexboard against the processor so much.
+ * - The 'no connect' pad falls off easily.
+ *
+ * Controls:
+ *
+ * - Trigger a measurement by long-pressing Alarm.
+ * Sensor integration is happening when the Signal indicator is on.
+ *
+ * - ISO setting can be cycled by long-pressing Light.
+ * During integration the current ISO setting will be displayed.
+ *
+ * - EV measurement in the top right: "LAP" indicates "half stop".
+ * So "LAP -1" means EV = -1.5. Likewise "LAP 13" means EV = +13.5
+ *
+ * - Aperture in the bottom right: the last 3 main digits are the f-stop.
+ * Adjust this number in half-stop increments using Alarm = +1/2 and Light = -1/2.
+ *
+ * - Best shutter speed in the bottom left: the first 3 digits are the shutter speed.
+ * Some special chars are needed here: "-" = seconds, "h" = extra half second, "K" = thousands.
+ * "HI" or "LO" if there's no shutter in the dictionary within 0.5 stops of correct exposure.
+ *
+ * - Mode long-press changes the main digits to show raw sensor lux measurements.
+ *
+ */
+
+#include
+#include
+#include
+#include "lightmeter_face.h"
+#include "watch_utility.h"
+#include "watch_slcd.h"
+
+uint16_t lightmeter_mod(uint16_t m, uint16_t n) { return (m%n + n)%n; }
+
+void lightmeter_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
+ (void) settings;
+ (void) watch_face_index;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(lightmeter_state_t));
+ lightmeter_state_t *state = (lightmeter_state_t*) *context_ptr;
+ state->waiting_for_conversion = 0;
+ state->lux = 0.0;
+ state->mode = 0;
+ state->iso = LIGHTMETER_ISO_100;
+ state->ap = LIGHTMETER_AP_4P0;
+ }
+}
+
+void lightmeter_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ lightmeter_state_t *state = (lightmeter_state_t*) context;
+ state->waiting_for_conversion = 0;
+ lightmeter_show_ev(state); // Print most current reading
+ watch_enable_i2c();
+ return;
+}
+
+void lightmeter_show_ev(lightmeter_state_t *state) {
+
+ float ev = max(min(
+ log2(state->lux) +
+ lightmeter_isos[state->iso].ev +
+ LIGHTMETER_CALIBRATION,
+ 99), -9);
+ int evt = round(2*ev); // Truncated EV
+
+ // Print EV
+ char strbuff[7];
+ watch_clear_all_indicators();
+ watch_display_string("EV ", 0);
+
+ sprintf(strbuff, "%2i", (uint16_t) abs(evt/2)); // Print whole part of EV
+ watch_display_string(strbuff, 2);
+ if(evt%2) watch_set_indicator(WATCH_INDICATOR_LAP); // Indicate half stop
+ if(ev<0) watch_set_pixel(1,9); // Indicate negative EV
+
+ // Handle lux mode
+ if(state->mode == 1) {
+ sprintf(strbuff, "%6.0f", min(state->lux, 999999.0));
+ watch_display_string(strbuff, 4);
+ return;
+ }
+
+ // Find and print best shutter speed
+ uint16_t bestsh = 0;
+ float besterr = 1.0/0.0;
+ float errbuf = 1.0/0.0;
+ float comp_ev = ev + lightmeter_aps[state->ap].ev;
+ for(uint16_t ind = 2; ind < LIGHTMETER_N_SHS; ind++) {
+ errbuf = comp_ev + lightmeter_shs[ind].ev;
+ if( fabs(errbuf) < fabs(besterr)) {
+ besterr = errbuf;
+ bestsh = ind;
+ }
+ }
+ if(besterr >= 0.5) watch_display_string(lightmeter_shs[LIGHTMETER_SH_HIGH].str, 4);
+ else if(besterr <= -0.5) watch_display_string(lightmeter_shs[LIGHTMETER_SH_LOW].str, 4);
+ else watch_display_string(lightmeter_shs[bestsh].str, 4);
+
+ // Print aperture
+ watch_display_string(lightmeter_aps[state->ap].str, 7);
+ return;
+}
+
+bool lightmeter_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ (void) settings;
+ lightmeter_state_t *state = (lightmeter_state_t*) context;
+
+ opt3001_Config_t c;
+ switch (event.event_type) {
+ case EVENT_TICK:
+ if(state->waiting_for_conversion) { // Check if measurement is ready...
+ c = opt3001_readConfig(lightmeter_addr);
+ if(c.ConversionReady) {
+ state->waiting_for_conversion = 0;
+ opt3001_t result = opt3001_readResult(lightmeter_addr);
+ state->lux = result.lux;
+ lightmeter_show_ev(state);
+ }
+ }
+ break;
+
+ case EVENT_ALARM_BUTTON_UP: // Increment aperture
+ state->ap = lightmeter_mod(state->ap+1, LIGHTMETER_N_APS);
+
+ lightmeter_show_ev(state);
+ break;
+
+ case EVENT_LIGHT_BUTTON_UP: // Decrement aperture
+ if(state->ap == 0) state->ap = LIGHTMETER_N_APS-1;
+ else state->ap = lightmeter_mod(state->ap-1, LIGHTMETER_N_APS);
+
+ lightmeter_show_ev(state);
+ break;
+
+ case EVENT_LIGHT_LONG_PRESS: // Cycle ISO
+ state->iso = lightmeter_mod(state->iso+1, LIGHTMETER_N_ISOS);
+
+ watch_clear_all_indicators();
+ watch_display_string("EV ", 0);
+ watch_display_string(lightmeter_isos[state->iso].str, 4);
+ break;
+
+ case EVENT_ALARM_LONG_PRESS: // Take measurement
+ opt3001_writeConfig(lightmeter_addr, lightmeter_takeNewReading);
+ state->waiting_for_conversion = 1;
+
+ watch_clear_all_indicators();
+ watch_display_string("EV ", 0);
+ watch_display_string(lightmeter_isos[state->iso].str, 4);
+ watch_set_indicator(WATCH_INDICATOR_SIGNAL);
+ break;
+
+ case EVENT_MODE_LONG_PRESS: // Toggle mode
+ state->mode = !state->mode;
+ lightmeter_show_ev(state);
+ break;
+
+ case EVENT_TIMEOUT:
+ movement_move_to_face(0);
+ break;
+
+ default:
+ movement_default_loop_handler(event, settings);
+ break;
+ }
+ return true;
+}
+
+void lightmeter_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+ opt3001_writeConfig(lightmeter_addr, lightmeter_off);
+ watch_disable_i2c();
+ return;
+}
diff --git a/movement/watch_faces/sensor/lightmeter_face.h b/movement/watch_faces/sensor/lightmeter_face.h
new file mode 100644
index 00000000..2f8813f4
--- /dev/null
+++ b/movement/watch_faces/sensor/lightmeter_face.h
@@ -0,0 +1,160 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 CC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef LIGHTMETER_FACE_H_
+#define LIGHTMETER_FACE_H_
+
+#include "movement.h"
+#include "opt3001.h"
+
+#define LIGHTMETER_CALIBRATION 2.58
+typedef struct {
+ char * str;
+ float ev;
+} lightmeter_ev_t;
+
+static const lightmeter_ev_t lightmeter_isos[] = {
+ {" i 25", -2},
+ {" i 50", -1},
+ {" i 100", 0},
+ {" i 160", 0.68},
+ {" i 200", 1},
+ {" i 400", 2},
+ {" i 800", 3},
+ {" i1600", 4}};
+typedef enum {
+ LIGHTMETER_ISO_25, LIGHTMETER_ISO_50, LIGHTMETER_ISO_100, LIGHTMETER_ISO_160, LIGHTMETER_ISO_200, LIGHTMETER_ISO_400, LIGHTMETER_ISO_800, LIGHTMETER_ISO_1600,
+ LIGHTMETER_N_ISOS
+} lightmeter_iso_t;
+
+static const lightmeter_ev_t lightmeter_aps[] = {
+ {"1.4", 0},
+ {"1.8", -0.5},
+ {"2.0", -1},
+ {"2.4", -1.5},
+ {"2.8", -2},
+ {"3.3", -2.5},
+ {"4.0", -3},
+ {"4.8", -3.5},
+ {"5.6", -4},
+ {"6.7", -4.5},
+ {"8.0", -5},
+ {"9.5", -5.5},
+ {"11.", -6},
+ {"13.", -6.5},
+ {"16.", -7},
+ {"19.", -7.5},
+ {"22.", -8}};
+typedef enum {
+ LIGHTMETER_AP_1P4, LIGHTMETER_AP_1P8, LIGHTMETER_AP_2P0, LIGHTMETER_AP_2P4, LIGHTMETER_AP_2P8, LIGHTMETER_AP_3P3, LIGHTMETER_AP_4P0, LIGHTMETER_AP_4P8, LIGHTMETER_AP_5P6, LIGHTMETER_AP_6P7, LIGHTMETER_AP_8P0, LIGHTMETER_AP_9P5,
+ LIGHTMETER_AP_11, LIGHTMETER_AP_13, LIGHTMETER_AP_16, LIGHTMETER_AP_19, LIGHTMETER_AP_22,
+ LIGHTMETER_N_APS
+} lightmeter_ap_t;
+
+static const lightmeter_ev_t lightmeter_shs[] = {
+ {"LO-", 99},
+ {"HI ", -99},
+ {"30-", 5.0},
+ {"20-", 4.5},
+ {"15-", 4.0},
+ {"11-", 3.5},
+ {"8- ", 3.0},
+ {"6- ", 2.5},
+ {"4- ", 2.0},
+ {"3- ", 1.5},
+ {"2- ", 1.0},
+ {"1h-", 0.5},
+ {"1 ", 0.0},
+ {"1h ", -0.5},
+ {"2 ", -1.0},
+ {"3 ", -1.5},
+ {"4 ", -2.0},
+ {"6 ", -2.5},
+ {"8 ", -3.0},
+ {"12 ", -3.5},
+ {"15 ", -4.0},
+ {"20 ", -4.5},
+ {"30 ", -5.0},
+ {"45 ", -5.5},
+ {"60 ", -6.0},
+ {"90 ", -6.5},
+ {"125", -7.0},
+ {"180", -7.5},
+ {"250", -8.0},
+ {"350", -8.5},
+ {"500", -9.0},
+ {"750", -9.5},
+ {"1K ", -10.0},
+ {"1K5", -10.5},
+ {"2K ", -11.0},
+ {"3K ", -11.5},
+ {"4K ", -12.0},
+ {"6K ", -12.5},
+ {"8K ", -13.0}};
+typedef enum {
+ LIGHTMETER_SH_LOW, LIGHTMETER_SH_HIGH,
+ LIGHTMETER_SH_30S, LIGHTMETER_SH_20S, LIGHTMETER_SH_15S, LIGHTMETER_SH_11S, LIGHTMETER_SH_8S, LIGHTMETER_SH_6S, LIGHTMETER_SH_3S, LIGHTMETER_SH_4S, LIGHTMETER_SH_2S, LIGHTMETER_SH_1HS,
+ LIGHTMETER_SH_1, LIGHTMETER_SH_1H, LIGHTMETER_SH_2, LIGHTMETER_SH_3, LIGHTMETER_SH_4, LIGHTMETER_SH_6, LIGHTMETER_SH_8, LIGHTMETER_SH_12, LIGHTMETER_SH_15, LIGHTMETER_SH_20, LIGHTMETER_SH_30, LIGHTMETER_SH_45, LIGHTMETER_SH_60, LIGHTMETER_SH_90, LIGHTMETER_SH_125, LIGHTMETER_SH_180, LIGHTMETER_SH_250, LIGHTMETER_SH_350, LIGHTMETER_SH_500, LIGHTMETER_SH_750,
+ LIGHTMETER_SH_1K, LIGHTMETER_SH_1K5, LIGHTMETER_SH_2K, LIGHTMETER_SH_3K, LIGHTMETER_SH_4K, LIGHTMETER_SH_6K, LIGHTMETER_SH_8K,
+ LIGHTMETER_N_SHS
+} lightmeter_sh_t;
+
+typedef struct {
+ lightmeter_iso_t iso;
+ lightmeter_ap_t ap;
+ bool waiting_for_conversion;
+ float lux;
+ int mode;
+} lightmeter_state_t;
+
+static const opt3001_Config_t lightmeter_takeNewReading = {
+ .RangeNumber = 0B1100,
+ .ConversionTime = 0B1,
+ .Latch = 0B1,
+ .ModeOfConversionOperation = 0B01
+};
+
+static const opt3001_Config_t lightmeter_off = {
+ .ModeOfConversionOperation = 0B00
+};
+
+uint16_t lightmeter_mod(uint16_t m, uint16_t n);
+
+void lightmeter_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void lightmeter_face_activate(movement_settings_t *settings, void *context);
+void lightmeter_show_ev(lightmeter_state_t *state);
+bool lightmeter_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void lightmeter_face_resign(movement_settings_t *settings, void *context);
+
+static const uint8_t lightmeter_addr = 0x44;
+
+#define lightmeter_face ((const watch_face_t){ \
+ lightmeter_face_setup, \
+ lightmeter_face_activate, \
+ lightmeter_face_loop, \
+ lightmeter_face_resign, \
+ NULL, \
+})
+
+#endif // LIGHTMETER_FACE_H_
diff --git a/movement/watch_faces/settings/place_face.h b/movement/watch_faces/settings/place_face.h
new file mode 100644
index 00000000..a98c2642
--- /dev/null
+++ b/movement/watch_faces/settings/place_face.h
@@ -0,0 +1,231 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Tobias Raayoni Last / @randogoth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef place_FACE_H_
+#define place_FACE_H_
+
+#include "movement.h"
+
+/*
+ * PLACE FACE
+ * ==========
+ *
+ * Based on and expanded from the Sunrise/Sunset face. Outsourced the location setting functionality to
+ * its own face. Also serves as a converter between different coordinate notation formats.
+ *
+ * With the LIGHT button each place coordinate can be shown and edited in 4 different display modes:
+ *
+ * 1) Decimal Latitude and Longitude (WGS84) up to 5 decimal points
+ * 2) Latitude and Longitude (WGS84) in traditional DD°MM'SS" notation
+ * 3) Ten digit Open Location Code (aka. PlusCode) format
+ * 4) Ten digit Geohash format
+ *
+ * Using the ALARM button the user can flip through 2 pages of coordinate info to see the first and
+ * second sets of digits.
+ *
+ * (please also refer to the notes on precision below)
+ *
+ * Editing Mode
+ * ============
+ *
+ * A LONG PRESS of the LIGHT button toggles editing mode for each of the selected notations.
+ *
+ * In this mode LIGHT moves the cursor and ALARM changes the letter cycling through the available
+ * alphabet or numbers.
+ *
+ * When OLC or Geohash display are edited, Digit Info mode is activated. It serves as a workaround
+ * for the limitation of how ambiguously alphanumeric characters are displayed on the main seven segment
+ * digits of the watch face ( S or 5, I or 1, U or W?).
+ *
+ * The selected letter is also shown in the much easier to read alphanumeric 8 segment weekday digit above.
+ * In addition the '24H' indicator is active when the selected digit represents a number and the 'PM'
+ * indicator for a letter.
+ *
+ * A LONG PRESS of LIGHT saves the changes.
+ *
+ * Coordinates are read or stored to both the traditional internal location register and a file on
+ * the LFS file system ("place.loc"). By default the Watch Face loads the coordinates from file
+ * when activated. If no file is present, the coordinates are loaded from the register.
+ * (please also see the notes on precision below)
+ *
+ * Auxiliary Mode: Digit Info
+ * ==========================
+ *
+ * A LONG PRESS of the ALARM button toggles Digit Info mode when OLC or Geohash display is active.
+ * (LAP indicator is on) It is a means of being able to see the detailed Digit Info as described above
+ * but without the risk of accidentally editing any of digits.
+ *
+ * Both ALARM and LIGHT buttons can be used to flip through the letters.
+ *
+ * Notes on Coordinate Precision
+ * =============================
+ *
+ * The common WGS84 Latitude and Longitude degrees naturally do not represent meters in distance
+ * on the ground. 1° Longitude on the equatorial line equals a width of 111.32 kilometers, but
+ * at 40° latitude further North or South it is approximately 85 kilometers wide. The closer to
+ * the poles the narrower (and more precise) the latitude degrees get.
+ *
+ * The Sensor Watch's traditional 16bit location register only stores latitudes and longitudes
+ * with two decimal points. That equals a longitudal precision of 36 arc seconds, or ~1111 meters
+ * at the equator - precise enough for astronomical calculations, but not if you want to store the
+ * location of let's say a building.
+ *
+ * Hence we propose the file that serves the same purpose, but with a precision of
+ * five decimal digits. That equals 0.04 arc seconds or 1.11 meters at the equator.
+ *
+ * Please also note that the different notations of this watch face also have varying magnitudes
+ * of precision:
+ *
+ * | Format | Notation | Precision at Equator | Precision at 67° N/S |
+ * | ------------------ | ---------------------- | -------------------- | -------------------- |
+ * | 2d. Decimal LatLon | 29.98, 31.13 | 1111.320 m | 435.125 m |
+ * | 5d. Decimal LatLon | 29.97916, 31.13417 | 1.111 m | 0.435 m |
+ * | DMS LatLon | N 29°58′45″, E 31°8′3″ | 30.833 m | 12.083 m |
+ * | Open Location Code | 7GXHX4HM+MM | 13.875 m | 13.875 m |
+ * | Geohash | stq4s3x1qu | 1.189 m | 0.596 m |
+ *
+ * Since all notations are internally converted into degrees with 5 decimal points, expect some
+ * rounding errors when editing or loading the coordinates in other notation formats.
+ *
+ */
+
+static const char olc_alphabet[20] = "23456789CFGHJMPQRUWX";
+static const char geohash_alphabet[32] = "0123456789bCdEfGhjkmnpqrstuVwxyz";
+
+typedef struct {
+ uint8_t sign: 1; // 0-1
+ uint8_t hundreds: 1; // 0-1
+ uint8_t tens: 4; // 0-9
+ uint8_t ones: 4; // 0-9
+ uint8_t d01: 4; // 0-9
+ uint8_t d02: 4; // 0-9
+ uint8_t d03: 4; // 0-9
+ uint8_t d04: 4; // 0-9
+ uint8_t d05: 4; // 0-9
+} place_format_decimal_latlon_t;
+
+typedef struct {
+ uint8_t sign: 1; // 0-1
+ uint8_t hundreds: 1; // 0-1
+ uint8_t tens: 4; // 0-9
+ uint8_t ones: 4; // 0-9
+ uint8_t mins_tens: 3; // 0-5
+ uint8_t mins_ones: 4; // 0-9
+ uint8_t secs_tens: 3; // 0-5
+ uint8_t secs_ones: 4; // 0-9
+} place_format_dms_latlon_t;
+
+typedef struct {
+ uint8_t lat1: 5; // 2-X
+ uint8_t lon1: 5; // 2-X
+ uint8_t lat2: 5; // 2-X
+ uint8_t lon2: 5; // 2-X
+ uint8_t lat3: 5; // 2-X
+ uint8_t lon3: 5; // 2-X
+ uint8_t lat4: 5; // 2-X
+ uint8_t lon4: 5; // 2-X
+ uint8_t lat5: 5; // 2-X
+ uint8_t lon5: 5; // 2-X
+} place_format_olc_t;
+
+typedef struct {
+ int32_t latitude : 25;
+ int32_t longitude : 26;
+} coordinate_t;
+
+typedef struct {
+ place_format_decimal_latlon_t latitude;
+ place_format_decimal_latlon_t longitude;
+} place_coordinate_t;
+
+typedef struct {
+ uint8_t d01: 6; // 0-z
+ uint8_t d02: 6; // 0-z
+ uint8_t d03: 6; // 0-z
+ uint8_t d04: 6; // 0-z
+ uint8_t d05: 6; // 0-z
+ uint8_t d06: 6; // 0-z
+ uint8_t d07: 6; // 0-z
+ uint8_t d08: 6; // 0-z
+ uint8_t d09: 6; // 0-z
+ uint8_t d10: 6; // 0-z
+} place_format_geohash_t;
+
+typedef struct {
+ double max;
+ double min;
+} place_format_geohash_interval;
+
+typedef struct {
+ uint8_t min_digit : 1;
+ uint8_t max_digit : 3;
+} place_mode_schema_page_t;
+
+typedef struct {
+ uint8_t max_page : 3;
+ place_mode_schema_page_t page[4];
+} place_mode_schema_mode_t;
+
+enum place_modes_e {
+ MODE_DECIMAL = 0,
+ MODE_DMS,
+ MODE_OLC,
+ MODE_GEOHASH
+};
+
+typedef struct {
+ enum place_modes_e mode;
+ uint8_t page : 3;
+ int8_t active_digit: 4;
+ bool edit;
+ bool digit_info;
+ place_format_decimal_latlon_t working_latitude;
+ place_format_decimal_latlon_t working_longitude;
+ place_format_dms_latlon_t working_dms_latitude;
+ place_format_dms_latlon_t working_dms_longitude;
+ place_format_olc_t working_pluscode;
+ place_format_geohash_t working_geohash;
+ place_mode_schema_mode_t modes[4];
+} place_state_t;
+
+// PUBLIC WATCH FACE FUNCTIONS ////////////////////////////////////////////////
+
+void place_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void place_face_activate(movement_settings_t *settings, void *context);
+bool place_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void place_face_resign(movement_settings_t *settings, void *context);
+
+void place_latlon_to_olc(char *pluscode, double latitude, double longitude);
+void place_latlon_to_geohash(char *geohash, double latitude, double longitude);
+
+#define place_face ((const watch_face_t){ \
+ place_face_setup, \
+ place_face_activate, \
+ place_face_loop, \
+ place_face_resign, \
+ NULL, \
+})
+
+#endif // place_FACE_H_
+
diff --git a/watch-library/shared/driver/opt3001.c b/watch-library/shared/driver/opt3001.c
new file mode 100644
index 00000000..d2f4e8ce
--- /dev/null
+++ b/watch-library/shared/driver/opt3001.c
@@ -0,0 +1,91 @@
+/*
+
+Arduino library for Texas Instruments opt3001_t Digital Ambient Light Sensor
+Written by AA for ClosedCube
+Adapted to Sensor-Watch interface by CC
+---
+
+The MIT License (MIT)
+
+Copyright (c) 2015 ClosedCube Limited
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+#include
+
+#include "opt3001.h"
+#include "watch_utility.h"
+
+uint16_t opt3001_readManufacturerID(uint8_t devaddr) {
+ uint8_t buf[2];
+ buf[0] = (uint8_t) OPT3001_MANUFACTURER_ID;
+ watch_i2c_send(devaddr, buf, 1);
+ watch_i2c_receive(devaddr, buf, 2);
+ return ((uint16_t) buf[0] << 8) | ((uint16_t) buf[1]);
+}
+
+uint16_t opt3001_readDeviceID(uint8_t devaddr) {
+ uint8_t buf[2];
+ buf[0] = (uint8_t) OPT3001_DEVICE_ID;
+ watch_i2c_send(devaddr, buf, 1);
+ watch_i2c_receive(devaddr, buf, 2);
+ return ((uint16_t) buf[0] << 8) | ((uint16_t) buf[1]);
+}
+
+opt3001_Config_t opt3001_readConfig(uint8_t devaddr) {
+ opt3001_Config_t config;
+ uint8_t buf[2];
+ buf[0] = (uint8_t) OPT3001_CONFIG;
+ watch_i2c_send(devaddr, buf, 1);
+ watch_i2c_receive(devaddr, buf, 2);
+ config.rawData = ((uint16_t) buf[0] << 8) | ((uint16_t) buf[1]);
+ return config;
+}
+
+void opt3001_writeConfig(uint8_t devaddr, opt3001_Config_t config) {
+ uint8_t buf[3] = {OPT3001_CONFIG, (uint8_t)(config.rawData >> 8), (uint8_t)(config.rawData & 0x00FF)};
+ watch_i2c_send(devaddr, buf, 3);
+ return;
+}
+
+opt3001_t opt3001_readResult(uint8_t devaddr) {
+ return opt3001_readRegister(devaddr, OPT3001_RESULT);
+}
+
+opt3001_t opt3001_readHighLimit(uint8_t devaddr) {
+ return opt3001_readRegister(devaddr, OPT3001_HIGH_LIMIT);
+}
+
+opt3001_t opt3001_readLowLimit(uint8_t devaddr) {
+ return opt3001_readRegister(devaddr, OPT3001_LOW_LIMIT);
+}
+
+opt3001_t opt3001_readRegister(uint8_t devaddr, opt3001_Command_t command) {
+ opt3001_t result;
+ opt3001_ER_t er;
+ uint8_t buf[2];
+ buf[0] = (uint8_t) command;
+ watch_i2c_send(devaddr, buf, 1);
+ watch_i2c_receive(devaddr, buf, 2);
+ er.rawData = ((uint16_t) buf[0] << 8) | ((uint16_t) buf[1]);
+ result.raw = er;
+ result.lux = 0.01*pow(2, er.Exponent)*er.Result;
+ return result;
+}
diff --git a/watch-library/shared/driver/opt3001.h b/watch-library/shared/driver/opt3001.h
new file mode 100644
index 00000000..1ed8eea9
--- /dev/null
+++ b/watch-library/shared/driver/opt3001.h
@@ -0,0 +1,87 @@
+/*
+
+Arduino library for Texas Instruments OPT3001 Digital Ambient Light Sensor
+Adapted to Sensor-Watch interface by CC
+---
+
+The MIT License (MIT)
+
+Copyright (c) 2015 ClosedCube Limited
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+#ifndef OPT3001_
+#define OPT3001_
+#include
+
+typedef enum {
+ OPT3001_RESULT = 0x00,
+ OPT3001_CONFIG = 0x01,
+ OPT3001_LOW_LIMIT = 0x02,
+ OPT3001_HIGH_LIMIT = 0x03,
+
+ OPT3001_MANUFACTURER_ID = 0x7E,
+ OPT3001_DEVICE_ID = 0x7F,
+} opt3001_Command_t;
+
+typedef union {
+ uint16_t rawData;
+ struct {
+ uint16_t Result : 12;
+ uint8_t Exponent : 4;
+ };
+} opt3001_ER_t;
+
+
+typedef union {
+ struct {
+ uint8_t FaultCount : 2;
+ uint8_t MaskExponent : 1;
+ uint8_t Polarity : 1;
+ uint8_t Latch : 1;
+ uint8_t FlagLow : 1;
+ uint8_t FlagHigh : 1;
+ uint8_t ConversionReady : 1;
+ uint8_t OverflowFlag : 1;
+ uint8_t ModeOfConversionOperation : 2;
+ uint8_t ConversionTime : 1;
+ uint8_t RangeNumber : 4;
+ };
+ uint16_t rawData;
+} opt3001_Config_t;
+
+typedef struct {
+ float lux;
+ opt3001_ER_t raw;
+} opt3001_t;
+
+uint16_t opt3001_readManufacturerID(uint8_t devaddr);
+uint16_t opt3001_readDeviceID(uint8_t devaddr);
+
+opt3001_t opt3001_readResult(uint8_t devaddr);
+opt3001_t opt3001_readHighLimit(uint8_t devaddr);
+opt3001_t opt3001_readLowLimit(uint8_t devaddr);
+
+opt3001_Config_t opt3001_readConfig(uint8_t devaddr);
+void opt3001_writeConfig(uint8_t devaddr, opt3001_Config_t config);
+opt3001_t opt3001_readRegister(uint8_t devaddr, opt3001_Command_t command);
+
+#endif // OPT3001_
diff --git a/watch-library/simulator/shell.html b/watch-library/simulator/shell.html
index 8d371cd6..335b9534 100644
--- a/watch-library/simulator/shell.html
+++ b/watch-library/simulator/shell.html
@@ -842,6 +842,7 @@
+